diff --git a/nx/include/switch.h b/nx/include/switch.h index 755a0430..8be1a533 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -109,6 +109,8 @@ extern "C" { #include "switch/services/grc.h" #include "switch/services/friends.h" #include "switch/services/notif.h" +#include "switch/services/mii.h" +#include "switch/services/miiimg.h" #include "switch/display/binder.h" #include "switch/display/parcel.h" diff --git a/nx/include/switch/services/mii.h b/nx/include/switch/services/mii.h new file mode 100644 index 00000000..9a6d63d2 --- /dev/null +++ b/nx/include/switch/services/mii.h @@ -0,0 +1,176 @@ +/** + * @file mii.h + * @brief Mii services (mii:*) IPC wrapper. + * @author XorTroll + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" +#include "../sf/service.h" + +typedef enum { + MiiServiceType_System = 0, ///< Initializes mii:e. + MiiServiceType_User = 1, ///< Initializes mii:u. +} MiiServiceType; + +/// Mii age. +typedef enum { + MiiAge_Young = 0, ///< Young + MiiAge_Normal = 1, ///< Normal + MiiAge_Old = 2, ///< Old + MiiAge_All = 3, ///< All of them +} MiiAge; + +/// Mii gender. +typedef enum { + MiiGender_Male = 0, ///< Male + MiiGender_Female = 1, ///< Female + MiiGender_All = 2, ///< Both of them +} MiiGender; + +/// Mii face color. +typedef enum { + MiiFaceColor_Black = 0, ///< Black + MiiFaceColor_White = 1, ///< White + MiiFaceColor_Asian = 2, ///< Asian + MiiFaceColor_All = 3, ///< All of them +} MiiFaceColor; + +// Mii source flag. +typedef enum { + MiiSourceFlag_Database = BIT(0), ///< Miis created by the user + MiiSourceFlag_Default = BIT(1), ///< Default console miis + MiiSourceFlag_All = MiiSourceFlag_Database | MiiSourceFlag_Default, ///< All of them +} MiiSourceFlag; + +// Mii special key code +typedef enum { + MiiSpecialKeyCode_Normal = 0, ///< Normal miis + MiiSpecialKeyCode_Special = 0xA523B78F, ///< Special miis +} MiiSpecialKeyCode; + +typedef struct { + Service s; +} MiiDatabase; + +// Mii create ID. +typedef struct { + Uuid uuid; +} MiiCreateId; + +// Mii data structure. +typedef struct { + MiiCreateId create_id; + u16 mii_name[10+1]; ///< utf-16be, null-terminated + u8 unk_x26; + u8 mii_color; + u8 mii_sex; + u8 mii_height; + u8 mii_width; + u8 unk_x2b[2]; + u8 mii_face_shape; + u8 mii_face_color; + u8 mii_wrinkles_style; + u8 mii_makeup_style; + u8 mii_hair_style; + u8 mii_hair_color; + u8 mii_has_hair_flipped; + u8 mii_eye_style; + u8 mii_eye_color; + u8 mii_eye_size; + u8 mii_eye_thickness; + u8 mii_eye_angle; + u8 mii_eye_pos_x; + u8 mii_eye_pos_y; + u8 mii_eyebrow_style; + u8 mii_eyebrow_color; + u8 mii_eyebrow_size; + u8 mii_eyebrow_thickness; + u8 mii_eyebrow_angle; + u8 mii_eyebrow_pos_x; + u8 mii_eyebrow_pos_y; + u8 mii_nose_style; + u8 mii_nose_size; + u8 mii_nose_pos; + u8 mii_mouth_style; + u8 mii_mouth_color; + u8 mii_mouth_size; + u8 mii_mouth_thickness; + u8 mii_mouth_pos; + u8 mii_facial_hair_color; + u8 mii_beard_style; + u8 mii_mustache_style; + u8 mii_mustache_size; + u8 mii_mustache_pos; + u8 mii_glasses_style; + u8 mii_glasses_color; + u8 mii_glasses_size; + u8 mii_glasses_pos; + u8 mii_has_mole; + u8 mii_mole_size; + u8 mii_mole_pos_x; + u8 mii_mole_pos_y; + u8 unk_x57; +} MiiCharInfo; + +/// Initialize mii. +Result miiInitialize(MiiServiceType service_type); + +/// Exit mii. +void miiExit(void); + +/// Gets the Service object for the actual mii service session. +Service* miiGetServiceSession(void); + +/** + * @brief Opens a mii database. + * @param[in] key_code Mii key code filter. + * @param[out] out Database. + */ +Result miiOpenDatabase(MiiDatabase *out, MiiSpecialKeyCode key_code); + +/** + * @brief Returns whether the mii database is updated. + * @param[in] db Database. + * @param[in] flag Source flag. + * @param[out] out_updated Whether the mii database is updated. + */ +Result miiDatabaseIsUpdated(MiiDatabase *db, bool *out_updated, MiiSourceFlag flag); + +/** + * @brief Returns whether the mii database is full. + * @param[in] db Database. + * @param[out] out_full Whether the mii database is full. + */ +Result miiDatabaseIsFull(MiiDatabase *db, bool *out_full); + +/** + * @brief Returns number of miis in the database with the specified source flag. + * @param[in] db Database. + * @param[in] flag Source flag. + * @param[out] out_count Out mii count. + */ +Result miiDatabaseGetCount(MiiDatabase *db, s32 *out_count, MiiSourceFlag flag); + +/** + * @brief Reads mii charinfo data from the specified source flag. + * @param[in] db Database. + * @param[in] flag Source flag. + * @param[out] out_infos Output mii charinfo array. + * @param[in] count Number of mii chainfos to read. + * @param[out] total_out Number of mii charinfos which were actually read. + */ +Result miiDatabaseGet1(MiiDatabase *db, MiiSourceFlag flag, MiiCharInfo *out_infos, s32 count, s32 *total_out); + +/** + * @brief Generates a random mii charinfo (doesn't register it in the console database). + * @param[in] db Database. + * @param[in] age Mii's age. + * @param[in] gender Mii's gender. + * @param[in] face_color Mii's face color. + * @param[out] out_info Out mii charinfo data. + */ +Result miiDatabaseBuildRandom(MiiDatabase *db, MiiAge age, MiiGender gender, MiiFaceColor face_color, MiiCharInfo *out_info); + +/// Closes a mii database. +void miiDatabaseClose(MiiDatabase *db); diff --git a/nx/include/switch/services/miiimg.h b/nx/include/switch/services/miiimg.h new file mode 100644 index 00000000..f269a488 --- /dev/null +++ b/nx/include/switch/services/miiimg.h @@ -0,0 +1,71 @@ +/** + * @file miiimg.h + * @brief Mii image (miiimg) service IPC wrapper. + * @author XorTroll + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" +#include "../sf/service.h" +#include "../services/mii.h" + +/// Image ID. +typedef struct { + Uuid uuid; +} MiiimgImageId; + +/// Image attribute. +typedef struct { + MiiimgImageId image_id; ///< Image ID. + MiiCreateId create_id; ///< Mii's create ID. + u32 unk; + u16 mii_name[10+1]; ///< utf-16be, null-terminated +} PACKED MiiimgImageAttribute; + +/// Initialize miiimg. +Result miiimgInitialize(void); + +/// Exit miiimg. +void miiimgExit(void); + +/// Gets the Service object for the actual miiimg service session. +Service* miiimgGetServiceSession(void); + +/** + * @brief Reloads the image database. + */ +Result miiimgReload(void); + +/** + * @brief Gets the number of mii images in the database. + * @param[out] out_count Mii image count. + */ +Result miiimgGetCount(s32 *out_count); + +/** + * @brief Gets whether the image database is empty. + * @param[out] out_empty Whether the database is empty. + */ +Result miiimgIsEmpty(bool *out_empty); + +/** + * @brief Gets whether the image database is full. + * @param[out] out_empty Whether the database is full. + */ +Result miiimgIsFull(bool *out_full); + +/** + * @brief Gets the image attribute for the specified image index. + * @param[in] index Image index. + * @param[out] out_attr Out image attribute. + */ +Result miiimgGetAttribute(s32 index, MiiimgImageAttribute *out_attr); + +/** + * @brief Loads the image data (raw RGBA8) for the specified image ID. + * @note Server doesn't seem to check the image buffer size, but 0x40000 is the optimal size. + * @param[in] id Input image ID. + * @param[out] out_image Out iamge buffer. + * @param[in] out_image_size Out image buffer size. + */ +Result miiimgLoadImage(MiiimgImageId id, void* out_image, size_t out_image_size); diff --git a/nx/include/switch/services/nfc.h b/nx/include/switch/services/nfc.h index 89f3612a..f5725f66 100644 --- a/nx/include/switch/services/nfc.h +++ b/nx/include/switch/services/nfc.h @@ -8,6 +8,7 @@ #pragma once #include "../types.h" #include "../sf/service.h" +#include "../services/mii.h" /// NfpServiceType typedef enum { @@ -72,61 +73,7 @@ typedef struct { } PACKED NfpModelInfo; typedef struct { - u8 unk_x0[0x10]; // Hash? - u16 mii_name[10+1]; ///< utf-16be, null-terminated - u8 unk_x26; - u8 mii_color; - u8 mii_sex; - u8 mii_height; - u8 mii_width; - u8 unk_x2b[2]; - u8 mii_face_shape; - u8 mii_face_color; - u8 mii_wrinkles_style; - u8 mii_makeup_style; - u8 mii_hair_style; - u8 mii_hair_color; - u8 mii_has_hair_flipped; - u8 mii_eye_style; - u8 mii_eye_color; - u8 mii_eye_size; - u8 mii_eye_thickness; - u8 mii_eye_angle; - u8 mii_eye_pos_x; - u8 mii_eye_pos_y; - u8 mii_eyebrow_style; - u8 mii_eyebrow_color; - u8 mii_eyebrow_size; - u8 mii_eyebrow_thickness; - u8 mii_eyebrow_angle; - u8 mii_eyebrow_pos_x; - u8 mii_eyebrow_pos_y; - u8 mii_nose_style; - u8 mii_nose_size; - u8 mii_nose_pos; - u8 mii_mouth_style; - u8 mii_mouth_color; - u8 mii_mouth_size; - u8 mii_mouth_thickness; - u8 mii_mouth_pos; - u8 mii_facial_hair_color; - u8 mii_beard_style; - u8 mii_mustache_style; - u8 mii_mustache_size; - u8 mii_mustache_pos; - u8 mii_glasses_style; - u8 mii_glasses_color; - u8 mii_glasses_size; - u8 mii_glasses_pos; - u8 mii_has_mole; - u8 mii_mole_size; - u8 mii_mole_pos_x; - u8 mii_mole_pos_y; - u8 unk_x57; -} PACKED NfpMiiCharInfo; - -typedef struct { - NfpMiiCharInfo mii; + MiiCharInfo mii; u16 first_write_year; u8 first_write_month; u8 first_write_day; diff --git a/nx/source/services/mii.c b/nx/source/services/mii.c new file mode 100644 index 00000000..b15bb051 --- /dev/null +++ b/nx/source/services/mii.c @@ -0,0 +1,80 @@ +#define NX_SERVICE_ASSUME_NON_DOMAIN +#include "service_guard.h" +#include "services/mii.h" + +static MiiServiceType g_miiServiceType; +static Service g_miiSrv; + +NX_GENERATE_SERVICE_GUARD_PARAMS(mii, (MiiServiceType service_type), (service_type)); + +Result _miiInitialize(MiiServiceType service_type) { + Result rc = MAKERESULT(Module_Libnx, LibnxError_BadInput); + + g_miiServiceType = service_type; + switch (g_miiServiceType) { + case MiiServiceType_System: + rc = smGetService(&g_miiSrv, "mii:e"); + break; + case MiiServiceType_User: + rc = smGetService(&g_miiSrv, "mii:u"); + break; + } + + return rc; +} + +void _miiCleanup(void) { + serviceClose(&g_miiSrv); +} + +Service* miiGetServiceSession(void) { + return &g_miiSrv; +} + +Result miiOpenDatabase(MiiDatabase *out, MiiSpecialKeyCode key_code) { + u32 in = (u32)key_code; + return serviceDispatchIn(&g_miiSrv, 0, in, + .out_num_objects = 1, + .out_objects = &out->s, + ); +} + +Result miiDatabaseIsUpdated(MiiDatabase *db, bool *out_updated, MiiSourceFlag flag) { + u32 in = (u32)flag; + u8 tmp = 0; + Result rc = serviceDispatchInOut(&db->s, 0, in, tmp); + if (R_SUCCEEDED(rc) && out_updated) *out_updated = tmp & 1; + return rc; +} + +Result miiDatabaseIsFull(MiiDatabase *db, bool *out_full) { + u8 tmp = 0; + Result rc = serviceDispatchOut(&db->s, 1, tmp); + if (R_SUCCEEDED(rc) && out_full) *out_full = tmp & 1; + return rc; +} +Result miiDatabaseGetCount(MiiDatabase *db, s32 *out_count, MiiSourceFlag flag) { + u32 in = (u32)flag; + return serviceDispatchInOut(&db->s, 2, in, *out_count); +} + +Result miiDatabaseGet1(MiiDatabase *db, MiiSourceFlag flag, MiiCharInfo *out_infos, s32 count, s32 *total_out) { + u32 in = (u32)flag; + return serviceDispatchInOut(&db->s, 4, in, *total_out, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { out_infos, count * sizeof(MiiCharInfo) } }, + ); +} + +Result miiDatabaseBuildRandom(MiiDatabase *db, MiiAge age, MiiGender gender, MiiFaceColor face_color, MiiCharInfo *out_info) { + const struct { + u32 age; + u32 gender; + u32 face_color; + } in = { age, gender, face_color }; + return serviceDispatchInOut(&db->s, 6, in, *out_info); +} + +void miiDatabaseClose(MiiDatabase *db) { + serviceClose(&db->s); +} diff --git a/nx/source/services/miiimg.c b/nx/source/services/miiimg.c new file mode 100644 index 00000000..d1c34fb6 --- /dev/null +++ b/nx/source/services/miiimg.c @@ -0,0 +1,64 @@ +#define NX_SERVICE_ASSUME_NON_DOMAIN +#include "service_guard.h" +#include "services/miiimg.h" +#include "runtime/hosversion.h" + +static Service g_miiimgSrv; + +NX_GENERATE_SERVICE_GUARD(miiimg); + +static Result _miiimgInitializeCmd(u8 in, u8 *out) { + return serviceDispatchInOut(&g_miiimgSrv, 0, in, *out); +} + +Result _miiimgInitialize(void) { + if (hosversionBefore(5,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + Result rc = smGetService(&g_miiimgSrv, "miiimg"); + if (R_SUCCEEDED(rc)) { + u8 tmp; + rc = _miiimgInitializeCmd(1, &tmp); + } + return rc; +} + +void _miiimgCleanup(void) { + serviceClose(&g_miiimgSrv); +} + +Service* miiimgGetServiceSession(void) { + return &g_miiimgSrv; +} + +Result miiimgReload(void) { + return serviceDispatch(&g_miiimgSrv, 10); +} + +Result miiimgGetCount(s32 *out_count) { + return serviceDispatchOut(&g_miiimgSrv, 11, *out_count); +} + +Result miiimgIsEmpty(bool *out_empty) { + u8 tmp = 0; + Result rc = serviceDispatchOut(&g_miiimgSrv, 12, tmp); + if (R_SUCCEEDED(rc) && out_empty) *out_empty = tmp & 1; + return rc; +} + +Result miiimgIsFull(bool *out_full) { + u8 tmp = 0; + Result rc = serviceDispatchOut(&g_miiimgSrv, 13, tmp); + if (R_SUCCEEDED(rc) && out_full) *out_full = tmp & 1; + return rc; +} + +Result miiimgGetAttribute(s32 index, MiiimgImageAttribute *out_attr) { + return serviceDispatchInOut(&g_miiimgSrv, 14, index, *out_attr); +} + +Result miiimgLoadImage(MiiimgImageId id, void* out_image, size_t out_image_size) { + return serviceDispatchIn(&g_miiimgSrv, 15, id, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { out_image, out_image_size } }, + ); +}