diff --git a/nx/include/switch/runtime/env.h b/nx/include/switch/runtime/env.h index 6f207ef6..2576739e 100644 --- a/nx/include/switch/runtime/env.h +++ b/nx/include/switch/runtime/env.h @@ -6,6 +6,7 @@ */ #pragma once #include "../types.h" +#include "../services/acc.h" /// Structure representing an entry in the homebrew environment configuration. typedef struct { @@ -114,4 +115,4 @@ bool envHasRandomSeed(void); void envGetRandomSeed(u64 out[2]); /// Returns a pointer to the user id storage area (if present). -u128* envGetUserIdStorage(void); +AccountUid* envGetUserIdStorage(void); diff --git a/nx/include/switch/services/acc.h b/nx/include/switch/services/acc.h index 560b5b7f..a24d62d7 100644 --- a/nx/include/switch/services/acc.h +++ b/nx/include/switch/services/acc.h @@ -6,37 +6,60 @@ */ #pragma once #include "../types.h" -#include "sm.h" +#include "../sf/service.h" #define ACC_USER_LIST_SIZE 8 +typedef enum { + AccountServiceType_NotInitialized = 0, ///< Same as ::AccountServiceType_Application during \ref accountInitialize. + AccountServiceType_Application = 1, ///< Initializes acc:u0. + AccountServiceType_System = 2, ///< Initializes acc:u1. + AccountServiceType_Administrator = 3, ///< Initializes acc:su. +} AccountServiceType; + /// Profile typedef struct { - Service s; + Service s; ///< IProfile } AccountProfile; -typedef struct -{ - u32 unk_x0; +/// Account UserId. +typedef struct { + u64 uid[2]; ///< UserId. All-zero is invalid / Uid not set. See also \ref accountUidIsValid. +} AccountUid; + +/// UserData +typedef struct { + u32 unk_x0; ///< Unknown. u32 iconID; ///< Icon ID. 0 = Mii, the rest are character icon IDs. u8 iconBackgroundColorID; ///< Profile icon background color ID - u8 unk_x9[0x7]; + u8 unk_x9[0x7]; ///< Unknown. u8 miiID[0x10]; ///< Some ID related to the Mii? All zeros when a character icon is used. u8 unk_x20[0x60]; ///< Usually zeros? -} PACKED AccountUserData; +} AccountUserData; -typedef struct -{ - u128 userID; +/// ProfileBase +typedef struct { + AccountUid userID; ///< \ref AccountUid u64 lastEditTimestamp; ///< POSIX UTC timestamp, for the last account edit. - char username[0x20]; ///< UTF-8 Username. -} PACKED AccountProfileBase; + char nickname[0x20]; ///< UTF-8 Nickname. +} AccountProfileBase; +/** + * @brief Sets the \ref AccountServiceType for initialization. Call this function before \ref accountInitialize, if needed. + * @note By default ::AccountServiceType_NotInitialized will be used. + */ +void accountSetServiceType(AccountServiceType serviceType); + +/// Initialize account. Result accountInitialize(void); + +/// Exit account. void accountExit(void); + +/// Gets the Service object for the actual account service session. Service* accountGetServiceSession(void); -/// Get the total number of user profiles +/// Get the total number of user profiles. Result accountGetUserCount(s32* user_count); /** @@ -45,26 +68,35 @@ Result accountGetUserCount(s32* user_count); * @param max_userIDs Maximum number of user IDs to return. * @param actual_total The actual total number of user IDs found. */ -Result accountListAllUsers(u128* userIDs, size_t max_userIDs, size_t *actual_total); +Result accountListAllUsers(AccountUid* userIDs, s32 max_userIDs, s32 *actual_total); -/// Get the userID for the last opened user. The output userID is only valid when the output account_selected==1. -Result accountGetLastOpenedUser(u128 *userID, bool *account_selected); +/// Get the userID for the last opened user. +Result accountGetLastOpenedUser(AccountUid *userID); /// Get an AccountProfile for the specified userID. -Result accountGetProfile(AccountProfile* out, u128 userID); +Result accountGetProfile(AccountProfile* out, const AccountUid *userID); + +/// Close the AccountProfile. +void accountProfileClose(AccountProfile* profile); /// Get \ref AccountUserData and \ref AccountProfileBase for the specified profile, userdata is optional (can be NULL). Result accountProfileGet(AccountProfile* profile, AccountUserData* userdata, AccountProfileBase* profilebase); /// Get the icon image size. -Result accountProfileGetImageSize(AccountProfile* profile, size_t* image_size); +Result accountProfileGetImageSize(AccountProfile* profile, u32* image_size); /// Load the JPEG profile icon, valid for both Miis and character icons. The output image_size is the same as the one from \ref accountProfileGetImageSize. -Result accountProfileLoadImage(AccountProfile* profile, void* buf, size_t len, size_t* image_size); - -void accountProfileClose(AccountProfile* profile); +Result accountProfileLoadImage(AccountProfile* profile, void* buf, size_t len, u32* image_size); /// Gets the userID which was selected by the profile-selector applet (if any), prior to launching the currently running Application title. /// This gets the cached PreselectedUser loaded during accountInitialize, when PreselectedUser is available. -Result accountGetPreselectedUser(u128 *userID); +Result accountGetPreselectedUser(AccountUid *userID); + +/** + * @brief Checks whether the specified \ref AccountUid is valid/set (non-zero). + * @param[in] Uid \ref AccountUid + */ +NX_CONSTEXPR bool accountUidIsValid(const AccountUid *Uid) { + return Uid->uid[0]!=0 || Uid->uid[1]!=0; +} diff --git a/nx/source/runtime/env.c b/nx/source/runtime/env.c index 36d5209e..740c8158 100644 --- a/nx/source/runtime/env.c +++ b/nx/source/runtime/env.c @@ -5,6 +5,7 @@ #include "services/sm.h" #include "services/fatal.h" #include "services/applet.h" +#include "services/acc.h" void NORETURN __nx_exit(Result rc, LoaderReturnFn retaddr); @@ -23,7 +24,7 @@ static char* g_nextLoadArgv = NULL; static Result g_lastLoadResult = 0; static bool g_hasRandomSeed = false; static u64 g_randomSeed[2] = { 0, 0 }; -static u128* g_userIdStorage = NULL; +static AccountUid* g_userIdStorage = NULL; extern __attribute__((weak)) u32 __nx_applet_type; @@ -104,7 +105,7 @@ void envSetup(void* ctx, Handle main_thread, LoaderReturnFn saved_lr) break; case EntryType_UserIdStorage: - g_userIdStorage = (u128*)(uintptr_t)ent->Value[0]; + g_userIdStorage = (AccountUid*)(uintptr_t)ent->Value[0]; break; case EntryType_HosVersion: @@ -222,6 +223,6 @@ void envGetRandomSeed(u64 out[2]) { out[1] = g_randomSeed[1]; } -u128* envGetUserIdStorage(void) { +AccountUid* envGetUserIdStorage(void) { return g_userIdStorage; } diff --git a/nx/source/services/acc.c b/nx/source/services/acc.c index 7c0c8d6d..03742724 100644 --- a/nx/source/services/acc.c +++ b/nx/source/services/acc.c @@ -1,37 +1,44 @@ +#define NX_SERVICE_ASSUME_NON_DOMAIN #include - -#include "types.h" -#include "arm/atomics.h" +#include "service_guard.h" #include "services/acc.h" -#include "services/sm.h" #include "services/applet.h" #include "runtime/env.h" #include "runtime/hosversion.h" +static AccountServiceType g_accServiceType = AccountServiceType_NotInitialized; static Service g_accSrv; -static u64 g_refCnt; -static u128 g_accPreselectedUserID; +static AccountUid g_accPreselectedUserID; static bool g_accPreselectedUserInitialized; static Result _accountInitializeApplicationInfo(void); -static Result _accountGetPreselectedUser(u128 *userID); +static Result _accountGetPreselectedUser(AccountUid *userID); -Result accountInitialize(void) -{ +NX_GENERATE_SERVICE_GUARD(account); + +void accountSetServiceType(AccountServiceType serviceType) { + g_accServiceType = serviceType; +} + +Result _accountInitialize(void) { Result rc=0; Result rc2=0; - u128 *userIdEnv = envGetUserIdStorage(); + AccountUid *userIdEnv = envGetUserIdStorage(); - atomicIncrement64(&g_refCnt); - - if (serviceIsActive(&g_accSrv)) - return 0; - - rc = smGetService(&g_accSrv, "acc:u1"); - if (R_FAILED(rc)) { - rc = smGetService(&g_accSrv, "acc:u0"); - if (R_SUCCEEDED(rc)) rc = _accountInitializeApplicationInfo(); + switch (g_accServiceType) { + case AccountServiceType_NotInitialized: + case AccountServiceType_Application: + g_accServiceType = AccountServiceType_Application; + rc = smGetService(&g_accSrv, "acc:u0"); + if (R_SUCCEEDED(rc)) rc = _accountInitializeApplicationInfo(); + break; + case AccountServiceType_System: + rc = smGetService(&g_accSrv, "acc:u1"); + break; + case AccountServiceType_Administrator: + rc = smGetService(&g_accSrv, "acc:su"); + break; } if (R_SUCCEEDED(rc)) { @@ -42,354 +49,128 @@ Result accountInitialize(void) } else if (userIdEnv) { g_accPreselectedUserID = *userIdEnv; - if (g_accPreselectedUserID) g_accPreselectedUserInitialized = true; + if (accountUidIsValid(&g_accPreselectedUserID)) g_accPreselectedUserInitialized = true; } } - if (R_FAILED(rc)) accountExit(); - return rc; } -void accountExit(void) -{ - if (atomicDecrement64(&g_refCnt) == 0) - serviceClose(&g_accSrv); +void _accountCleanup(void) { + serviceClose(&g_accSrv); + g_accServiceType = AccountServiceType_NotInitialized; } Service* accountGetServiceSession(void) { return &g_accSrv; } +static Result _accountCmdNoInOutU32(Service* srv, u32 *out, u32 cmd_id) { + return serviceDispatchOut(srv, cmd_id, *out); +} + static Result _accountInitializeApplicationInfo(void) { - IpcCommand c; - ipcInitialize(&c); - - struct { - u64 magic; - u64 cmd_id; - u64 pid_placeholder; - } *raw; - - ipcSendPid(&c); - - raw = serviceIpcPrepareHeader(&g_accSrv, &c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = hosversionBefore(6,0,0) ? 100 : 140; - raw->pid_placeholder = 0; - - Result rc = serviceIpcDispatch(&g_accSrv); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - struct { - u64 magic; - u64 result; - } *resp; - - serviceIpcParse(&g_accSrv, &r, sizeof(*resp)); - resp = r.Raw; - - rc = resp->result; - } - - return rc; + u64 pid_placeholder=0; + return serviceDispatchIn(&g_accSrv, hosversionBefore(6,0,0) ? 100 : 140, pid_placeholder, + .in_send_pid = true, + ); } -Result accountGetUserCount(s32* user_count) -{ - IpcCommand c; - ipcInitialize(&c); - - struct { - u64 magic; - u64 cmd_id; - } *raw; - - raw = ipcPrepareHeader(&c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 0; - - Result rc = serviceIpcDispatch(&g_accSrv); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - ipcParse(&r); - - struct { - u64 magic; - u64 result; - u32 user_count; - } *resp = r.Raw; - - rc = resp->result; - - if (R_SUCCEEDED(rc)) { - *user_count = resp->user_count; - } - } - - return rc; +Result accountGetUserCount(s32* user_count) { + return _accountCmdNoInOutU32(&g_accSrv, (u32*)user_count, 0); } -static Result _accountListAllUsers(u128* userIDs) -{ - IpcCommand c; - ipcInitialize(&c); +static Result _accountListAllUsers(AccountUid* userIDs) { + return serviceDispatch(&g_accSrv, 2, + .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_Out }, + .buffers = { { userIDs, sizeof(AccountUid)*ACC_USER_LIST_SIZE } }, + ); +} +Result accountListAllUsers(AccountUid* userIDs, s32 max_userIDs, s32 *actual_total) { Result rc=0; - - size_t bufsize = ACC_USER_LIST_SIZE * sizeof(u128); - - ipcAddRecvStatic(&c, userIDs, bufsize, 0); - - struct { - u64 magic; - u64 cmd_id; - } *raw; - - raw = ipcPrepareHeader(&c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 2; - - rc = serviceIpcDispatch(&g_accSrv); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - ipcParse(&r); - - struct { - u64 magic; - u64 result; - } *resp = r.Raw; - - rc = resp->result; - } - - return rc; -} - -Result accountListAllUsers(u128* userIDs, size_t max_userIDs, size_t *actual_total) -{ - Result rc=0; - u128 temp_userIDs[ACC_USER_LIST_SIZE]; + AccountUid temp_userIDs[ACC_USER_LIST_SIZE]; memset(temp_userIDs, 0, sizeof(temp_userIDs)); rc = _accountListAllUsers(temp_userIDs); if (R_SUCCEEDED(rc)) { - size_t total_userIDs; - for (total_userIDs = 0; total_userIDs < ACC_USER_LIST_SIZE; total_userIDs++) { - if (!temp_userIDs[total_userIDs]) - break; + s32 total_userIDs; + for (total_userIDs=0; total_userIDs total_userIDs) { max_userIDs = total_userIDs; } - memcpy(userIDs, temp_userIDs, sizeof(u128) * max_userIDs); + memcpy(userIDs, temp_userIDs, sizeof(AccountUid)*max_userIDs); *actual_total = max_userIDs; } return rc; } -Result accountGetLastOpenedUser(u128 *userID, bool *account_selected) -{ - IpcCommand c; - ipcInitialize(&c); - - struct { - u64 magic; - u64 cmd_id; - } *raw; - - raw = ipcPrepareHeader(&c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 4; - - Result rc = serviceIpcDispatch(&g_accSrv); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - ipcParse(&r); - - struct { - u64 magic; - u64 result; - u128 userID; - } *resp = r.Raw; - - rc = resp->result; - - if (R_SUCCEEDED(rc) && userID) { - *userID = resp->userID; - if (account_selected) { - *account_selected = 0; - if (*userID != 0) *account_selected = 1; - } - } - } - - return rc; +Result accountGetLastOpenedUser(AccountUid *userID) { + return serviceDispatchOut(&g_accSrv, 4, *userID); } -Result accountGetProfile(AccountProfile* out, u128 userID) { - IpcCommand c; - ipcInitialize(&c); - - struct { - u64 magic; - u64 cmd_id; - u128 userID; - } *raw; - - raw = ipcPrepareHeader(&c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 5; - raw->userID = userID; - - Result rc = serviceIpcDispatch(&g_accSrv); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - ipcParse(&r); - - struct { - u64 magic; - u64 result; - } *resp = r.Raw; - - rc = resp->result; - - if (R_SUCCEEDED(rc)) { - serviceCreate(&out->s, r.Handles[0]); - } - } - - return rc; +Result accountGetProfile(AccountProfile* out, const AccountUid *userID) { + return serviceDispatchIn(&g_accSrv, 5, *userID, + .out_num_objects = 1, + .out_objects = &out->s, + ); } -//IProfile implementation -Result accountProfileGet(AccountProfile* profile, AccountUserData* userdata, AccountProfileBase* profilebase) { - IpcCommand c; - ipcInitialize(&c); - if (userdata) ipcAddRecvStatic(&c, userdata, sizeof(AccountUserData), 0); - - struct { - u64 magic; - u64 cmd_id; - } *raw; - - raw = ipcPrepareHeader(&c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = userdata==NULL ? 1 : 0; - - Result rc = serviceIpcDispatch(&profile->s); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - ipcParse(&r); - - struct { - u64 magic; - u64 result; - AccountProfileBase profilebase; - } *resp = r.Raw; - - rc = resp->result; - - if (R_SUCCEEDED(rc) && profilebase) memcpy(profilebase, &resp->profilebase, sizeof(AccountProfileBase)); - } - - return rc; -} - -Result accountProfileGetImageSize(AccountProfile* profile, size_t* image_size) { - IpcCommand c; - ipcInitialize(&c); - - struct { - u64 magic; - u64 cmd_id; - } *raw; - - raw = ipcPrepareHeader(&c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 10; - - Result rc = serviceIpcDispatch(&profile->s); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - ipcParse(&r); - - struct { - u64 magic; - u64 result; - u32 image_size; - } *resp = r.Raw; - - rc = resp->result; - - if (R_SUCCEEDED(rc)) { - *image_size = resp->image_size; - } - } - - return rc; -} - -Result accountProfileLoadImage(AccountProfile* profile, void* buf, size_t len, size_t* image_size) { - IpcCommand c; - ipcInitialize(&c); - ipcAddRecvBuffer(&c, buf, len, 0); - - struct { - u64 magic; - u64 cmd_id; - } *raw; - - raw = ipcPrepareHeader(&c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 11; - - Result rc = serviceIpcDispatch(&profile->s); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - ipcParse(&r); - - struct { - u64 magic; - u64 result; - u32 image_size; - } *resp = r.Raw; - - rc = resp->result; - - if (R_SUCCEEDED(rc)) { - *image_size = resp->image_size; - } - } - - return rc; -} +// IProfile void accountProfileClose(AccountProfile* profile) { serviceClose(&profile->s); } -static Result _accountGetPreselectedUser(u128 *userID) { +static Result _accountProfileGet(AccountProfile* profile, AccountUserData* userdata, AccountProfileBase* profilebase) { + return serviceDispatchOut(&profile->s, 0, *profilebase, + .buffer_attrs = { SfBufferAttr_FixedSize | SfBufferAttr_HipcPointer | SfBufferAttr_Out }, + .buffers = { { userdata, sizeof(AccountUserData) } }, + ); +} + +static Result _accountProfileGetBase(AccountProfile* profile, AccountProfileBase* profilebase) { + return serviceDispatchOut(&profile->s, 1, *profilebase); +} + +Result accountProfileGet(AccountProfile* profile, AccountUserData* userdata, AccountProfileBase* profilebase) { + Result rc=0; + + if (!serviceIsActive(&profile->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + if (userdata) + rc = _accountProfileGet(profile, userdata, profilebase); + else + rc = _accountProfileGetBase(profile, profilebase); + + return rc; +} + +Result accountProfileGetImageSize(AccountProfile* profile, u32* image_size) { + if (!serviceIsActive(&profile->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return _accountCmdNoInOutU32(&profile->s, image_size, 10); +} + +Result accountProfileLoadImage(AccountProfile* profile, void* buf, size_t len, u32* image_size) { + if (!serviceIsActive(&profile->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return serviceDispatchOut(&profile->s, 11, *image_size, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { buf, len } }, + ); +} + +static Result _accountGetPreselectedUser(AccountUid *userID) { Result rc=0; AppletStorage storage; s64 tmpsize=0; @@ -398,9 +179,9 @@ static Result _accountGetPreselectedUser(u128 *userID) { u32 magicnum;//These two fields must match fixed values. u8 unk_x4; u8 pad[3]; - u128 userID; + AccountUid userID; u8 unk_x18[0x70];//unused - } PACKED storagedata; + } storagedata; memset(&storagedata, 0, sizeof(storagedata)); @@ -424,7 +205,7 @@ static Result _accountGetPreselectedUser(u128 *userID) { return rc; } -Result accountGetPreselectedUser(u128 *userID) { +Result accountGetPreselectedUser(AccountUid *userID) { if (!g_accPreselectedUserInitialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); *userID = g_accPreselectedUserID;