From 0ed65ef85d3262b95bb4da79b0392805ab638538 Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sat, 17 Mar 2018 10:23:59 -0500 Subject: [PATCH] Initial bring up of the Friend Service --- libctru/include/3ds.h | 1 + libctru/include/3ds/services/frd.h | 171 +++++++++++++++++ libctru/source/services/frd.c | 288 +++++++++++++++++++++++++++++ 3 files changed, 460 insertions(+) create mode 100644 libctru/include/3ds/services/frd.h create mode 100644 libctru/source/services/frd.c diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index d750482..86c92fe 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -40,6 +40,7 @@ extern "C" { #include <3ds/services/csnd.h> #include <3ds/services/dsp.h> #include <3ds/services/fs.h> +#include <3ds/services/frd.h> #include <3ds/services/gspgpu.h> #include <3ds/services/gsplcd.h> #include <3ds/services/hid.h> diff --git a/libctru/include/3ds/services/frd.h b/libctru/include/3ds/services/frd.h new file mode 100644 index 0000000..f92ef1e --- /dev/null +++ b/libctru/include/3ds/services/frd.h @@ -0,0 +1,171 @@ +/** + * @file frd.h + * @brief Friend Services + */ + #pragma once +#include <3ds.h> + +#define FRIENDS_SCREEN_NAME_SIZE 0x16 // 11 (0x16 because UTF-16) +#define FRIENDS_COMMENT_SIZE 0x22 // 16 (0x21 because UTF-16 + null character) +#define FRIEND_LIST_SIZE 0x64 // 100 (Number of Friends) +#define FRIEND_MII_STORE_DATA_SIZE 0x60 // 96 (Mii data) + +/// Friend key data +typedef struct +{ + u32 principalId; + u32 padding; + u64 localFriendCode; +} FriendKey; + +/// 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]; +} 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; +} Profile; + +/// Initializes FRD service. +Result frdInit(void); + +/// Exists FRD. +void frdExit(void); + +/** + * @brief Gets the login status of the current user. + * @param state Pointer to write the current user's login status to. + */ +Result FRDU_HasLoggedIn(bool *state); + +/** + * @brief Gets the online status of the current user. + * @param state Pointer to write the current user's online status to. + */ +Result FRDU_IsOnline(bool *state); + +/// Logs out of Nintendo's friend server. +Result FRD_Logout(void); + +/** + * @brief Gets the current user's friend key. + * @param key Pointer to write the current user's friend key to. + */ +Result FRD_GetMyFriendKey(FriendKey *key); + +/** + * @brief Gets the current user's privacy information. + * @param isPublicMode Determines whether friends are notified of the current user's online status. + * @param isShowGameName Determines whether friends are notified of the application that the current user is running. + * @param isShowPlayedGame Determiens whether to display the current user's game history. + */ +Result FRD_GetMyPreference(bool *isPublicMode, bool *isShowGameName, bool *isShowPlayedGame); + +/** + * @brief Gets the current user's profile information. + * @param profile Pointer to write the current user's profile information to. + */ +Result FRD_GetMyProfile(Profile *profile); + +/** + * @brief Gets the current user's screen name. + * @param name Pointer to write the current user's screen name to. 11-byte UTF-16 screen name (with null terminator) + */ +Result FRD_GetMyScreenName(u16 *name); + +/** + * @brief Gets the current user's Mii data. + * @param mii Pointer to write the current user's mii data to. + */ +Result FRD_GetMyMii(MiiData *mii); + +/** + * @brief Gets the current user's playing game. + * @param titleId Pointer to write the current user's playing game to. + */ +Result FRD_GetMyPlayingGame(u64 *titleId); + +/** + * @brief Gets the current user's favourite game. + * @param titleId Pointer to write the title ID of current user's favourite game to. + */ +Result FRD_GetMyFavoriteGame(u64 *titleId); + +/** + * @brief Gets the current user's comment on their friend profile. + * @param comment Pointer to write the current user's comment to. + */ +Result FRD_GetMyComment(u16 *comment); + +/** + * @brief Gets the current user's firend key list + * @param friendKeyList Pointer to write the friend key list to. + * @param num Stores the number of friend keys obtained. + * @param offset the index of the friend key to start with. + * @param size Size of the friend key list. (FRIEND_LIST_SIZE) + */ +Result FRD_GetFriendKeyList(FriendKey *friendKeyList, size_t *num, size_t offset, size_t size); + +/** + * @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. + * @param isFromList Pointer to a write the friendship status to. + */ +Result FRD_IsFromFriendList(FriendKey *friendKeyList, bool *isFromList); + +/** + * @brief Updates the game mode description string. + * @param desc Pointer to write the game mode description to. + */ +Result FRD_UpdateGameModeDescription(u16 *desc); + +/** + * @brief Returns the friend code using the given principal ID. + * @param principalId The principal ID being used. + * @param friendCode Pointer to write the friend code to. + */ +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 principalId Pointer to write the principal ID to. + */ +Result FRD_FriendCodeToPrincipalId(u64 friendCode, u32 *principalId); + +/** + * @brief Checks if the friend code is valid. + * @param friendCode The friend code being used. + * @param isValid Pointer to write the validity of the friend code to. + */ +Result FRD_IsValidFriendCode(u64 friendCode, bool *isValid); + +/** + * @brief Sets the Friend API to use a specific SDK version. + * @param sdkVer The SDK version needed to be used. + */ +Result FRD_SetClientSdkVersion(u32 sdkVer); diff --git a/libctru/source/services/frd.c b/libctru/source/services/frd.c new file mode 100644 index 0000000..1cb7954 --- /dev/null +++ b/libctru/source/services/frd.c @@ -0,0 +1,288 @@ +#include +#include <3ds/services/frd.h> + +static Handle frdHandle; +static int frdRefCount; + +Result frdInit(void) +{ + Result ret = 0; + + if (AtomicPostIncrement(&frdRefCount)) return 0; + + ret = srvGetServiceHandle(&frdHandle, "frd:u"); + if (R_FAILED(ret)) ret = srvGetServiceHandle(&frdHandle, "frd:n"); + if (R_FAILED(ret)) ret = srvGetServiceHandle(&frdHandle, "frd:a"); + if (R_FAILED(ret)) AtomicDecrement(&frdRefCount); + + return ret; +} + +void frdExit(void) +{ + if (AtomicDecrement(&frdRefCount)) return; + svcCloseHandle(frdHandle); +} + +Result FRDU_HasLoggedIn(bool *state) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x01,0,0); // 0x10000 + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + *state = cmdbuf[2] & 0xFF; + + return (Result)cmdbuf[1]; +} + +Result FRDU_IsOnline(bool *state) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x02,0,0); // 0x20000 + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + *state = cmdbuf[2] & 0xFF; + + return (Result)cmdbuf[1]; +} + +Result FRD_Logout(void) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x04,0,0); // 0x40000 + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + return (Result)cmdbuf[1]; +} + +Result FRD_GetMyFriendKey(FriendKey *key) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x05,0,0); // 0x50000 + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + memcpy(key, &cmdbuf[2], sizeof(FriendKey)); + + return (Result)cmdbuf[1]; +} + +Result FRD_GetMyPreference(bool *isPublicMode, bool *isShowGameName, bool *isShowPlayedGame) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x06,0,0); // 0x60000 + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + *isPublicMode = cmdbuf[2] & 0xFF; // Public mode + *isShowGameName = cmdbuf[3] & 0xFF; // Show current game + *isShowPlayedGame = cmdbuf[4] & 0xFF; // Show game history. + + return (Result)cmdbuf[1]; +} + +Result FRD_GetMyProfile(Profile *profile) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x07, 0, 0); // 0x70000 + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + memcpy(profile, &cmdbuf[2], sizeof(Profile)); + + return (Result)cmdbuf[1]; +} + +Result FRD_GetMyScreenName(u16 *name) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x09,0,0); // 0x90000 + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + memcpy(name, &cmdbuf[2], FRIENDS_SCREEN_NAME_SIZE); // 11-byte UTF-16 screen name (with null terminator) + + return (Result)cmdbuf[1]; +} + +Result FRD_GetMyMii(MiiData *mii) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x0A,0,0); // 0xA0000 + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + memcpy(mii, &cmdbuf[2], FRIEND_MII_STORE_DATA_SIZE); + + return (Result)cmdbuf[1]; +} + +Result FRD_GetMyPlayingGame(u64 *titleId) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x0C,0,0); // 0xC0000 + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + *titleId = (((u64)cmdbuf[3]) << 32 | (u64)cmdbuf[2]); + + return (Result)cmdbuf[1]; +} + +Result FRD_GetMyFavoriteGame(u64 *titleId) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x0D,0,0); // 0xD0000 + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + *titleId = (((u64)cmdbuf[3]) << 32 | (u64)cmdbuf[2]); + + return (Result)cmdbuf[1]; +} + +Result FRD_GetMyComment(u16 *comment) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x0F,0,0); // 0xF0000 + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + memcpy(comment, &cmdbuf[2], FRIENDS_COMMENT_SIZE); // 16-byte UTF-16 comment + + return (Result)cmdbuf[1]; +} + +Result FRD_GetFriendKeyList(FriendKey *friendKeyList, size_t *num, size_t offset, size_t size) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x11,2,0); // 0x110080 + cmdbuf[1] = 0; + cmdbuf[2] = size; + cmdbuf[64] = (size << 18) | 2; + cmdbuf[65] = (u32)friendKeyList; + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + *num = cmdbuf[2]; + + return (Result)cmdbuf[1]; +} + +Result FRD_IsFromFriendList(FriendKey *friendKeyList, bool *isFromList) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x1B,2,0); // 0x1B0080 + cmdbuf[1] = (u32)(friendKeyList->localFriendCode & 0xFFFFFFFF); + cmdbuf[2] = (u32)(friendKeyList->localFriendCode >> 32); + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + *isFromList = cmdbuf[2] & 0xFF; + + return (Result)cmdbuf[1]; +} + +Result FRD_UpdateGameModeDescription(u16 *desc) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x1D,0,2); // 0x1D0002 + cmdbuf[1] = 0x400802; + cmdbuf[2] = (uintptr_t)desc; + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + return (Result)cmdbuf[1]; +} + +Result FRD_PrincipalIdToFriendCode(u32 principalId, u64 *friendCode) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x24,1,0); // 0x240040 + cmdbuf[1] = principalId; + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + *friendCode = (((u64)cmdbuf[3]) << 32 | (u64)cmdbuf[2]); + + return (Result)cmdbuf[1]; +} + +Result FRD_FriendCodeToPrincipalId(u64 friendCode, u32 *principalId) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x25,2,0); // 0x250080 + cmdbuf[1] = (u32)(friendCode & 0xFFFFFFFF); + cmdbuf[2] = (u32)(friendCode >> 32); + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + *principalId = cmdbuf[2]; + + return (Result)cmdbuf[1]; +} + +Result FRD_IsValidFriendCode(u64 friendCode, bool *isValid) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x26,2,0); // 0x260080 + cmdbuf[1] = (u32)(friendCode & 0xFFFFFFFF); + cmdbuf[2] = (u32)(friendCode >> 32); + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + *isValid = cmdbuf[2] & 0xFF; + + return (Result)cmdbuf[1]; +} + +Result FRD_SetClientSdkVersion(u32 sdkVer) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x32,1,2); // 0x320042 + cmdbuf[1] = sdkVer; + cmdbuf[2] = IPC_Desc_CurProcessHandle(); + + if (R_FAILED(ret = svcSendSyncRequest(frdHandle))) return ret; + + return (Result)cmdbuf[1]; +} \ No newline at end of file