From d3ea73707145b9966196d0a68a07b90a6731fd6e Mon Sep 17 00:00:00 2001 From: Oreo639 <31916379+Oreo639@users.noreply.github.com> Date: Fri, 3 May 2019 16:40:00 -0700 Subject: [PATCH] Add miiSelector configuration functions (#429) --- libctru/include/3ds/applets/miiselector.h | 125 +++++++++++++++++--- libctru/include/3ds/mii.h | 104 +++++++++++------ libctru/source/applets/miiselector.c | 136 ++++++++++++++++++++-- 3 files changed, 308 insertions(+), 57 deletions(-) diff --git a/libctru/include/3ds/applets/miiselector.h b/libctru/include/3ds/applets/miiselector.h index 1012370..cab0698 100644 --- a/libctru/include/3ds/applets/miiselector.h +++ b/libctru/include/3ds/applets/miiselector.h @@ -22,25 +22,25 @@ /// Parameter structure passed to AppletEd typedef struct { - char enable_cancel_button; ///< Enables canceling of selection if nonzero. - char enable_selecting_guests; ///< Makes Guets Miis selectable if nonzero. - char show_on_top_screen; ///< Shows applet on top screen if nonzero, + u8 enable_cancel_button; ///< Enables canceling of selection if nonzero. + u8 enable_selecting_guests; ///< Makes Guets Miis selectable if nonzero. + u8 show_on_top_screen; ///< Shows applet on top screen if nonzero, ///< otherwise show it on the bottom screen. - char _unk0x3[5]; ///< @private + u8 _unk0x3[5]; ///< @private u16 title[MIISELECTOR_TITLE_LEN]; ///< UTF16-LE string displayed at the top of the applet. If ///< set to the empty string, a default title is displayed. - char _unk0x88[4]; ///< @private - char show_guest_page; ///< If nonzero, the applet shows a page with Guest + u8 _unk0x88[4]; ///< @private + u8 show_guest_page; ///< If nonzero, the applet shows a page with Guest ///< Miis on launch. - char _unk0x8D[3]; ///< @private + u8 _unk0x8D[3]; ///< @private u32 initial_index; ///< Index of the initially selected Mii. If ///< @ref MiiSelectorConf.show_guest_page is ///< set, this is the index of a Guest Mii, ///< otherwise that of a user Mii. - char mii_guest_whitelist[MIISELECTOR_GUESTMII_SLOTS]; ///< Each byte set to a nonzero value + u8 mii_guest_whitelist[MIISELECTOR_GUESTMII_SLOTS]; ///< Each byte set to a nonzero value ///< enables its corresponding Guest ///< Mii to be enabled for selection. - char mii_whitelist[MIISELECTOR_USERMII_SLOTS]; ///< Each byte set to a nonzero value enables + u8 mii_whitelist[MIISELECTOR_USERMII_SLOTS]; ///< Each byte set to a nonzero value enables ///< its corresponding user Mii to be enabled ///< for selection. u16 _unk0xFE; ///< @private @@ -48,9 +48,6 @@ typedef struct ///< applet. } MiiSelectorConf; -/// Size of the data describing a single Mii -#define MIISELECTOR_MIIDATA_SIZE 0x5c - /// Maximum length of the localized name of a Guest Mii #define MIISELECTOR_GUESTMII_NAME_LEN 12 @@ -73,6 +70,21 @@ typedef struct ///< string). Zeroed otherwise. } MiiSelectorReturn; +/// AppletEd options +enum +{ + MIISELECTOR_CANCEL = BIT(0), ///< Show the cancel button + MIISELECTOR_GUESTS = BIT(1), ///< Make Guets Miis selectable + MIISELECTOR_TOP = BIT(2), ///< Show AppletEd on top screen + MIISELECTOR_GUESTSTART = BIT(3), ///< Start on guest page +}; + +/** + * @brief Initialize Mii selector config + * @param conf Pointer to Miiselector config. + */ +void miiSelectorInit(MiiSelectorConf *conf); + /** * @brief Launch the Mii selector library applet * @@ -81,12 +93,97 @@ typedef struct */ Result miiSelectorLaunch(const MiiSelectorConf *conf, MiiSelectorReturn* returnbuf); +/** + * @brief Sets title of the Mii selector library applet + * + * @param conf Pointer to miiSelector configuration + * @param text Title text of Mii selector + */ +void miiSelectorSetTitle(MiiSelectorConf *conf, const char* text); + +/** + * @brief Specifies which special options are enabled in the Mii selector + * + * @param conf Pointer to miiSelector configuration + * @param options Options bitmask + */ +void miiSelectorSetOptions(MiiSelectorConf *conf, u32 options); + +/** + * @brief Specifies which guest Miis will be selectable + * + * @param conf Pointer to miiSelector configuration + * @param index Index of the guest Miis that will be whitelisted. + * @ref MIISELECTOR_GUESTMII_SLOTS can be used to whitelist all the guest Miis. + */ +void miiSelectorWhitelistGuestMii(MiiSelectorConf *conf, u32 index); + +/** + * @brief Specifies which guest Miis will be unselectable + * + * @param conf Pointer to miiSelector configuration + * @param index Index of the guest Miis that will be blacklisted. + * @ref MIISELECTOR_GUESTMII_SLOTS can be used to blacklist all the guest Miis. + */ +void miiSelectorBlacklistGuestMii(MiiSelectorConf *conf, u32 index); + +/** + * @brief Specifies which user Miis will be selectable + * + * @param conf Pointer to miiSelector configuration + * @param index Index of the user Miis that will be whitelisted. + * @ref MIISELECTOR_USERMII_SLOTS can be used to whitlist all the user Miis + */ +void miiSelectorWhitelistUserMii(MiiSelectorConf *conf, u32 index); + +/** + * @brief Specifies which user Miis will be selectable + * + * @param conf Pointer to miiSelector configuration + * @param index Index of the user Miis that will be blacklisted. + * @ref MIISELECTOR_USERMII_SLOTS can be used to blacklist all the user Miis + */ +void miiSelectorBlacklistUserMii(MiiSelectorConf *conf, u32 index); + +/** + * @brief Specifies which Mii the cursor should start from + * + * @param conf Pointer to miiSelector configuration + * @param index Indexed number of the Mii that the cursor will start on. + * If there is no mii with that index, the the cursor will start at the Mii + * with the index 0 (the personal Mii). + */ +static inline void miiSelectorSetInitialIndex(MiiSelectorConf *conf, u32 index) +{ + conf->initial_index = index; +} + +/** + * @brief Get Mii name + * + * @param returnbuf Pointer to miiSelector return + * @param out String containing a Mii's name + * @param max_size Size of string. Since UTF8 characters range in size from 1-3 bytes + * (assuming that no non-BMP characters are used), this value should be 36 (or 30 if you are not + * dealing with guest miis). + */ +void miiSelectorReturnGetName(const MiiSelectorReturn *returnbuf, char* out, size_t max_size); + +/** + * @brief Get Mii Author + * + * @param returnbuf Pointer to miiSelector return + * @param out String containing a Mii's author + * @param max_size Size of string. Since UTF8 characters range in size from 1-3 bytes + * (assuming that no non-BMP characters are used), this value should be 30. + */ +void miiSelectorReturnGetAuthor(const MiiSelectorReturn *returnbuf, char* out, size_t max_size); + /** * @brief Verifies that the Mii data returned from the applet matches its * checksum * - * @param returnbuffer Buffer filled by Mii selector applet - * + * @param returnbuf Buffer filled by Mii selector applet * @return `true` if `returnbuf->checksum` is the same as the one computed from `returnbuf` */ bool miiSelectorChecksumIsValid(const MiiSelectorReturn *returnbuf); diff --git a/libctru/include/3ds/mii.h b/libctru/include/3ds/mii.h index d264e4c..692cecc 100644 --- a/libctru/include/3ds/mii.h +++ b/libctru/include/3ds/mii.h @@ -1,56 +1,84 @@ +/** + * @file mii.h + * @brief Shared Mii struct. + * + * @see https://www.3dbrew.org/wiki/Mii#Mii_format + */ #pragma once #include <3ds/types.h> +/// Shared Mii struct typedef struct { - u8 magic; - + u8 magic; ///< Always 3? + + /// Mii options struct { - bool allow_copying : 1; - bool is_private_name : 1; - u8 region_lock : 2; - u8 char_set : 2; + bool allow_copying : 1; ///< True if copying is allowed + bool is_private_name : 1; ///< Private name? + u8 region_lock : 2; ///< Region lock (0=no lock, 1=JPN, 2=USA, 3=EUR) + u8 char_set : 2; ///< Character set (0=JPN+USA+EUR, 1=CHN, 2=KOR, 3=TWN) } mii_options; - + + /// Mii position in Mii selector or Mii maker struct { - u8 page_index : 4; - u8 slot_index : 4; + u8 page_index : 4; ///< Page index of Mii + u8 slot_index : 4; ///< Slot offset of Mii on its Page } mii_pos; - - u8 console_identity; - u64 system_id; - u32 mii_id; - u8 mac[6]; - u8 pad[2]; - u16 mii_details; - u16 mii_name[10]; - u8 height; - u8 width; - + + /// Console Identity struct { - bool disable_sharing : 1; - u8 shape : 4; - u8 skinColor : 3; + u8 unknown0 : 4; ///< Mabye padding (always seems to be 0)? + u8 origin_console : 3; ///< Console that the Mii was created on (1=WII, 2=DSI, 3=3DS) + } console_identity; + + u64 system_id; ///< Identifies the system that the Mii was created on (Determines pants) + u32 mii_id; ///< ID of Mii + u8 mac[6]; ///< Creator's system's full MAC address + u8 pad[2]; ///< Padding + + /// Mii details + struct { + bool sex : 1; ///< Sex of Mii (False=Male, True=Female) + u16 bday_month : 4; ///< Month of Mii's birthday + u16 bday_day : 5; ///< Day of Mii's birthday + u16 shirt_color : 4; ///< Color of Mii's shirt + bool favorite : 1; ///< Whether the Mii is one of your 10 favorite Mii's + } mii_details; + + u16 mii_name[10]; ///< Name of Mii (Encoded using UTF16) + u8 height; ///< How tall the Mii is + u8 width; ///< How wide the Mii is + + /// Face style + struct + { + bool disable_sharing : 1; ///< Whether or not Sharing of the Mii is allowed + u8 shape : 4; ///< Face shape + u8 skinColor : 3; ///< Color of skin } face_style; - + + /// Face details struct { u8 wrinkles : 4; u8 makeup : 4; } face_details; - + u8 hair_style; - + + /// Hair details struct { u8 color : 3; bool flip : 1; } hair_details; - + + /// Eye details struct { u32 style : 6; @@ -61,18 +89,21 @@ typedef struct u32 xspacing : 4; u32 yposition : 5; } eye_details; - + + /// Eyebrow details struct { - u32 style : 6; + u32 style : 5; u32 color : 3; u32 scale : 4; u32 yscale : 3; + u32 pad : 1; u32 rotation : 5; u32 xspacing : 4; u32 yposition : 5; } eyebrow_details; - + + /// Nose details struct { u16 style : 5; @@ -80,21 +111,24 @@ typedef struct u16 yposition : 5; } nose_details; + /// Mouth details struct { u16 style : 6; u16 color : 3; u16 scale : 4; u16 yscale : 3; - } mouse_details; + } mouth_details; + /// Mustache details struct { - u16 mouse_yposition : 5; + u16 mouth_yposition : 5; u16 mustach_style : 3; u16 pad : 2; } mustache_details; - + + /// Beard details struct { u16 style : 3; @@ -103,6 +137,7 @@ typedef struct u16 ypos : 5; } beard_details; + /// Glasses details struct { u16 style : 4; @@ -111,6 +146,7 @@ typedef struct u16 ypos : 5; } glasses_details; + /// Mole details struct { bool enable : 1; @@ -119,5 +155,5 @@ typedef struct u16 ypos : 5; } mole_details; - u16 author_name[10]; + u16 author_name[10]; ///< Name of Mii's author (Encoded using UTF16) } PACKED MiiData; diff --git a/libctru/source/applets/miiselector.c b/libctru/source/applets/miiselector.c index 365dea2..3f11ac0 100644 --- a/libctru/source/applets/miiselector.c +++ b/libctru/source/applets/miiselector.c @@ -1,11 +1,23 @@ #include <3ds/types.h> #include <3ds/result.h> #include <3ds/services/apt.h> +#include <3ds/util/utf.h> #include <3ds/applets/miiselector.h> #include // for memcpy +void miiSelectorInit(MiiSelectorConf *conf) +{ + memset(conf, 0, sizeof(*conf)); + + for (int i = 0; i < MIISELECTOR_GUESTMII_SLOTS; i ++) + conf->mii_guest_whitelist[i] = 1; + + for (int i = 0; i < MIISELECTOR_USERMII_SLOTS; i ++) + conf->mii_whitelist[i] = 1; +} + Result miiSelectorLaunch(const MiiSelectorConf *conf, MiiSelectorReturn *returnbuf) { union { @@ -17,33 +29,139 @@ Result miiSelectorLaunch(const MiiSelectorConf *conf, MiiSelectorReturn *returnb ctx.config.magic = MIISELECTOR_MAGIC; Result ret = aptLaunchLibraryApplet(APPID_APPLETED, &ctx.config, sizeof(MiiSelectorConf), 0); - if(R_SUCCEEDED(ret) && returnbuf) { + if(R_SUCCEEDED(ret) && returnbuf) memcpy(returnbuf, &ctx.ret, sizeof(MiiSelectorReturn)); - } return ret; } +static void miiSelectorConvertToUTF8(char* out, const u16* in, int max) +{ + 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; + } + + out[units] = 0; +} + +static void miiSelectorConvertToUTF16(u16* out, const char* in, int max) +{ + if (!in || !*in) + { + out[0] = 0; + return; + } + + ssize_t units = utf8_to_utf16(out, (const uint8_t*)in, max); + if (units < 0) + { + out[0] = 0; + return; + } + + out[units] = 0; +} + +void miiSelectorSetTitle(MiiSelectorConf *conf, const char* text) +{ + miiSelectorConvertToUTF16(conf->title, text, MIISELECTOR_TITLE_LEN); +} + +void miiSelectorSetOptions(MiiSelectorConf *conf, u32 options) +{ + static const u8 miiSelectorOptions[] = + { + offsetof(MiiSelectorConf, enable_cancel_button), + offsetof(MiiSelectorConf, enable_selecting_guests), + offsetof(MiiSelectorConf, show_on_top_screen), + offsetof(MiiSelectorConf, show_guest_page), + }; + for (int i = 0; i < sizeof(miiSelectorOptions); i ++) + *((u8*)conf + miiSelectorOptions[i]) = (options & BIT(i)) ? 1 : 0; +} + +void miiSelectorWhitelistGuestMii(MiiSelectorConf *conf, u32 index) +{ + if (index < MIISELECTOR_GUESTMII_SLOTS) + conf->mii_guest_whitelist[index] = 1; + else if (index == MIISELECTOR_GUESTMII_SLOTS) + for (int i = 0; i < MIISELECTOR_GUESTMII_SLOTS; i ++) + conf->mii_guest_whitelist[i] = 1; +} + +void miiSelectorBlacklistGuestMii(MiiSelectorConf *conf, u32 index) +{ + if (index < MIISELECTOR_GUESTMII_SLOTS) + conf->mii_guest_whitelist[index] = 0; + else if (index == MIISELECTOR_GUESTMII_SLOTS) + for (int i = 0; i < MIISELECTOR_GUESTMII_SLOTS; i ++) + conf->mii_guest_whitelist[i] = 0; +} + +void miiSelectorWhitelistUserMii(MiiSelectorConf *conf, u32 index) +{ + if (index < MIISELECTOR_USERMII_SLOTS) + conf->mii_whitelist[index] = 1; + else if (index == MIISELECTOR_USERMII_SLOTS) + for (int i = 0; i < MIISELECTOR_USERMII_SLOTS; i ++) + conf->mii_whitelist[i] = 1; +} + +void miiSelectorBlacklistUserMii(MiiSelectorConf *conf, u32 index) +{ + if (index < MIISELECTOR_USERMII_SLOTS) + conf->mii_whitelist[index] = 0; + else if (index == MIISELECTOR_USERMII_SLOTS) + for (int i = 0; i < MIISELECTOR_USERMII_SLOTS; i ++) + conf->mii_whitelist[i] = 0; +} + +void miiSelectorReturnGetName(const MiiSelectorReturn *returnbuf, char* out, size_t max_size) +{ + if (!out) + return; + + if (returnbuf->guest_mii_was_selected) + miiSelectorConvertToUTF8(out, returnbuf->guest_mii_name, max_size); + else + miiSelectorConvertToUTF8(out, returnbuf->mii.mii_name, max_size); +} + +void miiSelectorReturnGetAuthor(const MiiSelectorReturn *returnbuf, char* out, size_t max_size) +{ + if (!out) + return; + + miiSelectorConvertToUTF8(out, returnbuf->mii.author_name, max_size); +} + static u16 crc16_ccitt(void const *buf, size_t len, uint32_t starting_val) { - if(buf == NULL) { + if (!buf) return -1; - } u8 const *cbuf = buf; u32 crc = starting_val; static const u16 POLY = 0x1021; - for(size_t i = 0; i < len; i++) { - for(int bit = 7; bit >= 0; bit--) { + for (size_t i = 0; i < len; i++) + { + for (int bit = 7; bit >= 0; bit--) crc = ((crc << 1) | ((cbuf[i] >> bit) & 0x1)) ^ (crc & 0x8000 ? POLY : 0); - } } - for(int _ = 0; _ < 16; _++) { + for (int _ = 0; _ < 16; _++) crc = (crc << 1) ^ (crc & 0x8000 ? POLY : 0); - } return (u16)(crc & 0xffff); }