diff --git a/nx/include/switch/applets/friends_la.h b/nx/include/switch/applets/friends_la.h index 9227aada..5638f1c6 100644 --- a/nx/include/switch/applets/friends_la.h +++ b/nx/include/switch/applets/friends_la.h @@ -11,25 +11,71 @@ /// Arg type values used with \ref FriendsLaArg. typedef enum { - FriendsLaArgType_ShowFriendList = 0, ///< ShowFriendList. Launches the applet with the "Friend List" menu initially selected. - FriendsLaArgType_ShowUserDetailInfo = 1, ///< ShowUserDetailInfo - FriendsLaArgType_StartSendingFriendRequest = 2, ///< StartSendingFriendRequest - FriendsLaArgType_ShowMethodsOfSendingFriendRequest = 3, ///< ShowMethodsOfSendingFriendRequest. Launches the applet with the "Add Friend" menu initially selected. - FriendsLaArgType_StartFacedFriendRequest = 4, ///< StartFacedFriendRequest. Launches the applet where the "Search for Local Users" menu is initially shown. Returning from this menu will exit the applet. - FriendsLaArgType_ShowReceivedFriendRequestList = 5, ///< ShowReceivedFriendRequestList. Launches the applet where the "Received Friend Requests" menu is initially shown. Returning from this menu will exit the applet. - FriendsLaArgType_ShowBlockedUserList = 6, ///< ShowBlockedUserList. Launches the applet where the "Blocked-User List" menu is initially shown. Returning from this menu will exit the applet. - FriendsLaArgType_ShowMyProfile = 7, ///< ShowMyProfile. Launches the applet with the "Profile" menu initially selected. ShowMyProfileForHomeMenu is identical to this except for playStartupSound=true. + FriendsLaArgType_ShowFriendList = 0, ///< ShowFriendList. Launches the applet with the "Friend List" menu initially selected. + FriendsLaArgType_ShowUserDetailInfo = 1, ///< ShowUserDetailInfo + FriendsLaArgType_StartSendingFriendRequest = 2, ///< StartSendingFriendRequest + FriendsLaArgType_ShowMethodsOfSendingFriendRequest = 3, ///< ShowMethodsOfSendingFriendRequest. Launches the applet with the "Add Friend" menu initially selected. + FriendsLaArgType_StartFacedFriendRequest = 4, ///< StartFacedFriendRequest. Launches the applet where the "Search for Local Users" menu is initially shown. Returning from this menu will exit the applet. + FriendsLaArgType_ShowReceivedFriendRequestList = 5, ///< ShowReceivedFriendRequestList. Launches the applet where the "Received Friend Requests" menu is initially shown. Returning from this menu will exit the applet. + FriendsLaArgType_ShowBlockedUserList = 6, ///< ShowBlockedUserList. Launches the applet where the "Blocked-User List" menu is initially shown. Returning from this menu will exit the applet. + FriendsLaArgType_ShowMyProfile = 7, ///< ShowMyProfile. Launches the applet with the "Profile" menu initially selected. ShowMyProfileForHomeMenu is identical to this except for playStartupSound=true. + FriendsLaArgType_StartFriendInvitation = 8, ///< [9.0.0+] StartFriendInvitation. Launches the applet for sending online-play invites to friends, where the friends are selected via the UI. + FriendsLaArgType_StartSendingFriendInvitation = 9, ///< [9.0.0+] StartSendingFriendInvitation. + FriendsLaArgType_ShowReceivedInvitationDetail = 10, ///< [9.0.0+] ShowReceivedInvitationDetail. } FriendsLaArgType; -/// Arg struct pushed for the applet input storage. -/// The fields following the uid are only set for ::FriendsLaArgType_ShowUserDetailInfo/::FriendsLaArgType_StartSendingFriendRequest, for everything else these are cleared. +/// Header for the arg struct. typedef struct { - u32 type; ///< \ref FriendsLaArgType - u32 pad; ///< Padding. - AccountUid uid; ///< \ref AccountUid - u64 networkServiceAccountId; ///< NetworkServiceAccountId for the other account. - FriendsInAppScreenName first_inAppScreenName; ///< First InAppScreenName. - FriendsInAppScreenName second_inAppScreenName; ///< Second InAppScreenName. + u32 type; ///< \ref FriendsLaArgType + u32 pad; ///< Padding. + AccountUid uid; ///< \ref AccountUid +} FriendsLaArgHeader; + +/// Common data for the arg struct, for the pre-9.0.0 types. +/// This is only set for ::FriendsLaArgType_ShowUserDetailInfo/::FriendsLaArgType_StartSendingFriendRequest, for everything else this is cleared. +typedef struct { + AccountNetworkServiceAccountId id; ///< \ref AccountNetworkServiceAccountId for the other account. + FriendsInAppScreenName first_inAppScreenName; ///< First InAppScreenName. + FriendsInAppScreenName second_inAppScreenName; ///< Second InAppScreenName. +} FriendsLaArgCommonData; + +/// Arg struct pushed for the applet input storage, for pre-9.0.0. +typedef struct { + FriendsLaArgHeader hdr; ///< \ref FriendsLaArgHeader + FriendsLaArgCommonData data; ///< \ref FriendsLaArgCommonData +} FriendsLaArgV1; + +/// Arg struct pushed for the applet input storage, for [9.0.0+]. +typedef struct { + FriendsLaArgHeader hdr; ///< \ref FriendsLaArgHeader + + union { + u8 raw[0x1090]; ///< Raw data. + + FriendsLaArgCommonData common; ///< \ref FriendsLaArgCommonData + + struct { + s32 id_count; ///< \ref AccountNetworkServiceAccountId count, must be 1-15. + u32 pad; ///< Padding. + u64 userdata_size; ///< User-data size, must be <=0x400. + u8 userdata[0x400]; ///< Arbitrary user-data, see above size. + FriendsFriendInvitationGameModeDescription desc; ///< \ref FriendsFriendInvitationGameModeDescription + } start_friend_invitation; ///< Data for ::FriendsLaArgType_StartFriendInvitation. + + struct { + s32 id_count; ///< \ref AccountNetworkServiceAccountId count, must be 1-15. + u32 pad; ///< Padding. + AccountNetworkServiceAccountId id_list[16]; ///< \ref AccountNetworkServiceAccountId list, see above count. + u64 userdata_size; ///< User-data size, must be <=0x400. + u8 userdata[0x400]; ///< Arbitrary user-data, see above size. + FriendsFriendInvitationGameModeDescription desc; ///< \ref FriendsFriendInvitationGameModeDescription + } start_sending_friend_invitation; ///< Data for ::FriendsLaArgType_StartSendingFriendInvitation. + + struct { + FriendsFriendInvitationId invitation_id; ///< \ref FriendsFriendInvitationId + FriendsFriendInvitationGroupId invitation_group_id; ///< \ref FriendsFriendInvitationGroupId + } show_received_invitation_detail; ///< Data for ::FriendsLaArgType_ShowReceivedInvitationDetail. + } data; ///< Data for each \ref FriendsLaArgType. } FriendsLaArg; /** @@ -41,20 +87,20 @@ Result friendsLaShowFriendList(AccountUid uid); /** * @brief Launches the applet with ::FriendsLaArgType_ShowUserDetailInfo, the specified input, and playStartupSound=false. * @param[in] uid \ref AccountUid - * @param[in] networkServiceAccountId NetworkServiceAccountId for the user to show UserDetailInfo for. + * @param[in] id \ref AccountNetworkServiceAccountId for the user to show UserDetailInfo for. * @param[in] first_inAppScreenName First \ref FriendsInAppScreenName. * @param[in] second_inAppScreenName Second \ref FriendsInAppScreenName. */ -Result friendsLaShowUserDetailInfo(AccountUid uid, u64 networkServiceAccountId, const FriendsInAppScreenName *first_inAppScreenName, const FriendsInAppScreenName *second_inAppScreenName); +Result friendsLaShowUserDetailInfo(AccountUid uid, AccountNetworkServiceAccountId id, const FriendsInAppScreenName *first_inAppScreenName, const FriendsInAppScreenName *second_inAppScreenName); /** * @brief Launches the applet with ::FriendsLaArgType_StartSendingFriendRequest, the specified input, and playStartupSound=false. On success, this will load the output Result from the output storage. * @param[in] uid \ref AccountUid - * @param[in] networkServiceAccountId NetworkServiceAccountId to send the friend request to. + * @param[in] id \ref AccountNetworkServiceAccountId to send the friend request to. * @param[in] first_inAppScreenName First \ref FriendsInAppScreenName. * @param[in] second_inAppScreenName Second \ref FriendsInAppScreenName. */ -Result friendsLaStartSendingFriendRequest(AccountUid uid, u64 networkServiceAccountId, const FriendsInAppScreenName *first_inAppScreenName, const FriendsInAppScreenName *second_inAppScreenName); +Result friendsLaStartSendingFriendRequest(AccountUid uid, AccountNetworkServiceAccountId id, const FriendsInAppScreenName *first_inAppScreenName, const FriendsInAppScreenName *second_inAppScreenName); /** * @brief Launches the applet with ::FriendsLaArgType_ShowMethodsOfSendingFriendRequest, the specified input, and playStartupSound=false. @@ -92,3 +138,35 @@ Result friendsLaShowMyProfile(AccountUid uid); */ Result friendsLaShowMyProfileForHomeMenu(AccountUid uid); +/** + * @brief Launches the applet with ::FriendsLaArgType_StartFriendInvitation, the specified input, and playStartupSound=false. On success, this will load the output Result from the output storage. + * @note Only available on [9.0.0+]. + * @param[in] uid \ref AccountUid + * @param[in] id_count \ref AccountNetworkServiceAccountId count, must be 1-15. Number of friends to invite. + * @param[in] desc \ref FriendsFriendInvitationGameModeDescription + * @param[in] userdata Arbitrary user-data. Can be NULL. + * @param[in] userdata_size User-data size, must be <=0x400. Can be 0 if userdata is NULL. + */ +Result friendsLaStartFriendInvitation(AccountUid uid, s32 id_count, const FriendsFriendInvitationGameModeDescription *desc, const void* userdata, u64 userdata_size); + +/** + * @brief Launches the applet with ::FriendsLaArgType_StartSendingFriendInvitation, the specified input, and playStartupSound=false. On success, this will load the output Result from the output storage. + * @note Only available on [9.0.0+]. + * @param[in] uid \ref AccountUid + * @param[in] id_list \ref AccountNetworkServiceAccountId list. + * @param[in] id_count Size of the id_list array in entries, must be 1-15. Number of friends to invite. + * @param[in] desc \ref FriendsFriendInvitationGameModeDescription + * @param[in] userdata Arbitrary user-data. Can be NULL. + * @param[in] userdata_size User-data size, must be <=0x400. Can be 0 if userdata is NULL. + */ +Result friendsLaStartSendingFriendInvitation(AccountUid uid, const AccountNetworkServiceAccountId *id_list, s32 id_count, const FriendsFriendInvitationGameModeDescription *desc, const void* userdata, u64 userdata_size); + +/** + * @brief Launches the applet with ::FriendsLaArgType_ShowReceivedInvitationDetail, the specified input, and playStartupSound=false. + * @note Only available on [9.0.0+]. + * @param[in] uid \ref AccountUid + * @param[in] invitation_id \ref FriendsFriendInvitationId + * @param[in] invitation_group_id \ref FriendsFriendInvitationGroupId + */ +Result friendsLaShowReceivedInvitationDetail(AccountUid uid, FriendsFriendInvitationId invitation_id, FriendsFriendInvitationGroupId invitation_group_id); + diff --git a/nx/include/switch/services/acc.h b/nx/include/switch/services/acc.h index b9213947..388c327f 100644 --- a/nx/include/switch/services/acc.h +++ b/nx/include/switch/services/acc.h @@ -44,6 +44,11 @@ typedef struct { char nickname[0x20]; ///< UTF-8 Nickname. } AccountProfileBase; +/// NetworkServiceAccountId +typedef struct { + u64 id; ///< Id. +} AccountNetworkServiceAccountId; + /** * @brief Sets the \ref AccountServiceType for initialization. Call this function before \ref accountInitialize, if needed. * @note By default ::AccountServiceType_NotInitialized will be used. diff --git a/nx/include/switch/services/friends.h b/nx/include/switch/services/friends.h index be769ad9..5174f81a 100644 --- a/nx/include/switch/services/friends.h +++ b/nx/include/switch/services/friends.h @@ -14,3 +14,18 @@ typedef struct { u64 languageCode; ///< LanguageCode, see set.h. } FriendsInAppScreenName; +/// FriendInvitationGameModeDescription +typedef struct { + u8 unk_x0[0xc00]; ///< Unknown. +} FriendsFriendInvitationGameModeDescription; + +/// FriendInvitationId +typedef struct { + u64 id; ///< Id. +} FriendsFriendInvitationId; + +/// FriendInvitationGroupId +typedef struct { + u64 id; ///< Id. +} FriendsFriendInvitationGroupId; + diff --git a/nx/source/applets/friends_la.c b/nx/source/applets/friends_la.c index ba29aee1..35e3b050 100644 --- a/nx/source/applets/friends_la.c +++ b/nx/source/applets/friends_la.c @@ -5,20 +5,36 @@ #include "services/applet.h" #include "applets/libapplet.h" #include "applets/friends_la.h" +#include "runtime/hosversion.h" static Result _friendsLaShow(const FriendsLaArg *arg, bool playStartupSound) { Result rc=0; Result rc2=0; size_t readsize=0; + u32 version=0x1; + const void* arg_ptr = arg; + size_t arg_size = sizeof(*arg); LibAppletArgs commonargs; + FriendsLaArgV1 argv1; - libappletArgsCreate(&commonargs, 0x1); + if (hosversionAtLeast(9,0,0)) + version = 0x10000; + else { + memset(&argv1, 0, sizeof(argv1)); + arg_ptr = &argv1; + arg_size = sizeof(argv1); + + memcpy(&argv1.hdr, &arg->hdr, sizeof(FriendsLaArgHeader)); + memcpy(&argv1.data, &arg->data.common, sizeof(FriendsLaArgCommonData)); + } + + libappletArgsCreate(&commonargs, version); libappletArgsSetPlayStartupSound(&commonargs, playStartupSound); - if (arg->type != FriendsLaArgType_StartSendingFriendRequest) - rc = libappletLaunch(AppletId_myPage, &commonargs, arg, sizeof(*arg), NULL, 0, NULL); + if (arg->hdr.type != FriendsLaArgType_StartSendingFriendRequest && arg->hdr.type != FriendsLaArgType_StartFriendInvitation && arg->hdr.type != FriendsLaArgType_StartSendingFriendInvitation) + rc = libappletLaunch(AppletId_myPage, &commonargs, arg_ptr, arg_size, NULL, 0, NULL); else { - rc = libappletLaunch(AppletId_myPage, &commonargs, arg, sizeof(*arg), &rc2, sizeof(rc2), &readsize); + rc = libappletLaunch(AppletId_myPage, &commonargs, arg_ptr, arg_size, &rc2, sizeof(rc2), &readsize); if (R_SUCCEEDED(rc) && readsize!=sizeof(rc2)) rc = MAKERESULT(Module_Libnx, LibnxError_BadInput); if (R_SUCCEEDED(rc)) rc = rc2; } @@ -27,13 +43,13 @@ static Result _friendsLaShow(const FriendsLaArg *arg, bool playStartupSound) { } static Result _friendsLaShowSimple(FriendsLaArgType type, AccountUid uid, bool playStartupSound) { - FriendsLaArg arg = {.type = type, .uid = uid}; + FriendsLaArg arg = {.hdr.type = type, .hdr.uid = uid}; return _friendsLaShow(&arg, playStartupSound); } -static Result _friendsLaShowAll(FriendsLaArgType type, AccountUid uid, u64 networkServiceAccountId, const FriendsInAppScreenName *first_inAppScreenName, const FriendsInAppScreenName *second_inAppScreenName, bool playStartupSound) { - FriendsLaArg arg = {.type = type, .uid = uid, .networkServiceAccountId = networkServiceAccountId, .first_inAppScreenName = *first_inAppScreenName, .second_inAppScreenName = *second_inAppScreenName}; +static Result _friendsLaShowAll(FriendsLaArgType type, AccountUid uid, AccountNetworkServiceAccountId id, const FriendsInAppScreenName *first_inAppScreenName, const FriendsInAppScreenName *second_inAppScreenName, bool playStartupSound) { + FriendsLaArg arg = {.hdr.type = type, .hdr.uid = uid, .data.common.id = id, .data.common.first_inAppScreenName = *first_inAppScreenName, .data.common.second_inAppScreenName = *second_inAppScreenName}; return _friendsLaShow(&arg, playStartupSound); } @@ -42,12 +58,12 @@ Result friendsLaShowFriendList(AccountUid uid) { return _friendsLaShowSimple(FriendsLaArgType_ShowFriendList, uid, false); } -Result friendsLaShowUserDetailInfo(AccountUid uid, u64 networkServiceAccountId, const FriendsInAppScreenName *first_inAppScreenName, const FriendsInAppScreenName *second_inAppScreenName) { - return _friendsLaShowAll(FriendsLaArgType_ShowUserDetailInfo, uid, networkServiceAccountId, first_inAppScreenName, second_inAppScreenName, false); +Result friendsLaShowUserDetailInfo(AccountUid uid, AccountNetworkServiceAccountId id, const FriendsInAppScreenName *first_inAppScreenName, const FriendsInAppScreenName *second_inAppScreenName) { + return _friendsLaShowAll(FriendsLaArgType_ShowUserDetailInfo, uid, id, first_inAppScreenName, second_inAppScreenName, false); } -Result friendsLaStartSendingFriendRequest(AccountUid uid, u64 networkServiceAccountId, const FriendsInAppScreenName *first_inAppScreenName, const FriendsInAppScreenName *second_inAppScreenName) { - return _friendsLaShowAll(FriendsLaArgType_StartSendingFriendRequest, uid, networkServiceAccountId, first_inAppScreenName, second_inAppScreenName, false); +Result friendsLaStartSendingFriendRequest(AccountUid uid, AccountNetworkServiceAccountId id, const FriendsInAppScreenName *first_inAppScreenName, const FriendsInAppScreenName *second_inAppScreenName) { + return _friendsLaShowAll(FriendsLaArgType_StartSendingFriendRequest, uid, id, first_inAppScreenName, second_inAppScreenName, false); } Result friendsLaShowMethodsOfSendingFriendRequest(AccountUid uid) { @@ -74,3 +90,60 @@ Result friendsLaShowMyProfileForHomeMenu(AccountUid uid) { return _friendsLaShowSimple(FriendsLaArgType_ShowMyProfile, uid, true); } +Result friendsLaStartFriendInvitation(AccountUid uid, s32 id_count, const FriendsFriendInvitationGameModeDescription *desc, const void* userdata, u64 userdata_size) { + if (hosversionBefore(9,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + FriendsLaArg arg; + memset(&arg, 0, sizeof(arg)); + if (id_count<1 || id_count>15 || userdata_size>=sizeof(arg.data.start_friend_invitation.userdata) || (userdata==NULL && userdata_size!=0)) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + arg.hdr.type = FriendsLaArgType_StartFriendInvitation; + arg.hdr.uid = uid; + + arg.data.start_friend_invitation.id_count = id_count; + arg.data.start_friend_invitation.userdata_size = userdata_size; + if (userdata && userdata_size) memcpy(arg.data.start_friend_invitation.userdata, userdata, userdata_size); + memcpy(&arg.data.start_friend_invitation.desc, desc, sizeof(*desc)); + + return _friendsLaShow(&arg, false); +} + +Result friendsLaStartSendingFriendInvitation(AccountUid uid, const AccountNetworkServiceAccountId *id_list, s32 id_count, const FriendsFriendInvitationGameModeDescription *desc, const void* userdata, u64 userdata_size) { + if (hosversionBefore(9,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + FriendsLaArg arg; + memset(&arg, 0, sizeof(arg)); + if (id_count<1 || id_count>15 || userdata_size>=sizeof(arg.data.start_sending_friend_invitation.userdata) || (userdata==NULL && userdata_size!=0)) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + arg.hdr.type = FriendsLaArgType_StartSendingFriendInvitation; + arg.hdr.uid = uid; + + arg.data.start_sending_friend_invitation.id_count = id_count; + arg.data.start_sending_friend_invitation.userdata_size = userdata_size; + memcpy(arg.data.start_sending_friend_invitation.id_list, id_list, id_count*sizeof(AccountNetworkServiceAccountId)); + if (userdata && userdata_size) memcpy(arg.data.start_sending_friend_invitation.userdata, userdata, userdata_size); + memcpy(&arg.data.start_sending_friend_invitation.desc, desc, sizeof(*desc)); + + return _friendsLaShow(&arg, false); +} + +Result friendsLaShowReceivedInvitationDetail(AccountUid uid, FriendsFriendInvitationId invitation_id, FriendsFriendInvitationGroupId invitation_group_id) { + if (hosversionBefore(9,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + FriendsLaArg arg; + memset(&arg, 0, sizeof(arg)); + + arg.hdr.type = FriendsLaArgType_ShowReceivedInvitationDetail; + arg.hdr.uid = uid; + + arg.data.show_received_invitation_detail.invitation_id = invitation_id; + arg.data.show_received_invitation_detail.invitation_group_id = invitation_group_id; + + return _friendsLaShow(&arg, false); +} +