1 // Copyright 2018 yuzu emulator team
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4 
5 #include <algorithm>
6 #include <array>
7 #include "common/common_paths.h"
8 #include "common/common_types.h"
9 #include "common/file_util.h"
10 #include "common/logging/log.h"
11 #include "common/string_util.h"
12 #include "common/swap.h"
13 #include "core/constants.h"
14 #include "core/core.h"
15 #include "core/core_timing.h"
16 #include "core/file_sys/control_metadata.h"
17 #include "core/file_sys/patch_manager.h"
18 #include "core/hle/ipc_helpers.h"
19 #include "core/hle/kernel/kernel.h"
20 #include "core/hle/kernel/process.h"
21 #include "core/hle/service/acc/acc.h"
22 #include "core/hle/service/acc/acc_aa.h"
23 #include "core/hle/service/acc/acc_su.h"
24 #include "core/hle/service/acc/acc_u0.h"
25 #include "core/hle/service/acc/acc_u1.h"
26 #include "core/hle/service/acc/errors.h"
27 #include "core/hle/service/acc/profile_manager.h"
28 #include "core/hle/service/glue/arp.h"
29 #include "core/hle/service/glue/manager.h"
30 #include "core/hle/service/sm/sm.h"
31 #include "core/loader/loader.h"
32 
33 namespace Service::Account {
34 
35 constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 30};
36 constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
37 
GetImagePath(Common::UUID uuid)38 static std::string GetImagePath(Common::UUID uuid) {
39     return Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
40            "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
41 }
42 
SanitizeJPEGSize(std::size_t size)43 static constexpr u32 SanitizeJPEGSize(std::size_t size) {
44     constexpr std::size_t max_jpeg_image_size = 0x20000;
45     return static_cast<u32>(std::min(size, max_jpeg_image_size));
46 }
47 
48 class IManagerForSystemService final : public ServiceFramework<IManagerForSystemService> {
49 public:
IManagerForSystemService(Core::System & system_,Common::UUID)50     explicit IManagerForSystemService(Core::System& system_, Common::UUID)
51         : ServiceFramework{system_, "IManagerForSystemService"} {
52         // clang-format off
53         static const FunctionInfo functions[] = {
54             {0, nullptr, "CheckAvailability"},
55             {1, nullptr, "GetAccountId"},
56             {2, nullptr, "EnsureIdTokenCacheAsync"},
57             {3, nullptr, "LoadIdTokenCache"},
58             {100, nullptr, "SetSystemProgramIdentification"},
59             {101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+
60             {110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+
61             {111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+
62             {112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0
63             {113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+
64             {120, nullptr, "GetNintendoAccountId"},
65             {121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+
66             {130, nullptr, "GetNintendoAccountUserResourceCache"},
67             {131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"},
68             {132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"},
69             {133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+
70             {134, nullptr, "RefreshNintendoAccountVerificationUrlCache"}, // 9.0.0+
71             {135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+
72             {140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+
73             {141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+
74             {142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+
75             {150, nullptr, "CreateAuthorizationRequest"},
76         };
77         // clang-format on
78 
79         RegisterHandlers(functions);
80     }
81 };
82 
83 // 3.0.0+
84 class IFloatingRegistrationRequest final : public ServiceFramework<IFloatingRegistrationRequest> {
85 public:
IFloatingRegistrationRequest(Core::System & system_,Common::UUID)86     explicit IFloatingRegistrationRequest(Core::System& system_, Common::UUID)
87         : ServiceFramework{system_, "IFloatingRegistrationRequest"} {
88         // clang-format off
89         static const FunctionInfo functions[] = {
90             {0, nullptr, "GetSessionId"},
91             {12, nullptr, "GetAccountId"},
92             {13, nullptr, "GetLinkedNintendoAccountId"},
93             {14, nullptr, "GetNickname"},
94             {15, nullptr, "GetProfileImage"},
95             {21, nullptr, "LoadIdTokenCache"},
96             {100, nullptr, "RegisterUser"}, // [1.0.0-3.0.2] RegisterAsync
97             {101, nullptr, "RegisterUserWithUid"}, // [1.0.0-3.0.2] RegisterWithUidAsync
98             {102, nullptr, "RegisterNetworkServiceAccountAsync"}, // 4.0.0+
99             {103, nullptr, "RegisterNetworkServiceAccountWithUidAsync"}, // 4.0.0+
100             {110, nullptr, "SetSystemProgramIdentification"},
101             {111, nullptr, "EnsureIdTokenCacheAsync"},
102         };
103         // clang-format on
104 
105         RegisterHandlers(functions);
106     }
107 };
108 
109 class IAdministrator final : public ServiceFramework<IAdministrator> {
110 public:
IAdministrator(Core::System & system_,Common::UUID)111     explicit IAdministrator(Core::System& system_, Common::UUID)
112         : ServiceFramework{system_, "IAdministrator"} {
113         // clang-format off
114         static const FunctionInfo functions[] = {
115             {0, nullptr, "CheckAvailability"},
116             {1, nullptr, "GetAccountId"},
117             {2, nullptr, "EnsureIdTokenCacheAsync"},
118             {3, nullptr, "LoadIdTokenCache"},
119             {100, nullptr, "SetSystemProgramIdentification"},
120             {101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+
121             {110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+
122             {111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+
123             {112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0
124             {113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+
125             {120, nullptr, "GetNintendoAccountId"},
126             {121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+
127             {130, nullptr, "GetNintendoAccountUserResourceCache"},
128             {131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"},
129             {132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"},
130             {133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+
131             {134, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsync"}, // 9.0.0+
132             {135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+
133             {140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+
134             {141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+
135             {142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+
136             {150, nullptr, "CreateAuthorizationRequest"},
137             {200, nullptr, "IsRegistered"},
138             {201, nullptr, "RegisterAsync"},
139             {202, nullptr, "UnregisterAsync"},
140             {203, nullptr, "DeleteRegistrationInfoLocally"},
141             {220, nullptr, "SynchronizeProfileAsync"},
142             {221, nullptr, "UploadProfileAsync"},
143             {222, nullptr, "SynchronizaProfileAsyncIfSecondsElapsed"},
144             {250, nullptr, "IsLinkedWithNintendoAccount"},
145             {251, nullptr, "CreateProcedureToLinkWithNintendoAccount"},
146             {252, nullptr, "ResumeProcedureToLinkWithNintendoAccount"},
147             {255, nullptr, "CreateProcedureToUpdateLinkageStateOfNintendoAccount"},
148             {256, nullptr, "ResumeProcedureToUpdateLinkageStateOfNintendoAccount"},
149             {260, nullptr, "CreateProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+
150             {261, nullptr, "ResumeProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+
151             {280, nullptr, "ProxyProcedureToAcquireApplicationAuthorizationForNintendoAccount"},
152             {290, nullptr, "GetRequestForNintendoAccountUserResourceView"}, // 8.0.0+
153             {300, nullptr, "TryRecoverNintendoAccountUserStateAsync"}, // 6.0.0+
154             {400, nullptr, "IsServiceEntryRequirementCacheRefreshRequiredForOnlinePlay"}, // 6.1.0+
155             {401, nullptr, "RefreshServiceEntryRequirementCacheForOnlinePlayAsync"}, // 6.1.0+
156             {900, nullptr, "GetAuthenticationInfoForWin"}, // 9.0.0+
157             {901, nullptr, "ImportAsyncForWin"}, // 9.0.0+
158             {997, nullptr, "DebugUnlinkNintendoAccountAsync"},
159             {998, nullptr, "DebugSetAvailabilityErrorDetail"},
160         };
161         // clang-format on
162 
163         RegisterHandlers(functions);
164     }
165 };
166 
167 class IAuthorizationRequest final : public ServiceFramework<IAuthorizationRequest> {
168 public:
IAuthorizationRequest(Core::System & system_,Common::UUID)169     explicit IAuthorizationRequest(Core::System& system_, Common::UUID)
170         : ServiceFramework{system_, "IAuthorizationRequest"} {
171         // clang-format off
172         static const FunctionInfo functions[] = {
173             {0, nullptr, "GetSessionId"},
174             {10, nullptr, "InvokeWithoutInteractionAsync"},
175             {19, nullptr, "IsAuthorized"},
176             {20, nullptr, "GetAuthorizationCode"},
177             {21, nullptr, "GetIdToken"},
178             {22, nullptr, "GetState"},
179         };
180         // clang-format on
181 
182         RegisterHandlers(functions);
183     }
184 };
185 
186 class IOAuthProcedure final : public ServiceFramework<IOAuthProcedure> {
187 public:
IOAuthProcedure(Core::System & system_,Common::UUID)188     explicit IOAuthProcedure(Core::System& system_, Common::UUID)
189         : ServiceFramework{system_, "IOAuthProcedure"} {
190         // clang-format off
191         static const FunctionInfo functions[] = {
192             {0, nullptr, "PrepareAsync"},
193             {1, nullptr, "GetRequest"},
194             {2, nullptr, "ApplyResponse"},
195             {3, nullptr, "ApplyResponseAsync"},
196             {10, nullptr, "Suspend"},
197         };
198         // clang-format on
199 
200         RegisterHandlers(functions);
201     }
202 };
203 
204 // 3.0.0+
205 class IOAuthProcedureForExternalNsa final : public ServiceFramework<IOAuthProcedureForExternalNsa> {
206 public:
IOAuthProcedureForExternalNsa(Core::System & system_,Common::UUID)207     explicit IOAuthProcedureForExternalNsa(Core::System& system_, Common::UUID)
208         : ServiceFramework{system_, "IOAuthProcedureForExternalNsa"} {
209         // clang-format off
210         static const FunctionInfo functions[] = {
211             {0, nullptr, "PrepareAsync"},
212             {1, nullptr, "GetRequest"},
213             {2, nullptr, "ApplyResponse"},
214             {3, nullptr, "ApplyResponseAsync"},
215             {10, nullptr, "Suspend"},
216             {100, nullptr, "GetAccountId"},
217             {101, nullptr, "GetLinkedNintendoAccountId"},
218             {102, nullptr, "GetNickname"},
219             {103, nullptr, "GetProfileImage"},
220         };
221         // clang-format on
222 
223         RegisterHandlers(functions);
224     }
225 };
226 
227 class IOAuthProcedureForNintendoAccountLinkage final
228     : public ServiceFramework<IOAuthProcedureForNintendoAccountLinkage> {
229 public:
IOAuthProcedureForNintendoAccountLinkage(Core::System & system_,Common::UUID)230     explicit IOAuthProcedureForNintendoAccountLinkage(Core::System& system_, Common::UUID)
231         : ServiceFramework{system_, "IOAuthProcedureForNintendoAccountLinkage"} {
232         // clang-format off
233         static const FunctionInfo functions[] = {
234             {0, nullptr, "PrepareAsync"},
235             {1, nullptr, "GetRequest"},
236             {2, nullptr, "ApplyResponse"},
237             {3, nullptr, "ApplyResponseAsync"},
238             {10, nullptr, "Suspend"},
239             {100, nullptr, "GetRequestWithTheme"},
240             {101, nullptr, "IsNetworkServiceAccountReplaced"},
241             {199, nullptr, "GetUrlForIntroductionOfExtraMembership"}, // 2.0.0 - 5.1.0
242         };
243         // clang-format on
244 
245         RegisterHandlers(functions);
246     }
247 };
248 
249 class INotifier final : public ServiceFramework<INotifier> {
250 public:
INotifier(Core::System & system_,Common::UUID)251     explicit INotifier(Core::System& system_, Common::UUID)
252         : ServiceFramework{system_, "INotifier"} {
253         // clang-format off
254         static const FunctionInfo functions[] = {
255             {0, nullptr, "GetSystemEvent"},
256         };
257         // clang-format on
258 
259         RegisterHandlers(functions);
260     }
261 };
262 
263 class IProfileCommon : public ServiceFramework<IProfileCommon> {
264 public:
IProfileCommon(Core::System & system_,const char * name,bool editor_commands,Common::UUID user_id_,ProfileManager & profile_manager_)265     explicit IProfileCommon(Core::System& system_, const char* name, bool editor_commands,
266                             Common::UUID user_id_, ProfileManager& profile_manager_)
267         : ServiceFramework{system_, name}, profile_manager{profile_manager_}, user_id{user_id_} {
268         static const FunctionInfo functions[] = {
269             {0, &IProfileCommon::Get, "Get"},
270             {1, &IProfileCommon::GetBase, "GetBase"},
271             {10, &IProfileCommon::GetImageSize, "GetImageSize"},
272             {11, &IProfileCommon::LoadImage, "LoadImage"},
273         };
274 
275         RegisterHandlers(functions);
276 
277         if (editor_commands) {
278             static const FunctionInfo editor_functions[] = {
279                 {100, &IProfileCommon::Store, "Store"},
280                 {101, &IProfileCommon::StoreWithImage, "StoreWithImage"},
281             };
282 
283             RegisterHandlers(editor_functions);
284         }
285     }
286 
287 protected:
Get(Kernel::HLERequestContext & ctx)288     void Get(Kernel::HLERequestContext& ctx) {
289         LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
290         ProfileBase profile_base{};
291         ProfileData data{};
292         if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
293             ctx.WriteBuffer(data);
294             IPC::ResponseBuilder rb{ctx, 16};
295             rb.Push(RESULT_SUCCESS);
296             rb.PushRaw(profile_base);
297         } else {
298             LOG_ERROR(Service_ACC, "Failed to get profile base and data for user={}",
299                       user_id.Format());
300             IPC::ResponseBuilder rb{ctx, 2};
301             rb.Push(RESULT_UNKNOWN); // TODO(ogniK): Get actual error code
302         }
303     }
304 
GetBase(Kernel::HLERequestContext & ctx)305     void GetBase(Kernel::HLERequestContext& ctx) {
306         LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
307         ProfileBase profile_base{};
308         if (profile_manager.GetProfileBase(user_id, profile_base)) {
309             IPC::ResponseBuilder rb{ctx, 16};
310             rb.Push(RESULT_SUCCESS);
311             rb.PushRaw(profile_base);
312         } else {
313             LOG_ERROR(Service_ACC, "Failed to get profile base for user={}", user_id.Format());
314             IPC::ResponseBuilder rb{ctx, 2};
315             rb.Push(RESULT_UNKNOWN); // TODO(ogniK): Get actual error code
316         }
317     }
318 
LoadImage(Kernel::HLERequestContext & ctx)319     void LoadImage(Kernel::HLERequestContext& ctx) {
320         LOG_DEBUG(Service_ACC, "called");
321 
322         IPC::ResponseBuilder rb{ctx, 3};
323         rb.Push(RESULT_SUCCESS);
324 
325         const Common::FS::IOFile image(GetImagePath(user_id), "rb");
326         if (!image.IsOpen()) {
327             LOG_WARNING(Service_ACC,
328                         "Failed to load user provided image! Falling back to built-in backup...");
329             ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG);
330             rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
331             return;
332         }
333 
334         const u32 size = SanitizeJPEGSize(image.GetSize());
335         std::vector<u8> buffer(size);
336         image.ReadBytes(buffer.data(), buffer.size());
337 
338         ctx.WriteBuffer(buffer);
339         rb.Push<u32>(size);
340     }
341 
GetImageSize(Kernel::HLERequestContext & ctx)342     void GetImageSize(Kernel::HLERequestContext& ctx) {
343         LOG_DEBUG(Service_ACC, "called");
344         IPC::ResponseBuilder rb{ctx, 3};
345         rb.Push(RESULT_SUCCESS);
346 
347         const Common::FS::IOFile image(GetImagePath(user_id), "rb");
348 
349         if (!image.IsOpen()) {
350             LOG_WARNING(Service_ACC,
351                         "Failed to load user provided image! Falling back to built-in backup...");
352             rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
353         } else {
354             rb.Push(SanitizeJPEGSize(image.GetSize()));
355         }
356     }
357 
Store(Kernel::HLERequestContext & ctx)358     void Store(Kernel::HLERequestContext& ctx) {
359         IPC::RequestParser rp{ctx};
360         const auto base = rp.PopRaw<ProfileBase>();
361 
362         const auto user_data = ctx.ReadBuffer();
363 
364         LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}",
365                   Common::StringFromFixedZeroTerminatedBuffer(
366                       reinterpret_cast<const char*>(base.username.data()), base.username.size()),
367                   base.timestamp, base.user_uuid.Format());
368 
369         if (user_data.size() < sizeof(ProfileData)) {
370             LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
371             IPC::ResponseBuilder rb{ctx, 2};
372             rb.Push(ERR_INVALID_BUFFER_SIZE);
373             return;
374         }
375 
376         ProfileData data;
377         std::memcpy(&data, user_data.data(), sizeof(ProfileData));
378 
379         if (!profile_manager.SetProfileBaseAndData(user_id, base, data)) {
380             LOG_ERROR(Service_ACC, "Failed to update profile data and base!");
381             IPC::ResponseBuilder rb{ctx, 2};
382             rb.Push(ERR_FAILED_SAVE_DATA);
383             return;
384         }
385 
386         IPC::ResponseBuilder rb{ctx, 2};
387         rb.Push(RESULT_SUCCESS);
388     }
389 
StoreWithImage(Kernel::HLERequestContext & ctx)390     void StoreWithImage(Kernel::HLERequestContext& ctx) {
391         IPC::RequestParser rp{ctx};
392         const auto base = rp.PopRaw<ProfileBase>();
393 
394         const auto user_data = ctx.ReadBuffer();
395         const auto image_data = ctx.ReadBuffer(1);
396 
397         LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}",
398                   Common::StringFromFixedZeroTerminatedBuffer(
399                       reinterpret_cast<const char*>(base.username.data()), base.username.size()),
400                   base.timestamp, base.user_uuid.Format());
401 
402         if (user_data.size() < sizeof(ProfileData)) {
403             LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
404             IPC::ResponseBuilder rb{ctx, 2};
405             rb.Push(ERR_INVALID_BUFFER_SIZE);
406             return;
407         }
408 
409         ProfileData data;
410         std::memcpy(&data, user_data.data(), sizeof(ProfileData));
411 
412         Common::FS::IOFile image(GetImagePath(user_id), "wb");
413 
414         if (!image.IsOpen() || !image.Resize(image_data.size()) ||
415             image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() ||
416             !profile_manager.SetProfileBaseAndData(user_id, base, data)) {
417             LOG_ERROR(Service_ACC, "Failed to update profile data, base, and image!");
418             IPC::ResponseBuilder rb{ctx, 2};
419             rb.Push(ERR_FAILED_SAVE_DATA);
420             return;
421         }
422 
423         IPC::ResponseBuilder rb{ctx, 2};
424         rb.Push(RESULT_SUCCESS);
425     }
426 
427     ProfileManager& profile_manager;
428     Common::UUID user_id{Common::INVALID_UUID}; ///< The user id this profile refers to.
429 };
430 
431 class IProfile final : public IProfileCommon {
432 public:
IProfile(Core::System & system_,Common::UUID user_id_,ProfileManager & profile_manager_)433     explicit IProfile(Core::System& system_, Common::UUID user_id_,
434                       ProfileManager& profile_manager_)
435         : IProfileCommon{system_, "IProfile", false, user_id_, profile_manager_} {}
436 };
437 
438 class IProfileEditor final : public IProfileCommon {
439 public:
IProfileEditor(Core::System & system_,Common::UUID user_id_,ProfileManager & profile_manager_)440     explicit IProfileEditor(Core::System& system_, Common::UUID user_id_,
441                             ProfileManager& profile_manager_)
442         : IProfileCommon{system_, "IProfileEditor", true, user_id_, profile_manager_} {}
443 };
444 
445 class IAsyncContext final : public ServiceFramework<IAsyncContext> {
446 public:
IAsyncContext(Core::System & system_)447     explicit IAsyncContext(Core::System& system_) : ServiceFramework{system_, "IAsyncContext"} {
448         // clang-format off
449         static const FunctionInfo functions[] = {
450             {0, nullptr, "GetSystemEvent"},
451             {1, nullptr, "Cancel"},
452             {2, nullptr, "HasDone"},
453             {3, nullptr, "GetResult"},
454         };
455         // clang-format on
456 
457         RegisterHandlers(functions);
458     }
459 };
460 
461 class ISessionObject final : public ServiceFramework<ISessionObject> {
462 public:
ISessionObject(Core::System & system_,Common::UUID)463     explicit ISessionObject(Core::System& system_, Common::UUID)
464         : ServiceFramework{system_, "ISessionObject"} {
465         // clang-format off
466         static const FunctionInfo functions[] = {
467             {999, nullptr, "Dummy"},
468         };
469         // clang-format on
470 
471         RegisterHandlers(functions);
472     }
473 };
474 
475 class IGuestLoginRequest final : public ServiceFramework<IGuestLoginRequest> {
476 public:
IGuestLoginRequest(Core::System & system_,Common::UUID)477     explicit IGuestLoginRequest(Core::System& system_, Common::UUID)
478         : ServiceFramework{system_, "IGuestLoginRequest"} {
479         // clang-format off
480         static const FunctionInfo functions[] = {
481             {0, nullptr, "GetSessionId"},
482             {11, nullptr, "Unknown"}, // 1.0.0 - 2.3.0 (the name is blank on Switchbrew)
483             {12, nullptr, "GetAccountId"},
484             {13, nullptr, "GetLinkedNintendoAccountId"},
485             {14, nullptr, "GetNickname"},
486             {15, nullptr, "GetProfileImage"},
487             {21, nullptr, "LoadIdTokenCache"}, // 3.0.0+
488         };
489         // clang-format on
490 
491         RegisterHandlers(functions);
492     }
493 };
494 
495 class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
496 public:
IManagerForApplication(Core::System & system_,Common::UUID user_id_)497     explicit IManagerForApplication(Core::System& system_, Common::UUID user_id_)
498         : ServiceFramework{system_, "IManagerForApplication"}, user_id{user_id_} {
499         // clang-format off
500         static const FunctionInfo functions[] = {
501             {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"},
502             {1, &IManagerForApplication::GetAccountId, "GetAccountId"},
503             {2, nullptr, "EnsureIdTokenCacheAsync"},
504             {3, nullptr, "LoadIdTokenCache"},
505             {130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"},
506             {150, nullptr, "CreateAuthorizationRequest"},
507             {160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"},
508             {170, nullptr, "LoadNetworkServiceLicenseKindAsync"},
509         };
510         // clang-format on
511 
512         RegisterHandlers(functions);
513     }
514 
515 private:
CheckAvailability(Kernel::HLERequestContext & ctx)516     void CheckAvailability(Kernel::HLERequestContext& ctx) {
517         LOG_WARNING(Service_ACC, "(STUBBED) called");
518         IPC::ResponseBuilder rb{ctx, 3};
519         rb.Push(RESULT_SUCCESS);
520         rb.Push(false); // TODO: Check when this is supposed to return true and when not
521     }
522 
GetAccountId(Kernel::HLERequestContext & ctx)523     void GetAccountId(Kernel::HLERequestContext& ctx) {
524         LOG_DEBUG(Service_ACC, "called");
525 
526         IPC::ResponseBuilder rb{ctx, 4};
527         rb.Push(RESULT_SUCCESS);
528         rb.PushRaw<u64>(user_id.GetNintendoID());
529     }
530 
StoreOpenContext(Kernel::HLERequestContext & ctx)531     void StoreOpenContext(Kernel::HLERequestContext& ctx) {
532         LOG_WARNING(Service_ACC, "(STUBBED) called");
533         IPC::ResponseBuilder rb{ctx, 2};
534         rb.Push(RESULT_SUCCESS);
535     }
536 
537     Common::UUID user_id;
538 };
539 
540 // 6.0.0+
541 class IAsyncNetworkServiceLicenseKindContext final
542     : public ServiceFramework<IAsyncNetworkServiceLicenseKindContext> {
543 public:
IAsyncNetworkServiceLicenseKindContext(Core::System & system_,Common::UUID)544     explicit IAsyncNetworkServiceLicenseKindContext(Core::System& system_, Common::UUID)
545         : ServiceFramework{system_, "IAsyncNetworkServiceLicenseKindContext"} {
546         // clang-format off
547         static const FunctionInfo functions[] = {
548             {0, nullptr, "GetSystemEvent"},
549             {1, nullptr, "Cancel"},
550             {2, nullptr, "HasDone"},
551             {3, nullptr, "GetResult"},
552             {4, nullptr, "GetNetworkServiceLicenseKind"},
553         };
554         // clang-format on
555 
556         RegisterHandlers(functions);
557     }
558 };
559 
560 // 8.0.0+
561 class IOAuthProcedureForUserRegistration final
562     : public ServiceFramework<IOAuthProcedureForUserRegistration> {
563 public:
IOAuthProcedureForUserRegistration(Core::System & system_,Common::UUID)564     explicit IOAuthProcedureForUserRegistration(Core::System& system_, Common::UUID)
565         : ServiceFramework{system_, "IOAuthProcedureForUserRegistration"} {
566         // clang-format off
567         static const FunctionInfo functions[] = {
568             {0, nullptr, "PrepareAsync"},
569             {1, nullptr, "GetRequest"},
570             {2, nullptr, "ApplyResponse"},
571             {3, nullptr, "ApplyResponseAsync"},
572             {10, nullptr, "Suspend"},
573             {100, nullptr, "GetAccountId"},
574             {101, nullptr, "GetLinkedNintendoAccountId"},
575             {102, nullptr, "GetNickname"},
576             {103, nullptr, "GetProfileImage"},
577             {110, nullptr, "RegisterUserAsync"},
578             {111, nullptr, "GetUid"},
579         };
580         // clang-format on
581 
582         RegisterHandlers(functions);
583     }
584 };
585 
586 class DAUTH_O final : public ServiceFramework<DAUTH_O> {
587 public:
DAUTH_O(Core::System & system_,Common::UUID)588     explicit DAUTH_O(Core::System& system_, Common::UUID) : ServiceFramework{system_, "dauth:o"} {
589         // clang-format off
590         static const FunctionInfo functions[] = {
591             {0, nullptr, "EnsureAuthenticationTokenCacheAsync"}, // [5.0.0-5.1.0] GeneratePostData
592             {1, nullptr, "LoadAuthenticationTokenCache"}, // 6.0.0+
593             {2, nullptr, "InvalidateAuthenticationTokenCache"}, // 6.0.0+
594             {10, nullptr, "EnsureEdgeTokenCacheAsync"}, // 6.0.0+
595             {11, nullptr, "LoadEdgeTokenCache"}, // 6.0.0+
596             {12, nullptr, "InvalidateEdgeTokenCache"}, // 6.0.0+
597         };
598         // clang-format on
599 
600         RegisterHandlers(functions);
601     }
602 };
603 
604 // 6.0.0+
605 class IAsyncResult final : public ServiceFramework<IAsyncResult> {
606 public:
IAsyncResult(Core::System & system_,Common::UUID)607     explicit IAsyncResult(Core::System& system_, Common::UUID)
608         : ServiceFramework{system_, "IAsyncResult"} {
609         // clang-format off
610         static const FunctionInfo functions[] = {
611             {0, nullptr, "GetResult"},
612             {1, nullptr, "Cancel"},
613             {2, nullptr, "IsAvailable"},
614             {3, nullptr, "GetSystemEvent"},
615         };
616         // clang-format on
617 
618         RegisterHandlers(functions);
619     }
620 };
621 
GetUserCount(Kernel::HLERequestContext & ctx)622 void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
623     LOG_DEBUG(Service_ACC, "called");
624     IPC::ResponseBuilder rb{ctx, 3};
625     rb.Push(RESULT_SUCCESS);
626     rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount()));
627 }
628 
GetUserExistence(Kernel::HLERequestContext & ctx)629 void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
630     IPC::RequestParser rp{ctx};
631     Common::UUID user_id = rp.PopRaw<Common::UUID>();
632     LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
633 
634     IPC::ResponseBuilder rb{ctx, 3};
635     rb.Push(RESULT_SUCCESS);
636     rb.Push(profile_manager->UserExists(user_id));
637 }
638 
ListAllUsers(Kernel::HLERequestContext & ctx)639 void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) {
640     LOG_DEBUG(Service_ACC, "called");
641     ctx.WriteBuffer(profile_manager->GetAllUsers());
642     IPC::ResponseBuilder rb{ctx, 2};
643     rb.Push(RESULT_SUCCESS);
644 }
645 
ListOpenUsers(Kernel::HLERequestContext & ctx)646 void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) {
647     LOG_DEBUG(Service_ACC, "called");
648     ctx.WriteBuffer(profile_manager->GetOpenUsers());
649     IPC::ResponseBuilder rb{ctx, 2};
650     rb.Push(RESULT_SUCCESS);
651 }
652 
GetLastOpenedUser(Kernel::HLERequestContext & ctx)653 void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
654     LOG_DEBUG(Service_ACC, "called");
655     IPC::ResponseBuilder rb{ctx, 6};
656     rb.Push(RESULT_SUCCESS);
657     rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser());
658 }
659 
GetProfile(Kernel::HLERequestContext & ctx)660 void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
661     IPC::RequestParser rp{ctx};
662     Common::UUID user_id = rp.PopRaw<Common::UUID>();
663     LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
664 
665     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
666     rb.Push(RESULT_SUCCESS);
667     rb.PushIpcInterface<IProfile>(system, user_id, *profile_manager);
668 }
669 
IsUserRegistrationRequestPermitted(Kernel::HLERequestContext & ctx)670 void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx) {
671     LOG_WARNING(Service_ACC, "(STUBBED) called");
672     IPC::ResponseBuilder rb{ctx, 3};
673     rb.Push(RESULT_SUCCESS);
674     rb.Push(profile_manager->CanSystemRegisterUser());
675 }
676 
InitializeApplicationInfo(Kernel::HLERequestContext & ctx)677 void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
678     IPC::RequestParser rp{ctx};
679 
680     LOG_DEBUG(Service_ACC, "called");
681     IPC::ResponseBuilder rb{ctx, 2};
682     rb.Push(InitializeApplicationInfoBase());
683 }
684 
InitializeApplicationInfoRestricted(Kernel::HLERequestContext & ctx)685 void Module::Interface::InitializeApplicationInfoRestricted(Kernel::HLERequestContext& ctx) {
686     IPC::RequestParser rp{ctx};
687 
688     LOG_WARNING(Service_ACC, "(Partial implementation) called");
689 
690     // TODO(ogniK): We require checking if the user actually owns the title and what not. As of
691     // currently, we assume the user owns the title. InitializeApplicationInfoBase SHOULD be called
692     // first then we do extra checks if the game is a digital copy.
693 
694     IPC::ResponseBuilder rb{ctx, 2};
695     rb.Push(InitializeApplicationInfoBase());
696 }
697 
InitializeApplicationInfoBase()698 ResultCode Module::Interface::InitializeApplicationInfoBase() {
699     if (application_info) {
700         LOG_ERROR(Service_ACC, "Application already initialized");
701         return ERR_ACCOUNTINFO_ALREADY_INITIALIZED;
702     }
703 
704     // TODO(ogniK): This should be changed to reflect the target process for when we have multiple
705     // processes emulated. As we don't actually have pid support we should assume we're just using
706     // our own process
707     const auto& current_process = system.Kernel().CurrentProcess();
708     const auto launch_property =
709         system.GetARPManager().GetLaunchProperty(current_process->GetTitleID());
710 
711     if (launch_property.Failed()) {
712         LOG_ERROR(Service_ACC, "Failed to get launch property");
713         return ERR_ACCOUNTINFO_BAD_APPLICATION;
714     }
715 
716     switch (launch_property->base_game_storage_id) {
717     case FileSys::StorageId::GameCard:
718         application_info.application_type = ApplicationType::GameCard;
719         break;
720     case FileSys::StorageId::Host:
721     case FileSys::StorageId::NandUser:
722     case FileSys::StorageId::SdCard:
723     case FileSys::StorageId::None: // Yuzu specific, differs from hardware
724         application_info.application_type = ApplicationType::Digital;
725         break;
726     default:
727         LOG_ERROR(Service_ACC, "Invalid game storage ID! storage_id={}",
728                   launch_property->base_game_storage_id);
729         return ERR_ACCOUNTINFO_BAD_APPLICATION;
730     }
731 
732     LOG_WARNING(Service_ACC, "ApplicationInfo init required");
733     // TODO(ogniK): Actual initalization here
734 
735     return RESULT_SUCCESS;
736 }
737 
GetBaasAccountManagerForApplication(Kernel::HLERequestContext & ctx)738 void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx) {
739     LOG_DEBUG(Service_ACC, "called");
740     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
741     rb.Push(RESULT_SUCCESS);
742     rb.PushIpcInterface<IManagerForApplication>(system, profile_manager->GetLastOpenedUser());
743 }
744 
IsUserAccountSwitchLocked(Kernel::HLERequestContext & ctx)745 void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) {
746     LOG_DEBUG(Service_ACC, "called");
747     FileSys::NACP nacp;
748     const auto res = system.GetAppLoader().ReadControlData(nacp);
749 
750     bool is_locked = false;
751 
752     if (res != Loader::ResultStatus::Success) {
753         const FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID(),
754                                        system.GetFileSystemController(),
755                                        system.GetContentProvider()};
756         const auto nacp_unique = pm.GetControlMetadata().first;
757 
758         if (nacp_unique != nullptr) {
759             is_locked = nacp_unique->GetUserAccountSwitchLock();
760         } else {
761             LOG_ERROR(Service_ACC, "nacp_unique is null!");
762         }
763     } else {
764         is_locked = nacp.GetUserAccountSwitchLock();
765     }
766 
767     IPC::ResponseBuilder rb{ctx, 3};
768     rb.Push(RESULT_SUCCESS);
769     rb.Push(is_locked);
770 }
771 
GetProfileEditor(Kernel::HLERequestContext & ctx)772 void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) {
773     IPC::RequestParser rp{ctx};
774     Common::UUID user_id = rp.PopRaw<Common::UUID>();
775 
776     LOG_DEBUG(Service_ACC, "called, user_id={}", user_id.Format());
777 
778     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
779     rb.Push(RESULT_SUCCESS);
780     rb.PushIpcInterface<IProfileEditor>(system, user_id, *profile_manager);
781 }
782 
ListQualifiedUsers(Kernel::HLERequestContext & ctx)783 void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) {
784     LOG_DEBUG(Service_ACC, "called");
785 
786     // All users should be qualified. We don't actually have parental control or anything to do with
787     // nintendo online currently. We're just going to assume the user running the game has access to
788     // the game regardless of parental control settings.
789     ctx.WriteBuffer(profile_manager->GetAllUsers());
790     IPC::ResponseBuilder rb{ctx, 2};
791     rb.Push(RESULT_SUCCESS);
792 }
793 
LoadOpenContext(Kernel::HLERequestContext & ctx)794 void Module::Interface::LoadOpenContext(Kernel::HLERequestContext& ctx) {
795     LOG_WARNING(Service_ACC, "(STUBBED) called");
796 
797     // This is similar to GetBaasAccountManagerForApplication
798     // This command is used concurrently with ListOpenContextStoredUsers
799     // TODO: Find the differences between this and GetBaasAccountManagerForApplication
800     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
801     rb.Push(RESULT_SUCCESS);
802     rb.PushIpcInterface<IManagerForApplication>(system, profile_manager->GetLastOpenedUser());
803 }
804 
ListOpenContextStoredUsers(Kernel::HLERequestContext & ctx)805 void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) {
806     LOG_WARNING(Service_ACC, "(STUBBED) called");
807 
808     // TODO(ogniK): Handle open contexts
809     ctx.WriteBuffer(profile_manager->GetOpenUsers());
810     IPC::ResponseBuilder rb{ctx, 2};
811     rb.Push(RESULT_SUCCESS);
812 }
813 
TrySelectUserWithoutInteraction(Kernel::HLERequestContext & ctx)814 void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
815     LOG_DEBUG(Service_ACC, "called");
816     // A u8 is passed into this function which we can safely ignore. It's to determine if we have
817     // access to use the network or not by the looks of it
818     IPC::ResponseBuilder rb{ctx, 6};
819     if (profile_manager->GetUserCount() != 1) {
820         rb.Push(RESULT_SUCCESS);
821         rb.PushRaw<u128>(Common::INVALID_UUID);
822         return;
823     }
824 
825     const auto user_list = profile_manager->GetAllUsers();
826     if (std::all_of(user_list.begin(), user_list.end(),
827                     [](const auto& user) { return user.uuid == Common::INVALID_UUID; })) {
828         rb.Push(RESULT_UNKNOWN); // TODO(ogniK): Find the correct error code
829         rb.PushRaw<u128>(Common::INVALID_UUID);
830         return;
831     }
832 
833     // Select the first user we have
834     rb.Push(RESULT_SUCCESS);
835     rb.PushRaw<u128>(profile_manager->GetUser(0)->uuid);
836 }
837 
Interface(std::shared_ptr<Module> module_,std::shared_ptr<ProfileManager> profile_manager_,Core::System & system_,const char * name)838 Module::Interface::Interface(std::shared_ptr<Module> module_,
839                              std::shared_ptr<ProfileManager> profile_manager_,
840                              Core::System& system_, const char* name)
841     : ServiceFramework{system_, name}, module{std::move(module_)}, profile_manager{std::move(
842                                                                        profile_manager_)} {}
843 
844 Module::Interface::~Interface() = default;
845 
InstallInterfaces(Core::System & system)846 void InstallInterfaces(Core::System& system) {
847     auto module = std::make_shared<Module>();
848     auto profile_manager = std::make_shared<ProfileManager>();
849 
850     std::make_shared<ACC_AA>(module, profile_manager, system)
851         ->InstallAsService(system.ServiceManager());
852     std::make_shared<ACC_SU>(module, profile_manager, system)
853         ->InstallAsService(system.ServiceManager());
854     std::make_shared<ACC_U0>(module, profile_manager, system)
855         ->InstallAsService(system.ServiceManager());
856     std::make_shared<ACC_U1>(module, profile_manager, system)
857         ->InstallAsService(system.ServiceManager());
858 }
859 
860 } // namespace Service::Account
861