account: Updated for new-ipc. Renamed the username field in AccountProfileBase to nickname. Fixed various param types, and also removed the output bool from accountGetLastOpenedUser. Added struct AccountUid for proper userIDs, which replaces u128-userIDs. Added accountUidIsValid. Added accountSetServiceType()/AccountServiceType. Improved docs and minor improvements.

env: Updated UserIdStorage handling for using AccountUid.
This commit is contained in:
yellows8 2019-10-08 15:35:05 -04:00
parent 040b33c457
commit 95f91c431c
No known key found for this signature in database
GPG Key ID: 0AF90DA3F1E60E43
4 changed files with 166 additions and 351 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -1,37 +1,44 @@
#define NX_SERVICE_ASSUME_NON_DOMAIN
#include <string.h>
#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<ACC_USER_LIST_SIZE; total_userIDs++) {
if (!accountUidIsValid(&temp_userIDs[total_userIDs])) break;
}
if (max_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;