// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include #include #include "common/common_types.h" #include "common/fs/file.h" #include "common/fs/path_util.h" #include "common/logging.h" #include #include "common/stb.h" #include "common/string_util.h" #include "common/swap.h" #include "core/constants.h" #include "core/core.h" #include "core/core_timing.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/hle/service/acc/acc.h" #include "core/hle/service/acc/async_context.h" #include "core/hle/service/acc/errors.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/cmif_serialization.h" #include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/server_manager.h" #include "core/loader/loader.h" namespace Service::Account { // Thumbnails are hard coded to be at least this size constexpr std::size_t THUMBNAIL_SIZE = 0x24000; static std::filesystem::path GetImagePath(const Common::UUID& uuid) { return Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString()); } static void JPGToMemory(void* context, void* data, int len) { std::vector* jpg_image = static_cast*>(context); unsigned char* jpg = static_cast(data); jpg_image->insert(jpg_image->end(), jpg, jpg + len); } static void SanitizeJPEGImageSize(std::vector& image) { constexpr std::size_t max_jpeg_image_size = 0x20000; constexpr int profile_dimensions = 256; int original_width, original_height, color_channels; const auto plain_image = stbi_load_from_memory(image.data(), static_cast(image.size()), &original_width, &original_height, &color_channels, STBI_rgb); // Resize image to match 256*256 if (original_width != profile_dimensions || original_height != profile_dimensions) { // Use vector instead of array to avoid overflowing the stack std::vector out_image(profile_dimensions * profile_dimensions * STBI_rgb); stbir_resize_uint8_srgb(plain_image, original_width, original_height, 0, out_image.data(), profile_dimensions, profile_dimensions, 0, STBI_rgb, 0, STBIR_FILTER_BOX); image.clear(); if (!stbi_write_jpg_to_func(JPGToMemory, &image, profile_dimensions, profile_dimensions, STBI_rgb, out_image.data(), 0)) { LOG_ERROR(Service_ACC, "Failed to resize the user provided image."); } } image.resize((std::min)(image.size(), max_jpeg_image_size)); } class IManagerForSystemService final : public ServiceFramework { public: explicit IManagerForSystemService(Core::System& system_, Common::UUID uuid) : ServiceFramework{system_, "IManagerForSystemService"}, account_id{uuid} { // clang-format off static const FunctionInfo functions[] = { {0, D<&IManagerForSystemService::CheckAvailability>, "CheckAvailability"}, {1, D<&IManagerForSystemService::GetAccountId>, "GetAccountId"}, {2, nullptr, "EnsureIdTokenCacheAsync"}, {3, D<&IManagerForSystemService::LoadIdTokenCacheDeprecated>, "LoadIdTokenCacheDeprecated"}, // 19.0.0+ {4, D<&IManagerForSystemService::LoadIdTokenCache>, "LoadIdTokenCache"}, // 19.0.0+ {100, nullptr, "SetSystemProgramIdentification"}, {101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+ {110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+ {111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+ {112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0 {113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+ {120, nullptr, "GetNintendoAccountId"}, {121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+ {130, nullptr, "GetNintendoAccountUserResourceCache"}, {131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"}, {132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"}, {133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+ {134, nullptr, "RefreshNintendoAccountVerificationUrlCache"}, // 9.0.0+ {135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+ {136, nullptr, "GetNintendoAccountUserResourceCache"}, // 19.0.0+ {140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+ {141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+ {142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+ {143, D<&IManagerForSystemService::GetNetworkServiceLicenseCacheEx>, "GetNetworkServiceLicenseCacheEx"}, // 15.0.0+ {150, nullptr, "CreateAuthorizationRequest"}, {160, nullptr, "RequiresUpdateNetworkServiceAccountIdTokenCache"}, {161, nullptr, "RequireReauthenticationOfNetworkServiceAccount"}, {170, nullptr, "CreateDeviceHistoryRequest"}, // 17.0.0+ {180, nullptr, "GetRequestForNintendoAccountReauthentication"} // 18.0.0+ }; // clang-format on RegisterHandlers(functions); } private: Result CheckAvailability() { LOG_WARNING(Service_ACC, "(STUBBED) called"); R_SUCCEED(); } Result GetAccountId(Out out_account_id) { LOG_WARNING(Service_ACC, "(STUBBED) called"); *out_account_id = account_id.Hash(); R_SUCCEED(); } Result LoadIdTokenCacheDeprecated() { LOG_WARNING(Service_ACC, "(STUBBED) called"); R_SUCCEED(); } Result LoadIdTokenCache() { LOG_WARNING(Service_ACC, "(STUBBED) called"); R_SUCCEED(); } Result GetNetworkServiceLicenseCacheEx(Out out_license, Out out_expiration) { LOG_DEBUG(Service_ACC, "(STUBBED) called."); *out_license = 0; *out_expiration = 0; R_SUCCEED(); } Common::UUID account_id; }; // 3.0.0+ class IFloatingRegistrationRequest final : public ServiceFramework { public: explicit IFloatingRegistrationRequest(Core::System& system_, Common::UUID) : ServiceFramework{system_, "IFloatingRegistrationRequest"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetSessionId"}, {12, nullptr, "GetAccountId"}, {13, nullptr, "GetLinkedNintendoAccountId"}, {14, nullptr, "GetNickname"}, {15, nullptr, "GetProfileImage"}, {16, nullptr, "GetProfileLargeImage"}, // 18.0.0+ {21, nullptr, "LoadIdTokenCache"}, {100, nullptr, "RegisterUser"}, // [1.0.0-3.0.2] RegisterAsync {101, nullptr, "RegisterUserWithUid"}, // [1.0.0-3.0.2] RegisterWithUidAsync {102, nullptr, "RegisterNetworkServiceAccountAsync"}, // 4.0.0+ {103, nullptr, "RegisterNetworkServiceAccountWithUidAsync"}, // 4.0.0+ {110, nullptr, "SetSystemProgramIdentification"}, {111, nullptr, "EnsureIdTokenCacheAsync"}, }; // clang-format on RegisterHandlers(functions); } }; class IAdministrator final : public ServiceFramework { public: explicit IAdministrator(Core::System& system_, Common::UUID) : ServiceFramework{system_, "IAdministrator"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "CheckAvailability"}, {1, nullptr, "GetAccountId"}, {2, nullptr, "EnsureIdTokenCacheAsync"}, {3, nullptr, "LoadIdTokenCache"}, {100, nullptr, "SetSystemProgramIdentification"}, {101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+ {110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+ {111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+ {112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0 {113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+ {120, nullptr, "GetNintendoAccountId"}, {121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+ {130, nullptr, "GetNintendoAccountUserResourceCache"}, {131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"}, {132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"}, {133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+ {134, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsync"}, // 9.0.0+ {135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+ {140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+ {141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+ {142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+ {143, nullptr, "GetNetworkServiceLicenseCacheEx"}, {150, nullptr, "CreateAuthorizationRequest"}, {160, nullptr, "RequiresUpdateNetworkServiceAccountIdTokenCache"}, {161, nullptr, "RequireReauthenticationOfNetworkServiceAccount"}, {170, nullptr, "CreateDeviceHistoryRequest"}, // 17.0.0+ {180, nullptr, "GetRequestForNintendoAccountReauthentication"}, // 18.0.0+ {200, nullptr, "IsRegistered"}, {201, nullptr, "RegisterAsync"}, {202, nullptr, "UnregisterAsync"}, {203, nullptr, "DeleteRegistrationInfoLocally"}, {220, nullptr, "SynchronizeProfileAsync"}, {221, nullptr, "UploadProfileAsync"}, {222, nullptr, "SynchronizaProfileAsyncIfSecondsElapsed"}, {250, &IAdministrator::IsLinkedWithNintendoAccount, "IsLinkedWithNintendoAccount"}, {251, nullptr, "CreateProcedureToLinkWithNintendoAccount"}, {252, nullptr, "ResumeProcedureToLinkWithNintendoAccount"}, {255, nullptr, "CreateProcedureToUpdateLinkageStateOfNintendoAccount"}, {256, nullptr, "ResumeProcedureToUpdateLinkageStateOfNintendoAccount"}, {260, nullptr, "CreateProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+ {261, nullptr, "ResumeProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+ {280, nullptr, "ProxyProcedureToAcquireApplicationAuthorizationForNintendoAccount"}, {290, nullptr, "GetRequestForNintendoAccountUserResourceView"}, // 8.0.0+ {300, nullptr, "TryRecoverNintendoAccountUserStateAsync"}, // 6.0.0+ {400, nullptr, "IsServiceEntryRequirementCacheRefreshRequiredForOnlinePlay"}, // 6.1.0+ {401, nullptr, "RefreshServiceEntryRequirementCacheForOnlinePlayAsync"}, // 6.1.0+ {900, nullptr, "GetAuthenticationInfoForWin"}, // 9.0.0+ {901, nullptr, "ImportAsyncForWin"}, // 9.0.0+ {997, nullptr, "DebugUnlinkNintendoAccountAsync"}, {998, nullptr, "DebugSetAvailabilityErrorDetail"}, }; // clang-format on RegisterHandlers(functions); } private: void IsLinkedWithNintendoAccount(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(false); } }; class IAuthorizationRequest final : public ServiceFramework { public: explicit IAuthorizationRequest(Core::System& system_, Common::UUID) : ServiceFramework{system_, "IAuthorizationRequest"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetSessionId"}, {10, nullptr, "InvokeWithoutInteractionAsync"}, {19, nullptr, "IsAuthorized"}, {20, nullptr, "GetAuthorizationCode"}, {21, nullptr, "GetIdToken"}, {22, nullptr, "GetState"}, }; // clang-format on RegisterHandlers(functions); } }; class IOAuthProcedure final : public ServiceFramework { public: explicit IOAuthProcedure(Core::System& system_, Common::UUID) : ServiceFramework{system_, "IOAuthProcedure"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "PrepareAsync"}, {1, nullptr, "GetRequest"}, {2, nullptr, "ApplyResponse"}, {3, nullptr, "ApplyResponseAsync"}, {10, nullptr, "Suspend"}, }; // clang-format on RegisterHandlers(functions); } }; // 3.0.0+ class IOAuthProcedureForExternalNsa final : public ServiceFramework { public: explicit IOAuthProcedureForExternalNsa(Core::System& system_, Common::UUID) : ServiceFramework{system_, "IOAuthProcedureForExternalNsa"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "PrepareAsync"}, {1, nullptr, "GetRequest"}, {2, nullptr, "ApplyResponse"}, {3, nullptr, "ApplyResponseAsync"}, {10, nullptr, "Suspend"}, {100, nullptr, "GetAccountId"}, {101, nullptr, "GetLinkedNintendoAccountId"}, {102, nullptr, "GetNickname"}, {103, nullptr, "GetProfileImage"}, {104, nullptr, "GetProfileLargeImage"} // 18.0.0+ }; // clang-format on RegisterHandlers(functions); } }; class IOAuthProcedureForNintendoAccountLinkage final : public ServiceFramework { public: explicit IOAuthProcedureForNintendoAccountLinkage(Core::System& system_, Common::UUID) : ServiceFramework{system_, "IOAuthProcedureForNintendoAccountLinkage"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "PrepareAsync"}, {1, nullptr, "GetRequest"}, {2, nullptr, "ApplyResponse"}, {3, nullptr, "ApplyResponseAsync"}, {10, nullptr, "Suspend"}, {100, nullptr, "GetRequestWithTheme"}, {101, nullptr, "IsNetworkServiceAccountReplaced"}, {199, nullptr, "GetUrlForIntroductionOfExtraMembership"}, // 2.0.0 - 5.1.0 {200, nullptr, "ApplyAsyncWithAuthorizedToken"}, {210, nullptr, "IsProfileAvailable"}, // 17.0.0+ {220, nullptr, "RegisterUserAsyncWithoutProfile"}, // 17.0.0+ {221, nullptr, "RegisterUserWithProfileAsync"}, // 17.0.0+ {230, nullptr, "RegisterUserWithLargeImageProfileAsync"}, // 18.0.0+ }; // clang-format on RegisterHandlers(functions); } }; class INotifier final : public ServiceFramework { public: explicit INotifier(Core::System& system_, Common::UUID) : ServiceFramework{system_, "INotifier"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetSystemEvent"}, }; // clang-format on RegisterHandlers(functions); } }; class IProfileCommon : public ServiceFramework { public: explicit IProfileCommon(Core::System& system_, const char* name, bool editor_commands, Common::UUID user_id_, ProfileManager& profile_manager_) : ServiceFramework{system_, name}, profile_manager{profile_manager_}, user_id{user_id_} { // clang-format off static const FunctionInfo functions[] = { {0, &IProfileCommon::Get, "Get"}, {1, &IProfileCommon::GetBase, "GetBase"}, {10, &IProfileCommon::GetImageSize, "GetImageSize"}, {11, &IProfileCommon::LoadImage, "LoadImage"}, {20, &IProfileCommon::Unknown20, "Unknown20"}, {21, &IProfileCommon::Unknown21, "Unknown21"}, {30, &IProfileCommon::Unknown30, "Unknown30"} }; // clang-format on RegisterHandlers(functions); if (editor_commands) { // clang-format off static const FunctionInfo editor_functions[] = { {100, &IProfileCommon::Store, "Store"}, {101, &IProfileCommon::StoreWithImage, "StoreWithImage"}, {110, &IProfileCommon::Unknown110, "Unknown110"} }; // clang-format on RegisterHandlers(editor_functions); } } protected: void Unknown20(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "(STUBBED) called."); // TODO (jarrodnorwell) // inbytes: 0x0, outbytes: 0x4 IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Unknown21(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "(STUBBED) called."); // TODO (jarrodnorwell) // buffers: [0x6], inbytes: 0x0, outbytes: 0x4 IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Unknown30(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "(STUBBED) called."); // TODO (jarrodnorwell) // inbytes: 0x0, outbytes: 0x10 IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Unknown110(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "(STUBBED) called."); // TODO (jarrodnorwell) // buffer_entry_sizes: [0x80, 0x0], buffers: [0x19, 0x5], inbytes: 0x38, outbytes: 0x0 IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Get(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString()); ProfileBase profile_base{}; UserData data{}; if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { ctx.WriteBuffer(data); IPC::ResponseBuilder rb{ctx, 16}; rb.Push(ResultSuccess); rb.PushRaw(profile_base); } else { LOG_ERROR(Service_ACC, "Failed to get profile base and data for user=0x{}", user_id.RawString()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code } } void GetBase(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString()); ProfileBase profile_base{}; if (profile_manager.GetProfileBase(user_id, profile_base)) { IPC::ResponseBuilder rb{ctx, 16}; rb.Push(ResultSuccess); rb.PushRaw(profile_base); } else { LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.RawString()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code } } void LoadImage(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); const Common::FS::IOFile image(GetImagePath(user_id), Common::FS::FileAccessMode::Read, Common::FS::FileType::BinaryFile); if (!image.IsOpen()) { LOG_WARNING(Service_ACC, "Failed to load user provided image! Falling back to built-in backup..."); ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG); rb.Push(static_cast(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); return; } std::vector buffer(image.GetSize()); if (image.Read(buffer) != buffer.size()) { LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image."); } SanitizeJPEGImageSize(buffer); ctx.WriteBuffer(buffer); rb.Push(static_cast(buffer.size())); } void GetImageSize(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); const Common::FS::IOFile image(GetImagePath(user_id), Common::FS::FileAccessMode::Read, Common::FS::FileType::BinaryFile); if (!image.IsOpen()) { LOG_WARNING(Service_ACC, "Failed to load user provided image! Falling back to built-in backup..."); rb.Push(static_cast(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); return; } std::vector buffer(image.GetSize()); if (image.Read(buffer) != buffer.size()) { LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image."); } SanitizeJPEGImageSize(buffer); rb.Push(static_cast(buffer.size())); } void LoadIdTokenCache(HLERequestContext& ctx) { LOG_WARNING(Service_ACC, "(STUBBED) called"); std::vector token_data(0x100); std::fill(token_data.begin(), token_data.end(), u8(0)); (void)ctx.WriteBuffer(token_data); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(static_cast(token_data.size())); } void GetNintendoAccountUserResourceCacheForApplication(HLERequestContext& ctx) { LOG_WARNING(Service_ACC, "(STUBBED) called"); std::vector nas_user_base_for_application(0x68); (void)ctx.WriteBuffer(nas_user_base_for_application); if (ctx.CanWriteBuffer(1)) { std::vector unknown_out_buffer(ctx.GetWriteBufferSize(1)); (void)ctx.WriteBuffer(unknown_out_buffer, 1); } IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); rb.PushRaw(profile_manager.GetLastOpenedUser().Hash()); } void Store(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto base = rp.PopRaw(); const auto user_data = ctx.ReadBuffer(); LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}", Common::StringFromFixedZeroTerminatedBuffer( reinterpret_cast(base.username.data()), base.username.size()), base.timestamp, base.user_uuid.RawString()); if (user_data.size() < sizeof(UserData)) { LOG_ERROR(Service_ACC, "UserData buffer too small!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(Account::ResultInvalidArrayLength); return; } UserData data; std::memcpy(&data, user_data.data(), sizeof(UserData)); if (!profile_manager.SetProfileBaseAndData(user_id, base, data)) { LOG_ERROR(Service_ACC, "Failed to update user data and base!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(Account::ResultAccountUpdateFailed); return; } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void StoreWithImage(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto base = rp.PopRaw(); const auto image_data = ctx.ReadBufferA(0); const auto user_data = ctx.ReadBufferX(0); LOG_INFO(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}", Common::StringFromFixedZeroTerminatedBuffer( reinterpret_cast(base.username.data()), base.username.size()), base.timestamp, base.user_uuid.RawString()); if (user_data.size() < sizeof(UserData)) { LOG_ERROR(Service_ACC, "UserData buffer too small!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(Account::ResultInvalidArrayLength); return; } UserData data; std::memcpy(&data, user_data.data(), sizeof(UserData)); Common::FS::IOFile image(GetImagePath(user_id), Common::FS::FileAccessMode::Write, Common::FS::FileType::BinaryFile); if (!image.IsOpen() || !image.SetSize(image_data.size()) || image.Write(image_data) != image_data.size() || !profile_manager.SetProfileBaseAndData(user_id, base, data)) { LOG_ERROR(Service_ACC, "Failed to update profile data, base, and image!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(Account::ResultAccountUpdateFailed); return; } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } ProfileManager& profile_manager; Common::UUID user_id{}; ///< The user id this profile refers to. }; class IProfile final : public IProfileCommon { public: explicit IProfile(Core::System& system_, Common::UUID user_id_, ProfileManager& profile_manager_) : IProfileCommon{system_, "IProfile", false, user_id_, profile_manager_} {} }; class IProfileEditor final : public IProfileCommon { public: explicit IProfileEditor(Core::System& system_, Common::UUID user_id_, ProfileManager& profile_manager_) : IProfileCommon{system_, "IProfileEditor", true, user_id_, profile_manager_} {} }; class ISessionObject final : public ServiceFramework { public: explicit ISessionObject(Core::System& system_, Common::UUID) : ServiceFramework{system_, "ISessionObject"} { // clang-format off static const FunctionInfo functions[] = { {999, nullptr, "Dummy"}, }; // clang-format on RegisterHandlers(functions); } }; class IGuestLoginRequest final : public ServiceFramework { public: explicit IGuestLoginRequest(Core::System& system_, Common::UUID) : ServiceFramework{system_, "IGuestLoginRequest"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetSessionId"}, {11, nullptr, "Unknown"}, // 1.0.0 - 2.3.0 (the name is blank on Switchbrew) {12, nullptr, "GetAccountId"}, {13, nullptr, "GetLinkedNintendoAccountId"}, {14, nullptr, "GetNickname"}, {15, nullptr, "GetProfileImage"}, {16, nullptr, "GetProfileLargeImage"}, // 18.0.0+ {21, nullptr, "LoadIdTokenCache"}, // 3.0.0+ }; // clang-format on RegisterHandlers(functions); } }; class EnsureTokenIdCacheAsyncInterface final : public IAsyncContext { public: explicit EnsureTokenIdCacheAsyncInterface(Core::System& system_) : IAsyncContext{system_} { MarkComplete(); } ~EnsureTokenIdCacheAsyncInterface() = default; void LoadIdTokenCache(HLERequestContext& ctx) { LOG_WARNING(Service_ACC, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(0); } protected: bool IsComplete() const override { return true; } void Cancel() override {} Result GetResult() const override { return ResultSuccess; } }; class IManagerForApplication final : public ServiceFramework { public: explicit IManagerForApplication(Core::System& system_, const std::shared_ptr& profile_manager_) : ServiceFramework{system_, "IManagerForApplication"}, ensure_token_id{std::make_shared(system)}, profile_manager{profile_manager_} { // clang-format off static const FunctionInfo functions[] = { {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, {1, &IManagerForApplication::GetAccountId, "GetAccountId"}, {2, &IManagerForApplication::EnsureIdTokenCacheAsync, "EnsureIdTokenCacheAsync"}, {3, &IManagerForApplication::LoadIdTokenCacheDeprecated, "LoadIdTokenCacheDeprecated"}, {4, &IManagerForApplication::LoadIdTokenCache, "LoadIdTokenCache"}, {130, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCacheForApplication"}, {136, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCache"}, // 19.0.0+ {150, nullptr, "CreateAuthorizationRequest"}, {160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"}, {170, nullptr, "LoadNetworkServiceLicenseKindAsync"}, }; // clang-format on RegisterHandlers(functions); } private: void CheckAvailability(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void GetAccountId(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); rb.PushRaw(profile_manager->GetLastOpenedUser().Hash()); } void EnsureIdTokenCacheAsync(HLERequestContext& ctx) { LOG_WARNING(Service_ACC, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(ctx, ensure_token_id); } void LoadIdTokenCacheDeprecated(HLERequestContext& ctx) { LOG_WARNING(Service_ACC, "(STUBBED) called"); ensure_token_id->LoadIdTokenCache(ctx); } void LoadIdTokenCache(HLERequestContext& ctx) { LOG_WARNING(Service_ACC, "(STUBBED) called"); std::vector token_data(0x100); std::fill(token_data.begin(), token_data.end(), u8(0)); ctx.WriteBuffer(token_data); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(static_cast(token_data.size())); } void GetNintendoAccountUserResourceCacheForApplication(HLERequestContext& ctx) { LOG_WARNING(Service_ACC, "(STUBBED) called"); std::vector nas_user_base_for_application(0x68); ctx.WriteBuffer(nas_user_base_for_application); if (ctx.CanWriteBuffer(1)) { std::vector unknown_out_buffer(ctx.GetWriteBufferSize(1)); ctx.WriteBuffer(unknown_out_buffer, 1); } IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); rb.PushRaw(profile_manager->GetLastOpenedUser().Hash()); } void StoreOpenContext(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); profile_manager->StoreOpenedUsers(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } std::shared_ptr ensure_token_id{}; std::shared_ptr profile_manager; }; // 6.0.0+ class IAsyncNetworkServiceLicenseKindContext final : public ServiceFramework { public: explicit IAsyncNetworkServiceLicenseKindContext(Core::System& system_, Common::UUID) : ServiceFramework{system_, "IAsyncNetworkServiceLicenseKindContext"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetSystemEvent"}, {1, nullptr, "Cancel"}, {2, nullptr, "HasDone"}, {3, nullptr, "GetResult"}, {4, nullptr, "GetNetworkServiceLicenseKind"}, }; // clang-format on RegisterHandlers(functions); } }; // 8.0.0+ class IOAuthProcedureForUserRegistration final : public ServiceFramework { public: explicit IOAuthProcedureForUserRegistration(Core::System& system_, Common::UUID) : ServiceFramework{system_, "IOAuthProcedureForUserRegistration"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "PrepareAsync"}, {1, nullptr, "GetRequest"}, {2, nullptr, "ApplyResponse"}, {3, nullptr, "ApplyResponseAsync"}, {10, nullptr, "Suspend"}, {100, nullptr, "GetAccountId"}, {101, nullptr, "GetLinkedNintendoAccountId"}, {102, nullptr, "GetNickname"}, {103, nullptr, "GetProfileImage"}, {104, nullptr, "GetProfileLargeImage"}, // 18.0.0+ {110, nullptr, "RegisterUserAsync"}, {111, nullptr, "GetUid"}, {200, nullptr, "ApplyResponseForUserCreationAsync"}, // 17.0.0+ {205, nullptr, "SuspendAfterApplyResponse"}, // 17.0.0+ {210, nullptr, "IsProfileAvailable"}, // 17.0.0+ {220, nullptr, "RegisterUserAsyncWithoutProfile"}, // 17.0.0+ {221, nullptr, "RegisterUserWithProfileAsync"}, // 17.0.0+ {230, nullptr, "RegisterUserWithLargeImageProfileAsync"} // 18.0.0+ }; // clang-format on RegisterHandlers(functions); } }; class DAUTH_O final : public ServiceFramework { public: explicit DAUTH_O(Core::System& system_, Common::UUID) : ServiceFramework{system_, "dauth:o"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "EnsureAuthenticationTokenCacheAsync"}, {1, nullptr, "LoadAuthenticationTokenCache"}, {2, nullptr, "InvalidateAuthenticationTokenCache"}, {3, nullptr, "IsDeviceAuthenticationTokenCacheAvailable"}, {10, nullptr, "EnsureEdgeTokenCacheAsync"}, {11, nullptr, "LoadEdgeTokenCache"}, {12, nullptr, "InvalidateEdgeTokenCache"}, {13, nullptr, "IsEdgeTokenCacheAvailable"}, {20, nullptr, "EnsureApplicationAuthenticationCacheAsync"}, {21, nullptr, "LoadApplicationAuthenticationTokenCache"}, {22, nullptr, "LoadApplicationNetworkServiceClientConfigCache"}, {23, nullptr, "IsApplicationAuthenticationCacheAvailable"}, {24, nullptr, "InvalidateApplicationAuthenticationCache"}, }; // clang-format on RegisterHandlers(functions); } }; // 6.0.0+ class IAsyncResult final : public ServiceFramework { public: explicit IAsyncResult(Core::System& system_, Common::UUID) : ServiceFramework{system_, "IAsyncResult"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetResult"}, {1, nullptr, "Cancel"}, {2, nullptr, "IsAvailable"}, {3, nullptr, "GetSystemEvent"}, }; // clang-format on RegisterHandlers(functions); } }; void Module::Interface::GetUserCount(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(static_cast(profile_manager->GetUserCount())); } void Module::Interface::GetUserExistence(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; Common::UUID user_id = rp.PopRaw(); LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString()); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(profile_manager->UserExists(user_id)); } void Module::Interface::ListAllUsers(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); ctx.WriteBuffer(profile_manager->GetAllUsers()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Module::Interface::ListOpenUsers(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); ctx.WriteBuffer(profile_manager->GetOpenUsers()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Module::Interface::GetLastOpenedUser(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 6}; rb.Push(ResultSuccess); rb.PushRaw(profile_manager->GetLastOpenedUser()); } void Module::Interface::GetProfile(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; Common::UUID user_id = rp.PopRaw(); LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString()); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(ctx, system, user_id, *profile_manager); } void Module::Interface::IsUserRegistrationRequestPermitted(HLERequestContext& ctx) { LOG_WARNING(Service_ACC, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(profile_manager->CanSystemRegisterUser()); } void Module::Interface::InitializeApplicationInfo(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(InitializeApplicationInfoBase()); } void Module::Interface::InitializeApplicationInfoRestricted(HLERequestContext& ctx) { LOG_WARNING(Service_ACC, "(Partial implementation) called"); // TODO(ogniK): We require checking if the user actually owns the title and what not. As of // currently, we assume the user owns the title. InitializeApplicationInfoBase SHOULD be called // first then we do extra checks if the game is a digital copy. IPC::ResponseBuilder rb{ctx, 2}; rb.Push(InitializeApplicationInfoBase()); } Result Module::Interface::InitializeApplicationInfoBase() { if (application_info) { LOG_ERROR(Service_ACC, "Application already initialized"); return Account::ResultApplicationInfoAlreadyInitialized; } // TODO(ogniK): This should be changed to reflect the target process for when we have multiple // processes emulated. As we don't actually have pid support we should assume we're just using // our own process Glue::ApplicationLaunchProperty launch_property{}; const auto result = system.GetARPManager().GetLaunchProperty( &launch_property, system.GetApplicationProcessProgramID()); if (result != ResultSuccess) { LOG_ERROR(Service_ACC, "Failed to get launch property"); return Account::ResultInvalidApplication; } switch (launch_property.base_game_storage_id) { case FileSys::StorageId::GameCard: application_info.application_type = ApplicationType::GameCard; break; case FileSys::StorageId::Host: case FileSys::StorageId::NandSystem: case FileSys::StorageId::NandUser: case FileSys::StorageId::SdCard: case FileSys::StorageId::None: // Yuzu specific, differs from hardware application_info.application_type = ApplicationType::Digital; break; default: LOG_ERROR(Service_ACC, "Invalid game storage ID! storage_id={}", launch_property.base_game_storage_id); return Account::ResultInvalidApplication; } LOG_WARNING(Service_ACC, "ApplicationInfo init required"); // TODO(ogniK): Actual initialization here return ResultSuccess; } void Module::Interface::GetBaasAccountManagerForApplication(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(ctx, system, profile_manager); } void Module::Interface::IsUserAccountSwitchLocked(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); FileSys::NACP nacp; const auto res = system.GetAppLoader().ReadControlData(nacp); bool is_locked = false; if (res != Loader::ResultStatus::Success) { const FileSys::PatchManager pm{system.GetApplicationProcessProgramID(), system.GetFileSystemController(), system.GetContentProvider()}; const auto nacp_unique = pm.GetControlMetadata().first; if (nacp_unique != nullptr) { is_locked = nacp_unique->GetUserAccountSwitchLock(); } else { LOG_ERROR(Service_ACC, "nacp_unique is null!"); } } else { is_locked = nacp.GetUserAccountSwitchLock(); } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(is_locked); } void Module::Interface::InitializeApplicationInfoV2(HLERequestContext& ctx) { LOG_WARNING(Service_ACC, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Module::Interface::BeginUserRegistration(HLERequestContext& ctx) { const auto user_id = Common::UUID::MakeRandom(); profile_manager->CreateNewUser(user_id, "Eden"); LOG_INFO(Service_ACC, "called, uuid={}", user_id.FormattedString()); IPC::ResponseBuilder rb{ctx, 6}; rb.Push(ResultSuccess); rb.PushRaw(user_id); } void Module::Interface::CompleteUserRegistration(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; Common::UUID user_id = rp.PopRaw(); LOG_INFO(Service_ACC, "called, uuid={}", user_id.FormattedString()); profile_manager->WriteUserSaveFile(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Module::Interface::DeleteUser(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; Common::UUID user_id = rp.PopRaw(); LOG_INFO(Service_ACC, "called, uuid={}", user_id.FormattedString()); if (!profile_manager->RemoveUser(user_id)) { LOG_ERROR(Service_ACC, "Failed to delete user with uuid={}", user_id.RawString()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(1U); return; } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Module::Interface::SetUserPosition(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; u64 position = rp.Pop(); Common::UUID user_id = rp.PopRaw(); LOG_DEBUG(Service_ACC, "called, position={} user_id={}", position, user_id.FormattedString()); profile_manager->SetUserPosition(position, user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Module::Interface::GetProfileEditor(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; Common::UUID user_id = rp.PopRaw(); LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.RawString()); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(ctx, system, user_id, *profile_manager); } void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) { IPC::RequestParser rp{ctx}; const auto uuid = rp.PopRaw(); LOG_INFO(Service_ACC, "called, uuid=0x{}", uuid.RawString()); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(ctx, system, uuid); } void Module::Interface::ListQualifiedUsers(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); // All users should be qualified. We don't actually have parental control or anything to do with // nintendo online currently. We're just going to assume the user running the game has access to // the game regardless of parental control settings. ctx.WriteBuffer(profile_manager->GetAllUsers()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Module::Interface::ListOpenContextStoredUsers(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); ctx.WriteBuffer(profile_manager->GetStoredOpenedUsers()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Module::Interface::StoreSaveDataThumbnailApplication(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto uuid = rp.PopRaw(); LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.RawString()); // TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable // way of confirming things like the TID, we're going to assume a non zero value for the time // being. constexpr u64 tid{1}; StoreSaveDataThumbnail(ctx, uuid, tid); } void Module::Interface::GetBaasAccountManagerForSystemService(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto uuid = rp.PopRaw(); LOG_INFO(Service_ACC, "called, uuid=0x{}", uuid.RawString()); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(ctx, system, uuid); } void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto uuid = rp.PopRaw(); const auto tid = rp.Pop(); LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.RawString(), tid); StoreSaveDataThumbnail(ctx, uuid, tid); } void Module::Interface::GetPinCodeLength(HLERequestContext& ctx) { LOG_WARNING(Service_ACC, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(0); } void Module::Interface::StoreSaveDataThumbnail(HLERequestContext& ctx, const Common::UUID& uuid, const u64 tid) { IPC::ResponseBuilder rb{ctx, 2}; if (tid == 0) { LOG_ERROR(Service_ACC, "TitleID is not valid!"); rb.Push(Account::ResultInvalidApplication); return; } if (uuid.IsInvalid()) { LOG_ERROR(Service_ACC, "User ID is not valid!"); rb.Push(Account::ResultInvalidUserId); return; } const auto thumbnail_size = ctx.GetReadBufferSize(); if (thumbnail_size != THUMBNAIL_SIZE) { LOG_ERROR(Service_ACC, "Buffer size is empty! size={:X} expecting {:X}", thumbnail_size, THUMBNAIL_SIZE); rb.Push(Account::ResultInvalidArrayLength); return; } // TODO(ogniK): Construct save data thumbnail rb.Push(ResultSuccess); } void Module::Interface::TrySelectUserWithoutInteractionDeprecated(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); // A u8 is passed into this function which we can safely ignore. It's to determine if we have // access to use the network or not by the looks of it IPC::ResponseBuilder rb{ctx, 6}; if (profile_manager->GetUserCount() != 1) { rb.Push(ResultSuccess); rb.PushRaw(Common::InvalidUUID); return; } const auto user_list = profile_manager->GetAllUsers(); if (std::ranges::all_of(user_list, [](const auto& user) { return user.IsInvalid(); })) { rb.Push(ResultUnknown); // TODO(ogniK): Find the correct error code rb.PushRaw(Common::InvalidUUID); return; } // Select the first user we have rb.Push(ResultSuccess); rb.PushRaw(profile_manager->GetUser(0)->uuid); } void Module::Interface::TrySelectUserWithoutInteraction(HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); // A u8 is passed into this function which we can safely ignore. It's to determine if we have // access to use the network or not by the looks of it IPC::ResponseBuilder rb{ctx, 6}; if (profile_manager->GetUserCount() != 1) { rb.Push(ResultSuccess); rb.PushRaw(Common::InvalidUUID); return; } const auto user_list = profile_manager->GetAllUsers(); if (std::ranges::all_of(user_list, [](const auto& user) { return user.IsInvalid(); })) { rb.Push(ResultUnknown); // TODO(ogniK): Find the correct error code rb.PushRaw(Common::InvalidUUID); return; } // Select the first user we have rb.Push(ResultSuccess); rb.PushRaw(profile_manager->GetUser(0)->uuid); } Module::Interface::Interface(std::shared_ptr module_, std::shared_ptr profile_manager_, Core::System& system_, const char* name) : ServiceFramework{system_, name}, module{std::move(module_)}, profile_manager{std::move(profile_manager_)} {} Module::Interface::~Interface() = default; class ACC_AA final : public Module::Interface { public: explicit ACC_AA(std::shared_ptr module_, std::shared_ptr profile_manager_, Core::System& system_) : Interface(std::move(module_), std::move(profile_manager_), system_, "acc:aa") { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "EnsureCacheAsync"}, {1, nullptr, "LoadCache"}, {2, nullptr, "GetDeviceAccountId"}, {50, nullptr, "RegisterNotificationTokenAsync"}, // 1.0.0 - 6.2.0 {51, nullptr, "UnregisterNotificationTokenAsync"}, // 1.0.0 - 6.2.0 }; // clang-format on RegisterHandlers(functions); } ~ACC_AA() override = default; }; class ACC_SU final : public Module::Interface { public: explicit ACC_SU(std::shared_ptr module_, std::shared_ptr profile_manager_, Core::System& system_) : Interface(std::move(module_), std::move(profile_manager_), system_, "acc:su") { // clang-format off static const FunctionInfo functions[] = { {0, &ACC_SU::GetUserCount, "GetUserCount"}, {1, &ACC_SU::GetUserExistence, "GetUserExistence"}, {2, &ACC_SU::ListAllUsers, "ListAllUsers"}, {3, &ACC_SU::ListOpenUsers, "ListOpenUsers"}, {4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"}, {5, &ACC_SU::GetProfile, "GetProfile"}, {6, nullptr, "GetProfileDigest"}, {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, &ACC_SU::TrySelectUserWithoutInteractionDeprecated, "TrySelectUserWithoutInteractionDeprecated"}, {52, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, // 19.0.0+ {60, &ACC_SU::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, {99, nullptr, "DebugActivateOpenContextRetention"}, {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, {102, &ACC_SU::GetBaasAccountManagerForSystemService, "GetBaasAccountManagerForSystemService"}, {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, {104, nullptr, "GetProfileUpdateNotifier"}, {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, {106, nullptr, "GetProfileSyncNotifier"}, {110, &ACC_SU::StoreSaveDataThumbnailSystem, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {112, nullptr, "LoadSaveDataThumbnail"}, {113, nullptr, "GetSaveDataThumbnailExistence"}, {120, nullptr, "ListOpenUsersInApplication"}, {130, nullptr, "ActivateOpenContextRetention"}, {140, &ACC_SU::ListQualifiedUsers, "ListQualifiedUsers"}, {150, nullptr, "AuthenticateApplicationAsync"}, {151, nullptr, "EnsureSignedDeviceIdentifierCacheForNintendoAccountAsync"}, {152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"}, {190, nullptr, "GetUserLastOpenedApplication"}, {191, nullptr, "ActivateOpenContextHolder"}, {200, &ACC_SU::BeginUserRegistration, "BeginUserRegistration"}, {201, &ACC_SU::CompleteUserRegistration, "CompleteUserRegistration"}, {202, nullptr, "CancelUserRegistration"}, {203, &ACC_SU::DeleteUser, "DeleteUser"}, {204, &ACC_SU::SetUserPosition, "SetUserPosition"}, {205, &ACC_SU::GetProfileEditor, "GetProfileEditor"}, {206, nullptr, "CompleteUserRegistrationForcibly"}, {210, nullptr, "CreateFloatingRegistrationRequest"}, {211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"}, {212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"}, {230, nullptr, "AuthenticateServiceAsync"}, {250, &ACC_SU::GetBaasAccountAdministrator, "GetBaasAccountAdministrator"}, {290, nullptr, "ProxyProcedureForGuestLoginWithNintendoAccount"}, {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, {299, nullptr, "SuspendBackgroundDaemon"}, {400, nullptr, "SetPinCode"}, // 18.0.0+ {401, &ACC_SU::GetPinCodeLength, "GetPinCodeLength"}, // 18.0.0+ {402, nullptr, "GetPinCode"}, // 18.0.0+ {403, nullptr, "GetPinCodeParity"}, {404, nullptr, "VerifyPinCode"}, {405, nullptr, "IsPinCodeVerificationForbidden"}, {410, nullptr, "GetPinCodeErrorCount"}, // 18.0.0+ {411, nullptr, "ResetPinCodeErrorCount"}, // 18.0.0+ {412, nullptr, "IncrementPinCodeErrorCount"}, // 18.0.0+ {900, nullptr, "SetUserUnqualifiedForDebug"}, {901, nullptr, "UnsetUserUnqualifiedForDebug"}, {902, nullptr, "ListUsersUnqualifiedForDebug"}, {910, nullptr, "RefreshFirmwareSettingsForDebug"}, {997, nullptr, "DebugInvalidateTokenCacheForUser"}, {998, nullptr, "DebugSetUserStateClose"}, {999, nullptr, "DebugSetUserStateOpen"}, }; // clang-format on RegisterHandlers(functions); } ~ACC_SU() override = default; }; class ACC_U0 final : public Module::Interface { public: ACC_U0(std::shared_ptr module_, std::shared_ptr profile_manager_, Core::System& system_) : Interface(std::move(module_), std::move(profile_manager_), system_, "acc:u0") { // clang-format off static const FunctionInfo functions[] = { {0, &ACC_U0::GetUserCount, "GetUserCount"}, {1, &ACC_U0::GetUserExistence, "GetUserExistence"}, {2, &ACC_U0::ListAllUsers, "ListAllUsers"}, {3, &ACC_U0::ListOpenUsers, "ListOpenUsers"}, {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"}, {5, &ACC_U0::GetProfile, "GetProfile"}, {6, nullptr, "GetProfileDigest"}, // 3.0.0+ {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, &ACC_U0::TrySelectUserWithoutInteractionDeprecated, "TrySelectUserWithoutInteractionDeprecated"}, {52, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, {60, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, {102, nullptr, "AuthenticateApplicationAsync"}, {103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ {110, &ACC_U0::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {120, nullptr, "CreateGuestLoginRequest"}, {130, nullptr, "LoadOpenContext"}, // 5.0.0+ {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, // 6.0.0+ {160, &ACC_U0::InitializeApplicationInfoV2, "InitializeApplicationInfoV2"}, }; // clang-format on RegisterHandlers(functions); } ~ACC_U0() override = default; }; class ACC_U1 final : public Module::Interface { public: ACC_U1(std::shared_ptr module_, std::shared_ptr profile_manager_, Core::System& system_) : Interface(std::move(module_), std::move(profile_manager_), system_, "acc:u1") { // clang-format off static const FunctionInfo functions[] = { {0, &ACC_U1::GetUserCount, "GetUserCount"}, {1, &ACC_U1::GetUserExistence, "GetUserExistence"}, {2, &ACC_U1::ListAllUsers, "ListAllUsers"}, {3, &ACC_U1::ListOpenUsers, "ListOpenUsers"}, {4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"}, {5, &ACC_U1::GetProfile, "GetProfile"}, {6, nullptr, "GetProfileDigest"}, {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, {60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, {99, nullptr, "DebugActivateOpenContextRetention"}, {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, {102, &ACC_U1::GetBaasAccountManagerForSystemService, "GetBaasAccountManagerForSystemService"}, {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, {104, nullptr, "GetProfileUpdateNotifier"}, {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, {106, nullptr, "GetProfileSyncNotifier"}, {110, &ACC_U1::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {112, nullptr, "LoadSaveDataThumbnail"}, {113, nullptr, "GetSaveDataThumbnailExistence"}, {120, nullptr, "ListOpenUsersInApplication"}, {130, nullptr, "ActivateOpenContextRetention"}, {140, &ACC_U1::ListQualifiedUsers, "ListQualifiedUsers"}, {150, nullptr, "AuthenticateApplicationAsync"}, {151, nullptr, "EnsureSignedDeviceIdentifierCacheForNintendoAccountAsync"}, {152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"}, {190, nullptr, "GetUserLastOpenedApplication"}, {191, nullptr, "ActivateOpenContextHolder"}, {401, &ACC_U1::GetPinCodeLength, "GetPinCodeLength"}, // 18.0.0+ {402, nullptr, "GetPinCode"}, // 18.0.0+ {997, nullptr, "DebugInvalidateTokenCacheForUser"}, {998, nullptr, "DebugSetUserStateClose"}, {999, nullptr, "DebugSetUserStateOpen"}, }; // clang-format on RegisterHandlers(functions); } ~ACC_U1() override = default; }; class DAUTH_0 final : public ServiceFramework { public: explicit DAUTH_0(Core::System& system_) : ServiceFramework{system_, "dauth:0"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "EnsureAuthenticationTokenCacheAsync"}, {1, nullptr, "LoadAuthenticationTokenCache"}, {2, nullptr, "InvalidateAuthenticationTokenCache"}, {3, nullptr, "IsDeviceAuthenticationTokenCacheAvailable"}, {10, nullptr, "EnsureEdgeTokenCacheAsync"}, {11, nullptr, "LoadEdgeTokenCache"}, {12, nullptr, "InvalidateEdgeTokenCache"}, {13, nullptr, "IsEdgeTokenCacheAvailable"}, {20, nullptr, "EnsureApplicationAuthenticationCacheAsync"}, {21, nullptr, "LoadApplicationAuthenticationTokenCache"}, {22, nullptr, "LoadApplicationNetworkServiceClientConfigCache"}, {23, nullptr, "IsApplicationAuthenticationCacheAvailable"}, {24, nullptr, "InvalidateApplicationAuthenticationCache"}, {30, nullptr, "EnsureGameCardAuthenticationCacheAsync"}, {31, nullptr, "LoadGameCardAuthenticationTokenCache"}, {32, nullptr, "IsGameCardAuthenticationCacheAvailable"}, {33, nullptr, "InvalidateGameCardAuthenticationCache"}, {1000, nullptr, "GetInactiveElicenseUsedEvent"}, {9000, nullptr, "ImportVirtualClientCertificate"}, {9010, nullptr, "DeleteVirtualClientCertificate"}, }; // clang-format on RegisterHandlers(functions); } }; class ACC_E final : public Module::Interface { public: explicit ACC_E(std::shared_ptr module_, std::shared_ptr profile_manager_, Core::System& system_) : Interface(std::move(module_), std::move(profile_manager_), system_, "acc:e") { // clang-format off static const FunctionInfo functions[] = { { 0, nullptr, "GetUserCount"}, { 1, nullptr, "GetUserExistence"}, { 2, nullptr, "ListAllUsers"}, { 3, nullptr, "ListOpenUsers"}, { 4, nullptr, "GetLastOpenedUser"}, { 5, nullptr, "GetProfile"}, { 6, nullptr, "GetProfileDigest"}, { 50, nullptr, "IsUserRegistrationRequestPermitted"}, { 51, nullptr, "TrySelectUserWithoutInteractionDeprecated"}, { 52, nullptr, "TrySelectUserWithoutInteraction"}, { 99, nullptr, "DebugActivateOpenContextRetention"}, { 100, nullptr, "GetUserRegistrationNotifier"}, { 101, nullptr, "GetUserStateChangeNotifier"}, { 102, nullptr, "GetBaasAccountManagerForSystemService"}, { 103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, { 104, nullptr, "GetProfileUpdateNotifier"}, { 105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, { 106, nullptr, "GetProfileSyncNotifier"}, { 110, nullptr, "StoreSaveDataThumbnail"}, { 111, nullptr, "ClearSaveDataThumbnail"}, { 112, nullptr, "LoadSaveDataThumbnail"}, { 113, nullptr, "GetSaveDataThumbnailExistence"}, { 120, nullptr, "ListOpenUsersInApplication"}, { 130, nullptr, "ActivateOpenContextRetention"}, { 140, nullptr, "ListQualifiedUsers"}, { 151, nullptr, "EnsureSignedDeviceIdentifierCacheForNintendoAccountAsync"}, { 152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"}, { 170, nullptr, "GetNasOp2MembershipStateChangeNotifier"}, { 191, nullptr, "UpdateNotificationReceiverInfo"}, { 200, nullptr, "BeginUserRegistration"}, { 201, nullptr, "CompleteUserRegistration"}, { 202, nullptr, "CancelUserRegistration"}, { 203, nullptr, "DeleteUser"}, { 204, nullptr, "SetUserPosition"}, { 205, nullptr, "GetProfileEditor"}, { 206, nullptr, "CompleteUserRegistrationForcibly"}, { 210, nullptr, "CreateFloatingRegistrationRequest"}, { 211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"}, { 212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"}, { 213, nullptr, "CreateProcedureToCreateUserWithNintendoAccount"}, { 214, nullptr, "ResumeProcedureToCreateUserWithNintendoAccount"}, { 215, nullptr, "ResumeProcedureToCreateUserWithNintendoAccountAfterApplyResponse"}, { 230, nullptr, "AuthenticateServiceAsync"}, { 250, nullptr, "GetBaasAccountAdministrator"}, { 251, nullptr, "SynchronizeNetworkServiceAccountsSnapshotAsync"}, { 290, nullptr, "ProxyProcedureForGuestLoginWithNintendoAccount"}, { 291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, { 292, nullptr, "ProxyProcedureForDeviceMigrationAuthenticatingOperatingUser"}, { 293, nullptr, "ProxyProcedureForDeviceMigrationDownload"}, { 299, nullptr, "SuspendBackgroundDaemon"}, { 350, nullptr, "CreateDeviceMigrationUserExportRequest"}, { 351, nullptr, "UploadNasCredential"}, { 352, nullptr, "CreateDeviceMigrationUserImportRequest"}, { 353, nullptr, "DeleteUserMigrationSaveData"}, { 400, nullptr, "SetPinCode"}, { 401, nullptr, "GetPinCodeLength"}, { 402, nullptr, "GetPinCode"}, { 403, nullptr, "GetPinCodeParity"}, { 404, nullptr, "VerifyPinCode"}, { 405, nullptr, "IsPinCodeVerificationForbidden"}, { 410, nullptr, "GetPinCodeErrorCount"}, { 411, nullptr, "ResetPinCodeErrorCount"}, { 412, nullptr, "IncrementPinCodeErrorCount"}, { 413, nullptr, "SetPinCodeErrorCount"}, { 420, nullptr, "SetStartPenaltyTime"}, { 421, nullptr, "GetStartPenaltyTime"}, { 900, nullptr, "SetUserUnqualifiedForDebug"}, { 901, nullptr, "UnsetUserUnqualifiedForDebug"}, { 902, nullptr, "ListUsersUnqualifiedForDebug"}, { 910, nullptr, "RefreshFirmwareSettingsForDebug"}, { 997, nullptr, "DebugInvalidateTokenCacheForUser"}, { 998, nullptr, "DebugSetUserStateClose"}, { 999, nullptr, "DebugSetUserStateOpen"}, { 1000, nullptr, "CreateIAccountEntityServiceForApplication"}, { 1100, nullptr, "CreateIUserStateManager"}, { 10050, nullptr, "IsUserRegistrationRequestPermittedForAccountPolicy"}, { 10105, nullptr, "CheckNetworkServiceAvailabilityAsyncForAccountPolicy"}, }; // clang-format on RegisterHandlers(functions); } }; class ACC_E_U1 final : public Module::Interface { public: explicit ACC_E_U1(std::shared_ptr module_, std::shared_ptr profile_manager_, Core::System& system_) : Interface(std::move(module_), std::move(profile_manager_), system_, "acc:e:u1") { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetUserCount"}, {1, nullptr, "GetUserExistence"}, {2, nullptr, "ListAllUsers"}, {3, nullptr, "ListOpenUsers"}, {4, nullptr, "GetLastOpenedUser"}, {5, nullptr, "GetProfile"}, {6, nullptr, "GetProfileDigest"}, {50, nullptr, "IsUserRegistrationRequestPermitted"}, {51, nullptr, "TrySelectUserWithoutInteractionDeprecated"}, {99, nullptr, "DebugActivateOpenContextRetention"}, {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, {102, nullptr, "GetBaasAccountManagerForSystemService"}, {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, {104, nullptr, "GetProfileUpdateNotifier"}, {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, {106, nullptr, "GetProfileSyncNotifier"}, {110, nullptr, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {112, nullptr, "LoadSaveDataThumbnail"}, {113, nullptr, "GetSaveDataThumbnailExistence"}, {120, nullptr, "ListOpenUsersInApplication"}, {130, nullptr, "ActivateOpenContextRetention"}, {140, nullptr, "ListQualifiedUsers"}, {151, nullptr, "EnsureSignedDeviceIdentifierCacheForNintendoAccountAsync"}, {152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"}, {170, nullptr, "GetNasOp2MembershipStateChangeNotifier"}, {191, nullptr, "UpdateNotificationReceiverInfo"}, {997, nullptr, "DebugInvalidateTokenCacheForUser"}, {998, nullptr, "DebugSetUserStateClose"}, {999, nullptr, "DebugSetUserStateOpen"}, }; // clang-format on RegisterHandlers(functions); } }; class ACC_E_U2 final : public Module::Interface { public: explicit ACC_E_U2(std::shared_ptr module_, std::shared_ptr profile_manager_, Core::System& system_) : Interface(std::move(module_), std::move(profile_manager_), system_, "acc:e:u2") { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetUserCount"}, {1, nullptr, "GetUserExistence"}, {2, nullptr, "ListAllUsers"}, {3, nullptr, "ListOpenUsers"}, {4, nullptr, "GetLastOpenedUser"}, {5, nullptr, "GetProfile"}, {6, nullptr, "GetProfileDigest"}, {50, nullptr, "#IsUserRegistrationRequestPermitted"}, {51, nullptr, "TrySelectUserWithoutInteractionDeprecated"}, {52, nullptr, "TrySelectUserWithoutInteraction"}, {99, nullptr, "DebugActivateOpenContextRetention"}, {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, {102, nullptr, "GetBaasAccountManagerForSystemService"}, {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, {104, nullptr, "GetProfileUpdateNotifier"}, {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, {106, nullptr, "GetProfileSyncNotifier"}, {110, nullptr, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {112, nullptr, "LoadSaveDataThumbnail"}, {113, nullptr, "GetSaveDataThumbnailExistence"}, {120, nullptr, "ListOpenUsersInApplication"}, {130, nullptr, "ActivateOpenContextRetention"}, {140, nullptr, "ListQualifiedUsers"}, {151, nullptr, "EnsureSignedDeviceIdentifierCacheForNintendoAccountAsync"}, {152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"}, {170, nullptr, "GetNasOp2MembershipStateChangeNotifier"}, {191, nullptr, "UpdateNotificationReceiverInfo"}, {205, nullptr, "GetProfileEditor"}, {401, nullptr, "GetPinCodeLength"}, {402, nullptr, "GetPinCode"}, {403, nullptr, "GetPinCodeParity"}, {404, nullptr, "VerifyPinCode"}, {405, nullptr, "IsPinCodeVerificationForbidden"}, {997, nullptr, "DebugInvalidateTokenCacheForUser"}, {998, nullptr, "DebugSetUserStateClose"}, {999, nullptr, "DebugSetUserStateOpen"}, }; // clang-format on RegisterHandlers(functions); } }; void LoopProcess(Core::System& system) { auto module = std::make_shared(); auto profile_manager = std::make_shared(); auto server_manager = std::make_unique(system); server_manager->RegisterNamedService("acc:aa", std::make_shared(module, profile_manager, system)); server_manager->RegisterNamedService("acc:su", std::make_shared(module, profile_manager, system)); server_manager->RegisterNamedService("acc:u0", std::make_shared(module, profile_manager, system)); server_manager->RegisterNamedService("acc:u1", std::make_shared(module, profile_manager, system)); server_manager->RegisterNamedService("acc:e", std::make_shared(module, profile_manager, system)); server_manager->RegisterNamedService("acc:e:u1", std::make_shared(module, profile_manager, system)); server_manager->RegisterNamedService("acc:e:u2", std::make_shared(module, profile_manager, system)); server_manager->RegisterNamedService("dauth:0", std::make_shared(system)); ServerManager::RunServer(std::move(server_manager)); } } // namespace Service::Account