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
This commit is contained in:
parent
60d815fada
commit
1492d73ccb
@ -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;
|
||||
|
@ -11,10 +11,12 @@
|
||||
|
||||
namespace Mii {
|
||||
|
||||
using Nickname = std::array<char16_t, 10>;
|
||||
|
||||
#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<u16_le, 10> 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<u16_le, 10> author_name; ///< Name of Mii's author (Encoded using UTF16)
|
||||
Nickname author_name; ///< Name of Mii's author (Encoded using UTF16)
|
||||
private:
|
||||
template <class Archive>
|
||||
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;
|
||||
}
|
||||
|
@ -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<char16_t, 10> 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>(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::ChecksummedMiiData>(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>(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>(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>(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<u32>(is_public);
|
||||
rb.Push<u32>(show_game);
|
||||
rb.Push<u32>(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");
|
||||
|
||||
|
@ -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<char16_t, 10> name;
|
||||
};
|
||||
static_assert(sizeof(ScreenName) == 0x14, "ScreenName has inccorect size");
|
||||
|
||||
struct Comment {
|
||||
// 32 bytes according to 3dbrew
|
||||
std::array<char16_t, 16> 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
|
||||
*/
|
||||
|
@ -12,20 +12,20 @@ namespace Service::FRD {
|
||||
FRD_A::FRD_A(std::shared_ptr<Module> 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<Module> 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"},
|
||||
|
@ -12,20 +12,20 @@ namespace Service::FRD {
|
||||
FRD_U::FRD_U(std::shared_ptr<Module> 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"},
|
||||
|
Loading…
Reference in New Issue
Block a user