diff --git a/libctru/include/3ds/services/frd.h b/libctru/include/3ds/services/frd.h index 65d4674..4868863 100644 --- a/libctru/include/3ds/services/frd.h +++ b/libctru/include/3ds/services/frd.h @@ -2,7 +2,7 @@ * @file frd.h * @brief Friend Services */ - #pragma once +#pragma once #include <3ds.h> #define FRIENDS_SCREEN_NAME_SIZE 0x16 // 11 (0x16 because UTF-16) @@ -13,49 +13,88 @@ /// Friend key data typedef struct { - u32 principalId; - u32 padding; - u64 localFriendCode; + u32 principalId; + u32 padding; + u64 localFriendCode; } FriendKey; +/// Friend Title data +typedef struct +{ + u64 tid; + u32 version; + u32 unk; +} TitleData; /// Structure containing basic Mii information. typedef struct { - u32 mii_id; - u64 system_id; - u32 cdate; - u8 mac[0x6]; - u16 padding; - u16 misc1; - u16 mii_name[0xB]; - u8 width; - u8 height; - u32 misc2; - u32 unknown1; - u32 misc3; - u32 unknown2; - u8 allow_copy; - u8 unknown3[0x7]; - u16 author[0xB]; + u32 mii_id; + u64 system_id; + u32 cdate; + u8 mac[0x6]; + u16 padding; + u16 misc1; + u16 mii_name[0xB]; + u8 width; + u8 height; + u32 misc2; + u32 unknown1; + u32 misc3; + u32 unknown2; + u8 allow_copy; + u8 unknown3[0x7]; + u16 author[0xB]; } MiiData; /// Friend profile data typedef struct { - u8 region; // The region code for the hardware. - u8 country; // Country code. - u8 area; // Area code. - u8 language; // Language code. - u8 platform; // Platform code. - u32 padding; + u8 region; // The region code for the hardware. + u8 country; // Country code. + u8 area; // Area code. + u8 language; // Language code. + u8 platform; // Platform code. + u32 padding; } Profile; +/// Game Description structure +typedef struct +{ + TitleData data; + u16 desc[128]; +} GameDescription; + +/// Friend Notification Event structure +typedef struct +{ + u8 type; + u8 padding3[3]; + u32 padding; + FriendKey key; +}NotificationEvent; + +/// Enum to use with FRD_GetNotificationEvent +typedef enum +{ + selfOnline = 1, // Self went online + selfOffline, // Self went offline + friendOnline, // Friend Went Online + friendPresence, // Friend Presence changed + friendMii, // Friend Mii changed + friendProfile, // Friend Profile changed + friendOffline, // Friend went offline + friendBecameFriend, // Friend registered self as friend + friendInvitaton // Friend Sent invitation +}NotificationTypes; + /// Initializes FRD service. Result frdInit(void); /// Exists FRD. void frdExit(void); +/// Get FRD handle. +Handle *frdSessionGetHandle(void); /** * @brief Gets the login status of the current user. * @param state Pointer to write the current user's login status to. @@ -71,6 +110,12 @@ Result FRDU_IsOnline(bool *state); /// Logs out of Nintendo's friend server. Result FRD_Logout(void); +/** + * @brief Log in to Nintendo's friend server. + * @param event Event to signal when Login is done. + */ +Result FRD_Login(Handle event); + /** * @brief Gets the current user's friend key. * @param key Pointer to write the current user's friend key to. @@ -132,6 +177,38 @@ Result FRD_GetMyComment(char *comment, size_t max_size); */ Result FRD_GetFriendKeyList(FriendKey *friendKeyList, size_t *num, size_t offset, size_t size); +/** + * @brief Gets Friends Mii data. + * @param mii Pointer to write Mii data to. + * @param keys Pointer to FriendKeys. + * @param numberOfKeys Number of Friendkeys. + */ +Result FRD_GetFriendMii(MiiData *mii, const FriendKey *keys, size_t numberOfKeys); + +/** + * @brief Get a friend's profile data. + * @param profile Pointer to write profile data to. + * @param keys Pointer to FriendKeys. + * @param numberOfKeys Number of FriendKeys. + */ +Result FRD_GetFriendProfile(Profile *profile, const FriendKey *keys, size_t numberOfKeys); + +/** + * @brief Get a friend's playing Game. + * @param desc Pointer to write Game Description data to. + * @param keys Pointer to FriendKeys, + * @param numberOfKeys Number Of FriendKeys. + */ +Result FRD_GetFriendPlayingGame(GameDescription *desc, const FriendKey *keys, size_t numberOfKeys); + +/** + * @brief Get a friend's favourite Game. + * @param desc Pointer to write Game Description data to. + * @param keys Pointer to FriendKeys, + * @param numberOfKeys Number Of FriendKeys. + */ +Result FRD_GetFriendFavouriteGame(GameDescription *desc, const FriendKey *keys, size_t numberOfKeys); + /** * @brief Determines if the application was started using the join game option in the friends applet. * @param friendKeyList Pointer to a list of friend keys. @@ -145,6 +222,20 @@ Result FRD_IsFromFriendList(FriendKey *friendKeyList, bool *isFromList); */ Result FRD_UpdateGameModeDescription(const char *desc); +/** + * @brief Event which is signaled when friend login states change. + * @param event event which will be signaled. + */ +Result FRD_AttachToEventNotification(Handle event); + +/** + * @brief Get Latest Event Notification + * @param event Pointer to write recieved notification event struct to. + * @param size Number of events + * @param recievedNotifCount Number of notification reccieved. + */ +Result FRD_GetEventNotification(NotificationEvent *event, size_t size, u32 *recievedNotifCount); + /** * @brief Returns the friend code using the given principal ID. * @param principalId The principal ID being used. @@ -154,7 +245,7 @@ Result FRD_PrincipalIdToFriendCode(u32 principalId, u64 *friendCode); /** * @brief Returns the principal ID using the given friend code. - * @param friendCode The friend code being used. + * @param friendCode The friend code being used. * @param principalId Pointer to write the principal ID to. */ Result FRD_FriendCodeToPrincipalId(u64 friendCode, u32 *principalId); @@ -171,3 +262,17 @@ Result FRD_IsValidFriendCode(u64 friendCode, bool *isValid); * @param sdkVer The SDK version needed to be used. */ Result FRD_SetClientSdkVersion(u32 sdkVer); + +/** + * @brief Add a Friend online. + * @param event Event signaled when friend is registered. + * @param principalId PrincipalId of the friend to add. + */ +Result FRD_AddFriendOnline(Handle event, u32 principalId); + +/** + * @brief Remove a Friend. + * @param principalId PrinipalId of the friend code to remove. + * @param localFriendCode LocalFriendCode of the friend code to remove. + */ +Result FRD_RemoveFriend(u32 principalId, u64 localFriendCode); diff --git a/libctru/source/services/frd.c b/libctru/source/services/frd.c index 71dab45..aa52e58 100644 --- a/libctru/source/services/frd.c +++ b/libctru/source/services/frd.c @@ -6,20 +6,20 @@ static int frdRefCount; static void frdConvertToUTF8(char* out, const u16* in, size_t max) { - if (!in || !*in) - { - out[0] = 0; - return; - } + if (!in || !*in) + { + out[0] = 0; + return; + } - ssize_t units = utf16_to_utf8((uint8_t*)out, in, max); - if (units < 0) - { - out[0] = 0; - return; - } + ssize_t units = utf16_to_utf8((uint8_t*)out, in, max); + if (units < 0) + { + out[0] = 0; + return; + } - out[units] = 0; + out[units] = 0; } static void frdConvertToUTF16(u16* out, const char* in, size_t max) @@ -37,7 +37,7 @@ static void frdConvertToUTF16(u16* out, const char* in, size_t max) return; } - out[units] = 0; + out[units] = 0; } Result frdInit(void) @@ -46,9 +46,9 @@ Result frdInit(void) if (AtomicPostIncrement(&frdRefCount)) return 0; - ret = srvGetServiceHandle(&frdHandle, "frd:u"); + ret = srvGetServiceHandle(&frdHandle, "frd:a"); if (R_FAILED(ret)) ret = srvGetServiceHandle(&frdHandle, "frd:n"); - if (R_FAILED(ret)) ret = srvGetServiceHandle(&frdHandle, "frd:a"); + if (R_FAILED(ret)) ret = srvGetServiceHandle(&frdHandle, "frd:u"); if (R_FAILED(ret)) AtomicDecrement(&frdRefCount); return ret; @@ -60,6 +60,11 @@ void frdExit(void) svcCloseHandle(frdHandle); } +Handle *frdGetSessionHandle(void) +{ + return &frdHandle; +} + Result FRDU_HasLoggedIn(bool *state) { Result ret = 0; @@ -70,7 +75,7 @@ Result FRDU_HasLoggedIn(bool *state) if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; *state = cmdbuf[2] & 0xFF; - + return (Result)cmdbuf[1]; } @@ -84,7 +89,21 @@ Result FRDU_IsOnline(bool *state) if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; *state = cmdbuf[2] & 0xFF; - + + return (Result)cmdbuf[1]; +} + +Result FRD_Login(Handle event) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x03,0,2); // 0x30002 + cmdbuf[1] = 0; + cmdbuf[2] = (u32)event; + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + return (Result)cmdbuf[1]; } @@ -94,7 +113,7 @@ Result FRD_Logout(void) u32 *cmdbuf = getThreadCommandBuffer(); cmdbuf[0] = IPC_MakeHeader(0x04,0,0); // 0x40000 - + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; return (Result)cmdbuf[1]; @@ -110,7 +129,7 @@ Result FRD_GetMyFriendKey(FriendKey *key) if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; memcpy(key, &cmdbuf[2], sizeof(FriendKey)); - + return (Result)cmdbuf[1]; } @@ -123,10 +142,10 @@ Result FRD_GetMyPreference(bool *isPublicMode, bool *isShowGameName, bool *isSho if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; - *isPublicMode = cmdbuf[2] & 0xFF; // Public mode - *isShowGameName = cmdbuf[3] & 0xFF; // Show current game + *isPublicMode = cmdbuf[2] & 0xFF; // Public mode + *isShowGameName = cmdbuf[3] & 0xFF; // Show current game *isShowPlayedGame = cmdbuf[4] & 0xFF; // Show game history. - + return (Result)cmdbuf[1]; } @@ -140,7 +159,7 @@ Result FRD_GetMyProfile(Profile *profile) if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; memcpy(profile, &cmdbuf[2], sizeof(Profile)); - + return (Result)cmdbuf[1]; } @@ -152,9 +171,9 @@ Result FRD_GetMyScreenName(char *name, size_t max_size) cmdbuf[0] = IPC_MakeHeader(0x09,0,0); // 0x90000 if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; - + frdConvertToUTF8(name, (u16*)&cmdbuf[2], max_size); - + return (Result)cmdbuf[1]; } @@ -168,7 +187,7 @@ Result FRD_GetMyMii(MiiData *mii) if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; memcpy(mii, &cmdbuf[2], FRIEND_MII_STORE_DATA_SIZE); - + return (Result)cmdbuf[1]; } @@ -182,7 +201,7 @@ Result FRD_GetMyPlayingGame(u64 *titleId) if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; *titleId = (((u64)cmdbuf[3]) << 32 | (u64)cmdbuf[2]); - + return (Result)cmdbuf[1]; } @@ -196,7 +215,7 @@ Result FRD_GetMyFavoriteGame(u64 *titleId) if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; *titleId = (((u64)cmdbuf[3]) << 32 | (u64)cmdbuf[2]); - + return (Result)cmdbuf[1]; } @@ -208,9 +227,9 @@ Result FRD_GetMyComment(char *comment, size_t max_size) cmdbuf[0] = IPC_MakeHeader(0x0F,0,0); // 0xF0000 if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; - + frdConvertToUTF8(comment, (u16*)&cmdbuf[2], max_size); - + return (Result)cmdbuf[1]; } @@ -228,7 +247,80 @@ Result FRD_GetFriendKeyList(FriendKey *friendKeyList, size_t *num, size_t offset if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; *num = cmdbuf[2]; - + + return (Result)cmdbuf[1]; +} + +Result FRD_GetFriendMii(MiiData *mii, const FriendKey *keys, size_t numberOfKeys) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x14,1,4); //0x140044 + cmdbuf[1] = numberOfKeys; + cmdbuf[2] = (numberOfKeys << 18)|2; + cmdbuf[3] = (u32)keys; + cmdbuf[4] = 0x600 * numberOfKeys | 0xC; + cmdbuf[5] = (u32)mii; + + if(R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + return (Result)cmdbuf[1]; +} + +Result FRD_GetFriendProfile(Profile *profile, const FriendKey *keys, size_t numberOfKeys) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x15,1,2); // 0x150042 + cmdbuf[1] = numberOfKeys; + cmdbuf[2] = (numberOfKeys << 18)|2; + cmdbuf[3] = (u32)keys; + + u32 *staticbuf = getThreadStaticBuffers(); + staticbuf[0] = (numberOfKeys << 17)|2; + staticbuf[1] = (u32)profile; + + if(R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + return (Result)cmdbuf[1]; +} + +Result FRD_GetFriendPlayingGame(GameDescription *desc, const FriendKey *keys, size_t numberOfKeys) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x18,1,4); // 0x180044 + cmdbuf[1] = numberOfKeys; + cmdbuf[2] = (numberOfKeys << 18) | 2; + cmdbuf[3] = (u32)keys; + cmdbuf[4] = 0x1100 * numberOfKeys | 0xC; + cmdbuf[5] = (u32)desc; + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + return (Result)cmdbuf[1]; +} + +Result FRD_GetFriendFavouriteGame(GameDescription *desc, const FriendKey *keys, size_t numberOfKeys) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x19,1,2); // 0x190042 + cmdbuf[1] = numberOfKeys; + cmdbuf[2] = (numberOfKeys << 18) | 2; + cmdbuf[3] = (u32)keys; + + u32 *staticbuf = getThreadStaticBuffers(); + + staticbuf[0] = (numberOfKeys << 18) | 2; + staticbuf[1] = (u32)desc; + + if(R_FAILED(svcSendSyncRequest(frdHandle))) return ret; + return (Result)cmdbuf[1]; } @@ -244,14 +336,14 @@ Result FRD_IsFromFriendList(FriendKey *friendKeyList, bool *isFromList) if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; *isFromList = cmdbuf[2] & 0xFF; - + return (Result)cmdbuf[1]; } Result FRD_UpdateGameModeDescription(const char *desc) { u16 u16_desc[strlen(desc) + 1]; - + frdConvertToUTF16(u16_desc, desc, strlen(desc) + 1); Result ret = 0; @@ -260,12 +352,46 @@ Result FRD_UpdateGameModeDescription(const char *desc) cmdbuf[0] = IPC_MakeHeader(0x1D,0,2); // 0x1D0002 cmdbuf[1] = 0x400802; cmdbuf[2] = (uintptr_t)u16_desc; - + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; return (Result)cmdbuf[1]; } +Result FRD_AttachToEventNotification(Handle event) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] =IPC_MakeHeader(0x20,0,2); //0x200002; + cmdbuf[1] = 0; + cmdbuf[2] = (u32)event; + + if(R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + return (Result)cmdbuf[1]; +} + +Result FRD_GetEventNotification(NotificationEvent *event, size_t size, u32 *recievedNotifCount) +{ + Result ret = 0; + + u32 *cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x22,1,0); //0x220040 + cmdbuf[1] = (u32)size; + + u32 *staticbuf = getThreadStaticBuffers(); + staticbuf[0] = 0x60000 * size | 2; + staticbuf[1] = (u32)event; + + if(R_FAILED(ret = svcSendSyncRequest(frdHandle))) + return ret; + + *recievedNotifCount = cmdbuf[3]; + + return (Result)cmdbuf[1]; +} + Result FRD_PrincipalIdToFriendCode(u32 principalId, u64 *friendCode) { Result ret = 0; @@ -277,7 +403,7 @@ Result FRD_PrincipalIdToFriendCode(u32 principalId, u64 *friendCode) if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; *friendCode = (((u64)cmdbuf[3]) << 32 | (u64)cmdbuf[2]); - + return (Result)cmdbuf[1]; } @@ -293,7 +419,7 @@ Result FRD_FriendCodeToPrincipalId(u64 friendCode, u32 *principalId) if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; *principalId = cmdbuf[2]; - + return (Result)cmdbuf[1]; } @@ -309,7 +435,7 @@ Result FRD_IsValidFriendCode(u64 friendCode, bool *isValid) if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; *isValid = cmdbuf[2] & 0xFF; - + return (Result)cmdbuf[1]; } @@ -325,4 +451,32 @@ Result FRD_SetClientSdkVersion(u32 sdkVer) if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; return (Result)cmdbuf[1]; -} \ No newline at end of file +} + +Result FRD_AddFriendOnline(Handle event, u32 principalId) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x406,1,2); //0x4060042; + cmdbuf[1] = principalId; + cmdbuf[2] = 0; + cmdbuf[3] = (u32)event; + + if(R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + return (Result)cmdbuf[1]; +} + +Result FRD_RemoveFriend(u32 principalId, u64 localFriendCode) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x409,4,0); //0x4090100; + cmdbuf[1] = principalId; + cmdbuf[2] = localFriendCode & 0xffffffff; + cmdbuf[3] = (localFriendCode >> 32) & 0xffffffff; + + if(R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + return cmdbuf[1]; +}