From 1492d73ccb9447f8cb165b0486e863be370098ee Mon Sep 17 00:00:00 2001 From: Tobias Date: Sat, 30 Sep 2023 05:27:15 +0200 Subject: [PATCH] frd: Stub several functions (#7010) * mii: Improve mii data variable naming * frd: Stub several functions Allows the friend applet to open successfully. * frd: Address review comments --- src/core/hle/applets/mii_selector.cpp | 4 +- src/core/hle/mii.h | 77 +++++++++++---------- src/core/hle/service/frd/frd.cpp | 98 +++++++++++++++++++++++++-- src/core/hle/service/frd/frd.h | 83 ++++++++++++++++++++++- src/core/hle/service/frd/frd_a.cpp | 15 ++-- src/core/hle/service/frd/frd_u.cpp | 14 ++-- 6 files changed, 231 insertions(+), 60 deletions(-) diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp index 573f9d0526..7be3e0b460 100644 --- a/src/core/hle/applets/mii_selector.cpp +++ b/src/core/hle/applets/mii_selector.cpp @@ -90,7 +90,7 @@ MiiResult MiiSelector::GetStandardMiiResult() { // the LLEd Mii picker of version system version 11.8.0 to a file and then matching the values // to the members of the MiiResult struct Mii::MiiData mii_data; - mii_data.magic = 0x03; + mii_data.version = 0x03; mii_data.mii_options.raw = 0x00; mii_data.mii_pos.raw = 0x10; mii_data.console_identity.raw = 0x30; @@ -114,7 +114,7 @@ MiiResult MiiSelector::GetStandardMiiResult() { mii_data.beard_details.raw = 0x0029; mii_data.glasses_details.raw = 0x0052; mii_data.mole_details.raw = 0x4850; - mii_data.author_name = {'f', 'l', 'T', 'o', 'b', 'i', 0x0, 0x0, 0x0, 0x0}; + mii_data.author_name = {u'f', u'l', u'T', u'o', u'b', u'i'}; MiiResult result; result.return_code = 0x0; diff --git a/src/core/hle/mii.h b/src/core/hle/mii.h index bd5784131b..b15fb141c3 100644 --- a/src/core/hle/mii.h +++ b/src/core/hle/mii.h @@ -11,10 +11,12 @@ namespace Mii { +using Nickname = std::array; + #pragma pack(push, 1) // Reference: https://github.com/devkitPro/libctru/blob/master/libctru/include/3ds/mii.h struct MiiData { - u8 magic; ///< Always 3? + u8 version; ///< Always 3? /// Mii options union { @@ -52,23 +54,23 @@ struct MiiData { union { u16_be raw; - BitField<0, 1, u16> sex; ///< Sex of Mii (False=Male, True=Female) - BitField<1, 4, u16> bday_month; ///< Month of Mii's birthday - BitField<5, 5, u16> bday_day; ///< Day of Mii's birthday - BitField<10, 4, u16> shirt_color; ///< Color of Mii's shirt - BitField<14, 1, u16> favorite; ///< Whether the Mii is one of your 10 favorite Mii's + BitField<0, 1, u16> gender; ///< Gender of Mii (0=Male, 1=Female) + BitField<1, 4, u16> bday_month; ///< Month of Mii's birthday + BitField<5, 5, u16> bday_day; ///< Day of Mii's birthday + BitField<10, 4, u16> favorite_color; ///< Color of Mii's shirt + BitField<14, 1, u16> favorite; ///< Whether the Mii is one of your 10 favorite Mii's } mii_details; - std::array mii_name; ///< Name of Mii (Encoded using UTF16) - u8 height; ///< How tall the Mii is - u8 width; ///< How wide the Mii is + Nickname mii_name; ///< Name of Mii (Encoded using UTF16) + u8 height; ///< How tall the Mii is + u8 width; ///< How wide the Mii is /// Face style union { u8 raw; BitField<0, 1, u8> disable_sharing; ///< Whether or not Sharing of the Mii is allowed - BitField<1, 4, u8> shape; ///< Face shape + BitField<1, 4, u8> type; ///< Face type BitField<5, 3, u8> skin_color; ///< Color of skin } face_style; @@ -94,13 +96,13 @@ struct MiiData { union { u32_be raw; - BitField<0, 6, u32> style; + BitField<0, 6, u32> type; BitField<6, 3, u32> color; BitField<9, 4, u32> scale; - BitField<13, 3, u32> y_scale; - BitField<16, 5, u32> rotation; - BitField<21, 4, u32> x_spacing; - BitField<25, 5, u32> y_position; + BitField<13, 3, u32> aspect; + BitField<16, 5, u32> rotate; + BitField<21, 4, u32> x; + BitField<25, 5, u32> y; } eye_details; /// Eyebrow details @@ -110,72 +112,70 @@ struct MiiData { BitField<0, 5, u32> style; BitField<5, 3, u32> color; BitField<8, 4, u32> scale; - BitField<12, 3, u32> y_scale; - BitField<15, 1, u32> pad; - BitField<16, 5, u32> rotation; - BitField<21, 4, u32> x_spacing; - BitField<25, 5, u32> y_position; + BitField<12, 3, u32> aspect; + BitField<16, 5, u32> rotate; + BitField<21, 4, u32> x; + BitField<25, 5, u32> y; } eyebrow_details; /// Nose details union { u16_be raw; - BitField<0, 5, u16> style; + BitField<0, 5, u16> type; BitField<5, 4, u16> scale; - BitField<9, 5, u16> y_position; + BitField<9, 5, u16> y; } nose_details; /// Mouth details union { u16_be raw; - BitField<0, 6, u16> style; + BitField<0, 6, u16> type; BitField<6, 3, u16> color; BitField<9, 4, u16> scale; - BitField<13, 3, u16> y_scale; + BitField<13, 3, u16> aspect; } mouth_details; /// Mustache details union { u16_be raw; - BitField<0, 5, u16> mouth_yposition; - BitField<5, 3, u16> mustach_style; - BitField<8, 2, u16> pad; + BitField<0, 5, u16> mouth_y; + BitField<5, 3, u16> mustache_type; } mustache_details; /// Beard details union { u16_be raw; - BitField<0, 3, u16> style; + BitField<0, 3, u16> type; BitField<3, 3, u16> color; BitField<6, 4, u16> scale; - BitField<10, 5, u16> y_pos; + BitField<10, 5, u16> y; } beard_details; /// Glasses details union { u16_be raw; - BitField<0, 4, u16> style; + BitField<0, 4, u16> type; BitField<4, 3, u16> color; BitField<7, 4, u16> scale; - BitField<11, 5, u16> y_pos; + BitField<11, 5, u16> y; } glasses_details; /// Mole details union { u16_be raw; - BitField<0, 1, u16> enable; - BitField<1, 5, u16> scale; - BitField<6, 5, u16> x_pos; - BitField<11, 5, u16> y_pos; + BitField<0, 1, u16> type; + BitField<1, 4, u16> scale; + BitField<5, 5, u16> x; + BitField<10, 5, u16> y; } mole_details; - std::array author_name; ///< Name of Mii's author (Encoded using UTF16) + Nickname author_name; ///< Name of Mii's author (Encoded using UTF16) private: template void serialize(Archive& ar, const unsigned int) { @@ -209,6 +209,11 @@ public: return *this; } + void SetMiiData(MiiData data) { + mii_data = data; + FixChecksum(); + } + operator MiiData() const { return mii_data; } diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp index be8bdb8f58..bae62a30e2 100644 --- a/src/core/hle/service/frd/frd.cpp +++ b/src/core/hle/service/frd/frd.cpp @@ -9,8 +9,10 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/applets/mii_selector.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" +#include "core/hle/mii.h" #include "core/hle/result.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/frd/frd.h" @@ -97,11 +99,6 @@ void Module::Interface::GetMyScreenName(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); IPC::RequestBuilder rb = rp.MakeBuilder(7, 0); - struct ScreenName { - // 20 bytes according to 3dbrew - std::array name; - }; - auto cfg = Service::CFG::GetModule(frd->system); ASSERT_MSG(cfg, "CFG Module missing!"); auto username = cfg->GetUsername(); @@ -113,7 +110,86 @@ void Module::Interface::GetMyScreenName(Kernel::HLERequestContext& ctx) { rb.PushRaw(screen_name); rb.Push(0); - LOG_INFO(Service_FRD, "returning the username defined in cfg"); + LOG_DEBUG(Service_FRD, "called"); +} + +void Module::Interface::GetMyComment(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + IPC::RequestBuilder rb = rp.MakeBuilder(10, 0); + + constexpr Comment comment{.name = {u'H', u'e', u'y', '!'}}; + + rb.Push(RESULT_SUCCESS); + rb.PushRaw(comment); + rb.Push(0); + + LOG_WARNING(Service_FRD, "(STUBBED) called"); +} + +void Module::Interface::GetMyMii(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + IPC::RequestBuilder rb = rp.MakeBuilder(0x19, 0); + + const auto mii_data = HLE::Applets::MiiSelector::GetStandardMiiResult().selected_mii_data; + Mii::ChecksummedMiiData mii{}; + mii.SetMiiData(mii_data); + + rb.Push(RESULT_SUCCESS); + rb.PushRaw(mii); + + LOG_WARNING(Service_FRD, "(STUBBED) called"); +} + +void Module::Interface::GetMyProfile(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); + + constexpr Profile profile{.region = 1, .country = 1, .area = 1, .language = 1, .platform = 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushRaw(profile); + + LOG_WARNING(Service_FRD, "(STUBBED) called"); +} + +void Module::Interface::GetMyFavoriteGame(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); + + constexpr Game game{.title_id = 0x0004000E00030700, .version = 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushRaw(game); + + LOG_WARNING(Service_FRD, "(STUBBED) called"); +} + +void Module::Interface::GetMyPlayingGame(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); + + constexpr Game game{.title_id = 0x0004000E00030700, .version = 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushRaw(game); + + LOG_WARNING(Service_FRD, "(STUBBED) called"); +} + +void Module::Interface::GetMyPreference(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + IPC::RequestBuilder rb = rp.MakeBuilder(4, 0); + + constexpr u32 is_public = 1; + constexpr u32 show_game = 1; + constexpr u32 show_history = 0; + + rb.Push(RESULT_SUCCESS); + rb.Push(is_public); + rb.Push(show_game); + rb.Push(show_history); + + LOG_WARNING(Service_FRD, "(STUBBED) called"); } void Module::Interface::UnscrambleLocalFriendCode(Kernel::HLERequestContext& ctx) { @@ -158,6 +234,16 @@ void Module::Interface::SetClientSdkVersion(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_FRD, "(STUBBED) called, version: 0x{:08X}", version); } +void Module::Interface::IsOnline(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + + rb.Push(RESULT_SUCCESS); + rb.Push(frd->logged_in); + + LOG_WARNING(Service_FRD, "(STUBBED) called"); +} + void Module::Interface::HasLoggedIn(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_FRD, "(STUBBED) called"); diff --git a/src/core/hle/service/frd/frd.h b/src/core/hle/service/frd/frd.h index 064e7f7f1c..bb5199cb72 100644 --- a/src/core/hle/service/frd/frd.h +++ b/src/core/hle/service/frd/frd.h @@ -49,8 +49,29 @@ struct Profile { u8 country; u8 area; u8 language; - u32 unknown; + u8 platform; + INSERT_PADDING_BYTES(0x3); }; +static_assert(sizeof(Profile) == 0x8, "Profile has incorrect size"); + +struct Game { + u64 title_id; + u16 version; + INSERT_PADDING_BYTES(0x6); +}; +static_assert(sizeof(Game) == 0x10, "Game has inccorect size"); + +struct ScreenName { + // 20 bytes according to 3dbrew + std::array name; +}; +static_assert(sizeof(ScreenName) == 0x14, "ScreenName has inccorect size"); + +struct Comment { + // 32 bytes according to 3dbrew + std::array name; +}; +static_assert(sizeof(Comment) == 0x20, "Comment has inccorect size"); class Module final { public: @@ -128,6 +149,56 @@ public: */ void GetMyScreenName(Kernel::HLERequestContext& ctx); + /** + * FRD::GetMyMii service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : MiiStoreData structure + */ + void GetMyMii(Kernel::HLERequestContext& ctx); + + /** + * FRD::GetMyProfile service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-3 : Profile structure + */ + void GetMyProfile(Kernel::HLERequestContext& ctx); + + /** + * FRD::GetMyComment service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : UTF16 encoded comment (max 16 symbols) + */ + void GetMyComment(Kernel::HLERequestContext& ctx); + + /** + * FRD::GetMyFavoriteGame service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-3 : Game structure + */ + void GetMyFavoriteGame(Kernel::HLERequestContext& ctx); + + /** + * FRD::GetMyPlayingGame service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-3 : Game structure + */ + void GetMyPlayingGame(Kernel::HLERequestContext& ctx); + + /** + * FRD::GetMyPreference service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Public mode (byte, 0 = private, non-zero = public) + * 3 : Show current game (byte, 0 = don't show, non-zero = show) + * 4 : Show game history (byte, 0 = don't show, non-zero = show) + */ + void GetMyPreference(Kernel::HLERequestContext& ctx); + /** * FRD::UnscrambleLocalFriendCode service function * Inputs: @@ -160,10 +231,18 @@ public: void Login(Kernel::HLERequestContext& ctx); /** - * FRD::HasLoggedIn service function + * FRD::IsOnline service function * Inputs: * none * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Online state (8-bit, 0 = not online, non-zero = online) + */ + void IsOnline(Kernel::HLERequestContext& ctx); + + /** + * FRD::HasLoggedIn service function + * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 2 : If the user has logged in 1, otherwise 0 */ diff --git a/src/core/hle/service/frd/frd_a.cpp b/src/core/hle/service/frd/frd_a.cpp index f0d95e8da8..64ea53dc6b 100644 --- a/src/core/hle/service/frd/frd_a.cpp +++ b/src/core/hle/service/frd/frd_a.cpp @@ -12,20 +12,20 @@ namespace Service::FRD { FRD_A::FRD_A(std::shared_ptr frd) : Module::Interface(std::move(frd), "frd:a", 8) { static const FunctionInfo functions[] = { {0x0001, &FRD_A::HasLoggedIn, "HasLoggedIn"}, - {0x0002, nullptr, "IsOnline"}, + {0x0002, &FRD_A::IsOnline, "IsOnline"}, {0x0003, &FRD_A::Login, "Login"}, {0x0004, nullptr, "Logout"}, {0x0005, &FRD_A::GetMyFriendKey, "GetMyFriendKey"}, - {0x0006, nullptr, "GetMyPreference"}, - {0x0007, nullptr, "GetMyProfile"}, + {0x0006, &FRD_A::GetMyPreference, "GetMyPreference"}, + {0x0007, &FRD_A::GetMyProfile, "GetMyProfile"}, {0x0008, &FRD_A::GetMyPresence, "GetMyPresence"}, {0x0009, &FRD_A::GetMyScreenName, "GetMyScreenName"}, - {0x000A, nullptr, "GetMyMii"}, + {0x000A, &FRD_A::GetMyMii, "GetMyMii"}, {0x000B, nullptr, "GetMyLocalAccountId"}, - {0x000C, nullptr, "GetMyPlayingGame"}, - {0x000D, nullptr, "GetMyFavoriteGame"}, + {0x000C, &FRD_A::GetMyPlayingGame, "GetMyPlayingGame"}, + {0x000D, &FRD_A::GetMyFavoriteGame, "GetMyFavoriteGame"}, {0x000E, nullptr, "GetMyNcPrincipalId"}, - {0x000F, nullptr, "GetMyComment"}, + {0x000F, &FRD_A::GetMyComment, "GetMyComment"}, {0x0010, nullptr, "GetMyPassword"}, {0x0011, &FRD_A::GetFriendKeyList, "GetFriendKeyList"}, {0x0012, nullptr, "GetFriendPresence"}, @@ -64,6 +64,7 @@ FRD_A::FRD_A(std::shared_ptr frd) : Module::Interface(std::move(frd), "f {0x0033, nullptr, "GetMyApproachContext"}, {0x0034, nullptr, "AddFriendWithApproach"}, {0x0035, nullptr, "DecryptApproachContext"}, + {0x0405, nullptr, "SaveData"}, {0x0406, nullptr, "AddFriendOnline"}, {0x0409, nullptr, "RemoveFriend"}, {0x040a, nullptr, "UpdatePlayingGame"}, diff --git a/src/core/hle/service/frd/frd_u.cpp b/src/core/hle/service/frd/frd_u.cpp index 33b6b6f3f4..ed545c808d 100644 --- a/src/core/hle/service/frd/frd_u.cpp +++ b/src/core/hle/service/frd/frd_u.cpp @@ -12,20 +12,20 @@ namespace Service::FRD { FRD_U::FRD_U(std::shared_ptr frd) : Module::Interface(std::move(frd), "frd:u", 8) { static const FunctionInfo functions[] = { {0x0001, &FRD_U::HasLoggedIn, "HasLoggedIn"}, - {0x0002, nullptr, "IsOnline"}, + {0x0002, &FRD_U::IsOnline, "IsOnline"}, {0x0003, &FRD_U::Login, "Login"}, {0x0004, nullptr, "Logout"}, {0x0005, &FRD_U::GetMyFriendKey, "GetMyFriendKey"}, - {0x0006, nullptr, "GetMyPreference"}, - {0x0007, nullptr, "GetMyProfile"}, + {0x0006, &FRD_U::GetMyPreference, "GetMyPreference"}, + {0x0007, &FRD_U::GetMyProfile, "GetMyProfile"}, {0x0008, &FRD_U::GetMyPresence, "GetMyPresence"}, {0x0009, &FRD_U::GetMyScreenName, "GetMyScreenName"}, - {0x000A, nullptr, "GetMyMii"}, + {0x000A, &FRD_U::GetMyMii, "GetMyMii"}, {0x000B, nullptr, "GetMyLocalAccountId"}, - {0x000C, nullptr, "GetMyPlayingGame"}, - {0x000D, nullptr, "GetMyFavoriteGame"}, + {0x000C, &FRD_U::GetMyPlayingGame, "GetMyPlayingGame"}, + {0x000D, &FRD_U::GetMyFavoriteGame, "GetMyFavoriteGame"}, {0x000E, nullptr, "GetMyNcPrincipalId"}, - {0x000F, nullptr, "GetMyComment"}, + {0x000F, &FRD_U::GetMyComment, "GetMyComment"}, {0x0010, nullptr, "GetMyPassword"}, {0x0011, &FRD_U::GetFriendKeyList, "GetFriendKeyList"}, {0x0012, nullptr, "GetFriendPresence"},