diff --git a/nx/include/switch/services/friends.h b/nx/include/switch/services/friends.h index 36a9a2fd..994b01d7 100644 --- a/nx/include/switch/services/friends.h +++ b/nx/include/switch/services/friends.h @@ -11,6 +11,14 @@ #include "../services/acc.h" #include "../sf/service.h" +typedef enum { + FriendsServiceType_User = 0, ///< Initializes friend:u + FriendsServiceType_Viewer = 1, ///< Initializes friend:v + FriendsServiceType_Manager = 2, ///< Initializes friend:m + FriendsServiceType_System = 3, ///< Initializes friend:s + FriendsServiceType_Administrator = 4, ///< Initializes friend:a +} FriendsServiceType; + /// InAppScreenName typedef struct { char name[0x40]; ///< UTF-8 string, NUL-terminated. @@ -32,6 +40,36 @@ typedef struct { u64 id; ///< Id. } FriendsFriendInvitationGroupId; +/// FriendsUserSetting +typedef struct { + AccountUid uid; ///< User ID + u32 presence_permission; ///< Presence permission + u32 play_log_permission; ///< Play log permission + u64 friend_request_reception; ///< Unknown + char friend_code[0x20]; ///< Friend Code + u64 friend_code_next_issuable_time; ///< Unknown + u8 unk_x48[0x7C8]; ///< Unknown +} FriendsUserSetting; + +/// Initialize friends +Result friendsInitialize(FriendsServiceType service_type); + +/// Exit friends +void friendsExit(void); + +/// Gets the Service object for the friends service session. +Service* friendsGetServiceSession(void); + +/// Gets the Service object for the actual IFriendsService service session. +Service* friendsGetServiceSession_IFriendsService(void); + +/** + * @brief Gets the \ref FriendsUserSetting details + * @param[in] uid \ref User AccountUid. + * @param[out] user_setting \ref FriendsUserSetting + */ +Result friendsGetUserSetting(AccountUid uid, FriendsUserSetting *user_setting); + /** * @brief Gets an Event which is signaled when data is available with \ref friendsTryPopFriendInvitationNotificationInfo. * @note This is a wrapper for \ref appletGetFriendInvitationStorageChannelEvent, see that for the usage requirements. @@ -51,4 +89,3 @@ NX_INLINE Result friendsGetFriendInvitationNotificationEvent(Event *out_event) { * @param[out] out_size Size of the data which was written into the output buffer. Optional, can be NULL. */ Result friendsTryPopFriendInvitationNotificationInfo(AccountUid *uid, void* buffer, u64 size, u64 *out_size); - diff --git a/nx/source/services/friends.c b/nx/source/services/friends.c index 4195f203..7d558c3e 100644 --- a/nx/source/services/friends.c +++ b/nx/source/services/friends.c @@ -1,6 +1,128 @@ #include "service_guard.h" +#include "sf/sessionmgr.h" #include "services/friends.h" +static FriendsServiceType g_friendsServiceType; + +static Service g_friendsSrv; +static Service g_friendsIFriendService; + +static SessionMgr g_friendsSessionMgr; + +NX_GENERATE_SERVICE_GUARD_PARAMS(friends, (FriendsServiceType service_type), (service_type)); + +NX_INLINE bool _friendsObjectIsChild(Service* s) { + return s->session == g_friendsSrv.session; +} + +static void _friendsObjectClose(Service* s) { + if (!_friendsObjectIsChild(s)) { + serviceClose(s); + } else { + int slot = sessionmgrAttachClient(&g_friendsSessionMgr); + uint32_t object_id = serviceGetObjectId(s); + + serviceAssumeDomain(s); + cmifMakeCloseRequest(armGetTls(), object_id); + svcSendSyncRequest(sessionmgrGetClientSession(&g_friendsSessionMgr, slot)); + sessionmgrDetachClient(&g_friendsSessionMgr, slot); + } +} + +NX_INLINE Result _friendsObjectDispatchImpl(Service* s, u32 request_id, + const void * in_data, u32 in_data_size, + void * out_data, u32 out_data_size, + SfDispatchParams disp) +{ + int slot = -1; + if (_friendsObjectIsChild(s)) { + slot = sessionmgrAttachClient(&g_friendsSessionMgr); + if (slot < 0) + __builtin_unreachable(); + disp.target_session = sessionmgrGetClientSession(&g_friendsSessionMgr, slot); + serviceAssumeDomain(s); + } + + Result rc = serviceDispatchImpl(s, request_id, in_data, in_data_size, out_data, out_data_size, disp); + + if (slot >= 0) + sessionmgrDetachClient(&g_friendsSessionMgr, slot); + + return rc; +} + +#define _friendsObjectDispatch(_s,_rid,...) \ + _friendsObjectDispatchImpl((_s),(_rid),NULL,0,NULL,0,(SfDispatchParams){ __VA_ARGS__ }) + +#define _friendsObjectDispatchIn(_s,_rid,_in,...) \ + _friendsObjectDispatchImpl((_s),(_rid),&(_in),sizeof(_in),NULL,0,(SfDispatchParams){ __VA_ARGS__ }) + +#define _friendsObjectDispatchOut(_s,_rid,_out,...) \ + _friendsObjectDispatchImpl((_s),(_rid),NULL,0,&(_out),sizeof(_out),(SfDispatchParams){ __VA_ARGS__ }) + +#define _friendsObjectDispatchInOut(_s,_rid,_in,_out,...) \ + _friendsObjectDispatchImpl((_s),(_rid),&(_in),sizeof(_in),&(_out),sizeof(_out),(SfDispatchParams){ __VA_ARGS__ }) + +Result _friendsInitialize(FriendsServiceType service_type) { + Result rc = MAKERESULT(Module_Libnx, LibnxError_BadInput); + + g_friendsServiceType = service_type; + + switch (g_friendsServiceType) { + case FriendsServiceType_User: + rc = smGetService(&g_friendsSrv, "friend:u"); + break; + case FriendsServiceType_Administrator: + rc = smGetService(&g_friendsSrv, "friend:a"); + break; + case FriendsServiceType_Manager: + rc = smGetService(&g_friendsSrv, "friend:m"); + break; + case FriendsServiceType_Viewer: + rc = smGetService(&g_friendsSrv, "friend:v"); + break; + case FriendsServiceType_System: + rc = smGetService(&g_friendsSrv, "friend:s"); + break; + } + + if (R_SUCCEEDED(rc)) + rc = serviceConvertToDomain(&g_friendsSrv); + + if (R_SUCCEEDED(rc)) + rc = sessionmgrCreate(&g_friendsSessionMgr, g_friendsSrv.session, 0x5); + + if (R_SUCCEEDED(rc)) { + rc = _friendsObjectDispatch(&g_friendsSrv, 0, + .out_num_objects = 1, + .out_objects = &g_friendsIFriendService + ); + } + + return rc; +} + +void _friendsCleanup(void) { + sessionmgrClose(&g_friendsSessionMgr); + _friendsObjectClose(&g_friendsIFriendService); + serviceClose(&g_friendsSrv); +} + +Service* friendsGetServiceSession(void) { + return &g_friendsSrv; +} + +Service* friendsGetServiceSession_IFriendsService(void) { + return &g_friendsIFriendService; +} + +Result friendsGetUserSetting(AccountUid uid, FriendsUserSetting *user_setting) { + return _friendsObjectDispatchIn(&g_friendsIFriendService, 20800, uid, + .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_Out | SfBufferAttr_FixedSize }, + .buffers = { { user_setting, sizeof(FriendsUserSetting) } } + ); +} + Result friendsTryPopFriendInvitationNotificationInfo(AccountUid *uid, void* buffer, u64 size, u64 *out_size) { Result rc=0; AppletStorage storage; @@ -27,4 +149,3 @@ Result friendsTryPopFriendInvitationNotificationInfo(AccountUid *uid, void* buff appletStorageClose(&storage); return rc; } -