1
0
Fork 1
forked from suyu/suyu

Merge pull request #11480 from german77/mii_service

service: mii: Update implementation Part1
This commit is contained in:
liamwhite 2023-09-13 09:39:16 -04:00 committed by GitHub
commit 5b5c69b8f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 4475 additions and 1952 deletions

View file

@ -584,13 +584,23 @@ add_library(core STATIC
hle/service/lm/lm.h
hle/service/mig/mig.cpp
hle/service/mig/mig.h
hle/service/mii/types/char_info.cpp
hle/service/mii/types/char_info.h
hle/service/mii/types/core_data.cpp
hle/service/mii/types/core_data.h
hle/service/mii/types/raw_data.cpp
hle/service/mii/types/raw_data.h
hle/service/mii/types/store_data.cpp
hle/service/mii/types/store_data.h
hle/service/mii/types/ver3_store_data.cpp
hle/service/mii/types/ver3_store_data.h
hle/service/mii/mii.cpp
hle/service/mii/mii.h
hle/service/mii/mii_manager.cpp
hle/service/mii/mii_manager.h
hle/service/mii/raw_data.cpp
hle/service/mii/raw_data.h
hle/service/mii/types.h
hle/service/mii/mii_result.h
hle/service/mii/mii_types.h
hle/service/mii/mii_util.h
hle/service/mm/mm_u.cpp
hle/service/mm/mm_u.h
hle/service/mnpp/mnpp_app.cpp

View file

@ -85,15 +85,18 @@ void MiiEdit::Execute() {
break;
case MiiEditAppletMode::CreateMii:
case MiiEditAppletMode::EditMii: {
Service::Mii::MiiManager mii_manager;
Mii::CharInfo char_info{};
Mii::StoreData store_data{};
store_data.BuildBase(Mii::Gender::Male);
char_info.SetFromStoreData(store_data);
const MiiEditCharInfo char_info{
const MiiEditCharInfo edit_char_info{
.mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii
? applet_input_v4.char_info.mii_info
: mii_manager.BuildBase(Mii::Gender::Male)},
: char_info},
};
MiiEditOutputForCharInfoEditing(MiiEditResult::Success, char_info);
MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info);
break;
}
default:

View file

@ -7,7 +7,8 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/service/mii/types.h"
#include "common/uuid.h"
#include "core/hle/service/mii/types/char_info.h"
namespace Service::AM::Applets {

View file

@ -7,17 +7,16 @@
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/mii/mii.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/mii/mii_result.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Service::Mii {
constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
public:
explicit IDatabaseService(Core::System& system_)
: ServiceFramework{system_, "IDatabaseService"} {
explicit IDatabaseService(Core::System& system_, bool is_system_)
: ServiceFramework{system_, "IDatabaseService"}, is_system{is_system_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IDatabaseService::IsUpdated, "IsUpdated"},
@ -54,34 +53,27 @@ public:
}
private:
template <typename T>
std::vector<u8> SerializeArray(const std::vector<T>& values) {
std::vector<u8> out(values.size() * sizeof(T));
std::size_t offset{};
for (const auto& value : values) {
std::memcpy(out.data() + offset, &value, sizeof(T));
offset += sizeof(T);
}
return out;
}
void IsUpdated(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
const bool is_updated = manager.IsUpdated(metadata, source_flag);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(manager.CheckAndResetUpdateCounter(source_flag, current_update_counter));
rb.Push<u8>(is_updated);
}
void IsFullDatabase(HLERequestContext& ctx) {
LOG_DEBUG(Service_Mii, "called");
const bool is_full_database = manager.IsFullDatabase();
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(manager.IsFullDatabase());
rb.Push<u8>(is_full_database);
}
void GetCount(HLERequestContext& ctx) {
@ -90,57 +82,63 @@ private:
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
const u32 mii_count = manager.GetCount(metadata, source_flag);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(manager.GetCount(source_flag));
rb.Push(mii_count);
}
void Get(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto source_flag{rp.PopRaw<SourceFlag>()};
const auto output_size{ctx.GetWriteBufferNumElements<CharInfoElement>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size);
const auto default_miis{manager.GetDefault(source_flag)};
if (default_miis.size() > 0) {
ctx.WriteBuffer(SerializeArray(default_miis));
u32 mii_count{};
std::vector<CharInfoElement> char_info_elements(output_size);
Result result = manager.Get(metadata, char_info_elements, mii_count, source_flag);
if (mii_count != 0) {
ctx.WriteBuffer(char_info_elements);
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(static_cast<u32>(default_miis.size()));
rb.Push(result);
rb.Push(mii_count);
}
void Get1(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto source_flag{rp.PopRaw<SourceFlag>()};
const auto output_size{ctx.GetWriteBufferNumElements<CharInfo>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size);
const auto default_miis{manager.GetDefault(source_flag)};
u32 mii_count{};
std::vector<CharInfo> char_info(output_size);
Result result = manager.Get(metadata, char_info, mii_count, source_flag);
std::vector<CharInfo> values;
for (const auto& element : default_miis) {
values.emplace_back(element.info);
if (mii_count != 0) {
ctx.WriteBuffer(char_info);
}
ctx.WriteBuffer(SerializeArray(values));
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(static_cast<u32>(default_miis.size()));
rb.Push(result);
rb.Push(mii_count);
}
void UpdateLatest(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto info{rp.PopRaw<CharInfo>()};
const auto char_info{rp.PopRaw<CharInfo>()};
const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
CharInfo new_char_info{};
const auto result{manager.UpdateLatest(&new_char_info, info, source_flag)};
if (result != ResultSuccess) {
const auto result = manager.UpdateLatest(metadata, new_char_info, char_info, source_flag);
if (result.IsFailure()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
@ -153,7 +151,6 @@ private:
void BuildRandom(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto age{rp.PopRaw<Age>()};
const auto gender{rp.PopRaw<Gender>()};
const auto race{rp.PopRaw<Race>()};
@ -162,47 +159,48 @@ private:
if (age > Age::All) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
LOG_ERROR(Service_Mii, "invalid age={}", age);
rb.Push(ResultInvalidArgument);
return;
}
if (gender > Gender::All) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
LOG_ERROR(Service_Mii, "invalid gender={}", gender);
rb.Push(ResultInvalidArgument);
return;
}
if (race > Race::All) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
LOG_ERROR(Service_Mii, "invalid race={}", race);
rb.Push(ResultInvalidArgument);
return;
}
CharInfo char_info{};
manager.BuildRandom(char_info, age, gender, race);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
rb.PushRaw<CharInfo>(manager.BuildRandom(age, gender, race));
rb.PushRaw<CharInfo>(char_info);
}
void BuildDefault(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto index{rp.Pop<u32>()};
LOG_DEBUG(Service_Mii, "called with index={}", index);
LOG_INFO(Service_Mii, "called with index={}", index);
if (index > 5) {
LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}",
index);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
rb.Push(ResultInvalidArgument);
return;
}
CharInfo char_info{};
manager.BuildDefault(char_info, index);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
rb.PushRaw<CharInfo>(manager.BuildDefault(index));
rb.PushRaw<CharInfo>(char_info);
}
void GetIndex(HLERequestContext& ctx) {
@ -211,19 +209,21 @@ private:
LOG_DEBUG(Service_Mii, "called");
u32 index{};
s32 index{};
const auto result = manager.GetIndex(metadata, info, index);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(manager.GetIndex(info, index));
rb.Push(result);
rb.Push(index);
}
void SetInterfaceVersion(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
current_interface_version = rp.PopRaw<u32>();
const auto interface_version{rp.PopRaw<u32>()};
LOG_DEBUG(Service_Mii, "called, interface_version={:08X}", current_interface_version);
LOG_INFO(Service_Mii, "called, interface_version={:08X}", interface_version);
UNIMPLEMENTED_IF(current_interface_version != 1);
manager.SetInterfaceVersion(metadata, interface_version);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@ -231,30 +231,27 @@ private:
void Convert(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto mii_v3{rp.PopRaw<Ver3StoreData>()};
LOG_INFO(Service_Mii, "called");
CharInfo char_info{};
manager.ConvertV3ToCharInfo(char_info, mii_v3);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
rb.PushRaw<CharInfo>(manager.ConvertV3ToCharInfo(mii_v3));
rb.PushRaw<CharInfo>(char_info);
}
constexpr bool IsInterfaceVersionSupported(u32 interface_version) const {
return current_interface_version >= interface_version;
}
MiiManager manager;
u32 current_interface_version{};
u64 current_update_counter{};
MiiManager manager{};
DatabaseSessionMetadata metadata{};
bool is_system{};
};
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
public:
explicit MiiDBModule(Core::System& system_, const char* name_)
: ServiceFramework{system_, name_} {
explicit MiiDBModule(Core::System& system_, const char* name_, bool is_system_)
: ServiceFramework{system_, name_}, is_system{is_system_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"},
@ -268,10 +265,12 @@ private:
void GetDatabaseService(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDatabaseService>(system);
rb.PushIpcInterface<IDatabaseService>(system, is_system);
LOG_DEBUG(Service_Mii, "called");
}
bool is_system{};
};
class MiiImg final : public ServiceFramework<MiiImg> {
@ -303,8 +302,10 @@ public:
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("mii:e", std::make_shared<MiiDBModule>(system, "mii:e"));
server_manager->RegisterNamedService("mii:u", std::make_shared<MiiDBModule>(system, "mii:u"));
server_manager->RegisterNamedService("mii:e",
std::make_shared<MiiDBModule>(system, "mii:e", true));
server_manager->RegisterNamedService("mii:u",
std::make_shared<MiiDBModule>(system, "mii:u", false));
server_manager->RegisterNamedService("miiimg", std::make_shared<MiiImg>(system));
ServerManager::RunServer(std::move(server_manager));
}

View file

@ -10,385 +10,24 @@
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/mii/raw_data.h"
#include "core/hle/service/mii/mii_result.h"
#include "core/hle/service/mii/mii_util.h"
#include "core/hle/service/mii/types/core_data.h"
#include "core/hle/service/mii/types/raw_data.h"
namespace Service::Mii {
namespace {
constexpr Result ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
constexpr MiiStoreData::Name DefaultMiiName{u'n', u'o', u' ', u'n', u'a', u'm', u'e'};
constexpr std::array<u8, 8> HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7};
constexpr std::array<u8, 6> EyeColorLookup{8, 9, 10, 11, 12, 13};
constexpr std::array<u8, 5> MouthColorLookup{19, 20, 21, 22, 23};
constexpr std::array<u8, 7> GlassesColorLookup{8, 14, 15, 16, 17, 18, 0};
constexpr std::array<u8, 62> EyeRotateLookup{
{0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04,
0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04,
0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04}};
constexpr std::array<u8, 24> EyebrowRotateLookup{{0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07,
0x04, 0x07, 0x06, 0x08, 0x05, 0x05, 0x06, 0x06,
0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05}};
MiiManager::MiiManager() {}
template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
std::array<T, DestArraySize> out{};
std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
return out;
}
CharInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
MiiStoreBitFields bf;
std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields));
return {
.uuid = data.data.uuid,
.name = ResizeArray<char16_t, 10, 11>(data.data.name),
.font_region = static_cast<u8>(bf.font_region.Value()),
.favorite_color = static_cast<u8>(bf.favorite_color.Value()),
.gender = static_cast<u8>(bf.gender.Value()),
.height = static_cast<u8>(bf.height.Value()),
.build = static_cast<u8>(bf.build.Value()),
.type = static_cast<u8>(bf.type.Value()),
.region_move = static_cast<u8>(bf.region_move.Value()),
.faceline_type = static_cast<u8>(bf.faceline_type.Value()),
.faceline_color = static_cast<u8>(bf.faceline_color.Value()),
.faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value()),
.faceline_make = static_cast<u8>(bf.faceline_makeup.Value()),
.hair_type = static_cast<u8>(bf.hair_type.Value()),
.hair_color = static_cast<u8>(bf.hair_color.Value()),
.hair_flip = static_cast<u8>(bf.hair_flip.Value()),
.eye_type = static_cast<u8>(bf.eye_type.Value()),
.eye_color = static_cast<u8>(bf.eye_color.Value()),
.eye_scale = static_cast<u8>(bf.eye_scale.Value()),
.eye_aspect = static_cast<u8>(bf.eye_aspect.Value()),
.eye_rotate = static_cast<u8>(bf.eye_rotate.Value()),
.eye_x = static_cast<u8>(bf.eye_x.Value()),
.eye_y = static_cast<u8>(bf.eye_y.Value()),
.eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value()),
.eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value()),
.eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value()),
.eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value()),
.eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value()),
.eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value()),
.eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3),
.nose_type = static_cast<u8>(bf.nose_type.Value()),
.nose_scale = static_cast<u8>(bf.nose_scale.Value()),
.nose_y = static_cast<u8>(bf.nose_y.Value()),
.mouth_type = static_cast<u8>(bf.mouth_type.Value()),
.mouth_color = static_cast<u8>(bf.mouth_color.Value()),
.mouth_scale = static_cast<u8>(bf.mouth_scale.Value()),
.mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value()),
.mouth_y = static_cast<u8>(bf.mouth_y.Value()),
.beard_color = static_cast<u8>(bf.beard_color.Value()),
.beard_type = static_cast<u8>(bf.beard_type.Value()),
.mustache_type = static_cast<u8>(bf.mustache_type.Value()),
.mustache_scale = static_cast<u8>(bf.mustache_scale.Value()),
.mustache_y = static_cast<u8>(bf.mustache_y.Value()),
.glasses_type = static_cast<u8>(bf.glasses_type.Value()),
.glasses_color = static_cast<u8>(bf.glasses_color.Value()),
.glasses_scale = static_cast<u8>(bf.glasses_scale.Value()),
.glasses_y = static_cast<u8>(bf.glasses_y.Value()),
.mole_type = static_cast<u8>(bf.mole_type.Value()),
.mole_scale = static_cast<u8>(bf.mole_scale.Value()),
.mole_x = static_cast<u8>(bf.mole_x.Value()),
.mole_y = static_cast<u8>(bf.mole_y.Value()),
.padding = 0,
};
}
u16 GenerateCrc16(const void* data, std::size_t size) {
s32 crc{};
for (std::size_t i = 0; i < size; i++) {
crc ^= static_cast<const u8*>(data)[i] << 8;
for (std::size_t j = 0; j < 8; j++) {
crc <<= 1;
if ((crc & 0x10000) != 0) {
crc = (crc ^ 0x1021) & 0xFFFF;
}
}
}
return Common::swap16(static_cast<u16>(crc));
}
template <typename T>
T GetRandomValue(T min, T max) {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(static_cast<u64>(min), static_cast<u64>(max));
return static_cast<T>(distribution(gen));
}
template <typename T>
T GetRandomValue(T max) {
return GetRandomValue<T>({}, max);
}
MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) {
MiiStoreBitFields bf{};
if (gender == Gender::All) {
gender = GetRandomValue<Gender>(Gender::Maximum);
}
bf.gender.Assign(gender);
bf.favorite_color.Assign(GetRandomValue<u8>(11));
bf.region_move.Assign(0);
bf.font_region.Assign(FontRegion::Standard);
bf.type.Assign(0);
bf.height.Assign(64);
bf.build.Assign(64);
if (age == Age::All) {
const auto temp{GetRandomValue<int>(10)};
if (temp >= 8) {
age = Age::Old;
} else if (temp >= 4) {
age = Age::Normal;
} else {
age = Age::Young;
}
}
if (race == Race::All) {
const auto temp{GetRandomValue<int>(10)};
if (temp >= 8) {
race = Race::Black;
} else if (temp >= 4) {
race = Race::White;
} else {
race = Race::Asian;
}
}
u32 axis_y{};
if (gender == Gender::Female && age == Age::Young) {
axis_y = GetRandomValue<u32>(3);
}
const std::size_t index{3 * static_cast<std::size_t>(age) +
9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)};
const auto faceline_type_info{RawData::RandomMiiFaceline.at(index)};
const auto faceline_color_info{RawData::RandomMiiFacelineColor.at(
3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))};
const auto faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)};
const auto faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)};
const auto hair_type_info{RawData::RandomMiiHairType.at(index)};
const auto hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast<std::size_t>(race) +
static_cast<std::size_t>(age))};
const auto eye_type_info{RawData::RandomMiiEyeType.at(index)};
const auto eye_color_info{RawData::RandomMiiEyeColor.at(static_cast<std::size_t>(race))};
const auto eyebrow_type_info{RawData::RandomMiiEyebrowType.at(index)};
const auto nose_type_info{RawData::RandomMiiNoseType.at(index)};
const auto mouth_type_info{RawData::RandomMiiMouthType.at(index)};
const auto glasses_type_info{RawData::RandomMiiGlassType.at(static_cast<std::size_t>(age))};
bf.faceline_type.Assign(
faceline_type_info.values[GetRandomValue<std::size_t>(faceline_type_info.values_count)]);
bf.faceline_color.Assign(
faceline_color_info.values[GetRandomValue<std::size_t>(faceline_color_info.values_count)]);
bf.faceline_wrinkle.Assign(
faceline_wrinkle_info
.values[GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]);
bf.faceline_makeup.Assign(
faceline_makeup_info
.values[GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]);
bf.hair_type.Assign(
hair_type_info.values[GetRandomValue<std::size_t>(hair_type_info.values_count)]);
bf.hair_color.Assign(
HairColorLookup[hair_color_info
.values[GetRandomValue<std::size_t>(hair_color_info.values_count)]]);
bf.hair_flip.Assign(GetRandomValue<HairFlip>(HairFlip::Maximum));
bf.eye_type.Assign(
eye_type_info.values[GetRandomValue<std::size_t>(eye_type_info.values_count)]);
const auto eye_rotate_1{gender != Gender::Male ? 4 : 2};
const auto eye_rotate_2{gender != Gender::Male ? 3 : 4};
const auto eye_rotate_offset{32 - EyeRotateLookup[eye_rotate_1] + eye_rotate_2};
const auto eye_rotate{32 - EyeRotateLookup[bf.eye_type]};
bf.eye_color.Assign(
EyeColorLookup[eye_color_info
.values[GetRandomValue<std::size_t>(eye_color_info.values_count)]]);
bf.eye_scale.Assign(4);
bf.eye_aspect.Assign(3);
bf.eye_rotate.Assign(eye_rotate_offset - eye_rotate);
bf.eye_x.Assign(2);
bf.eye_y.Assign(axis_y + 12);
bf.eyebrow_type.Assign(
eyebrow_type_info.values[GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]);
const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0};
const auto eyebrow_y{race == Race::Asian ? 9 : 10};
const auto eyebrow_rotate_offset{32 - EyebrowRotateLookup[eyebrow_rotate_1] + 6};
const auto eyebrow_rotate{
32 - EyebrowRotateLookup[static_cast<std::size_t>(bf.eyebrow_type.Value())]};
bf.eyebrow_color.Assign(bf.hair_color);
bf.eyebrow_scale.Assign(4);
bf.eyebrow_aspect.Assign(3);
bf.eyebrow_rotate.Assign(eyebrow_rotate_offset - eyebrow_rotate);
bf.eyebrow_x.Assign(2);
bf.eyebrow_y.Assign(axis_y + eyebrow_y);
const auto nose_scale{gender == Gender::Female ? 3 : 4};
bf.nose_type.Assign(
nose_type_info.values[GetRandomValue<std::size_t>(nose_type_info.values_count)]);
bf.nose_scale.Assign(nose_scale);
bf.nose_y.Assign(axis_y + 9);
const auto mouth_color{gender == Gender::Female ? GetRandomValue<int>(4) : 0};
bf.mouth_type.Assign(
mouth_type_info.values[GetRandomValue<std::size_t>(mouth_type_info.values_count)]);
bf.mouth_color.Assign(MouthColorLookup[mouth_color]);
bf.mouth_scale.Assign(4);
bf.mouth_aspect.Assign(3);
bf.mouth_y.Assign(axis_y + 13);
bf.beard_color.Assign(bf.hair_color);
bf.mustache_scale.Assign(4);
if (gender == Gender::Male && age != Age::Young && GetRandomValue<int>(10) < 2) {
const auto mustache_and_beard_flag{
GetRandomValue<BeardAndMustacheFlag>(BeardAndMustacheFlag::All)};
auto beard_type{BeardType::None};
auto mustache_type{MustacheType::None};
if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) ==
BeardAndMustacheFlag::Beard) {
beard_type = GetRandomValue<BeardType>(BeardType::Beard1, BeardType::Beard5);
}
if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) ==
BeardAndMustacheFlag::Mustache) {
mustache_type =
GetRandomValue<MustacheType>(MustacheType::Mustache1, MustacheType::Mustache5);
}
bf.mustache_type.Assign(mustache_type);
bf.beard_type.Assign(beard_type);
bf.mustache_y.Assign(10);
} else {
bf.mustache_type.Assign(MustacheType::None);
bf.beard_type.Assign(BeardType::None);
bf.mustache_y.Assign(axis_y + 10);
}
const auto glasses_type_start{GetRandomValue<std::size_t>(100)};
u8 glasses_type{};
while (glasses_type_start < glasses_type_info.values[glasses_type]) {
if (++glasses_type >= glasses_type_info.values_count) {
ASSERT(false);
break;
}
}
bf.glasses_type.Assign(glasses_type);
bf.glasses_color.Assign(GlassesColorLookup[0]);
bf.glasses_scale.Assign(4);
bf.glasses_y.Assign(axis_y + 10);
bf.mole_type.Assign(0);
bf.mole_scale.Assign(4);
bf.mole_x.Assign(2);
bf.mole_y.Assign(20);
return {DefaultMiiName, bf, user_id};
}
MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) {
MiiStoreBitFields bf{};
bf.font_region.Assign(info.font_region);
bf.favorite_color.Assign(info.favorite_color);
bf.gender.Assign(info.gender);
bf.height.Assign(info.height);
bf.build.Assign(info.weight);
bf.type.Assign(info.type);
bf.region_move.Assign(info.region);
bf.faceline_type.Assign(info.face_type);
bf.faceline_color.Assign(info.face_color);
bf.faceline_wrinkle.Assign(info.face_wrinkle);
bf.faceline_makeup.Assign(info.face_makeup);
bf.hair_type.Assign(info.hair_type);
bf.hair_color.Assign(HairColorLookup[info.hair_color]);
bf.hair_flip.Assign(static_cast<HairFlip>(info.hair_flip));
bf.eye_type.Assign(info.eye_type);
bf.eye_color.Assign(EyeColorLookup[info.eye_color]);
bf.eye_scale.Assign(info.eye_scale);
bf.eye_aspect.Assign(info.eye_aspect);
bf.eye_rotate.Assign(info.eye_rotate);
bf.eye_x.Assign(info.eye_x);
bf.eye_y.Assign(info.eye_y);
bf.eyebrow_type.Assign(info.eyebrow_type);
bf.eyebrow_color.Assign(HairColorLookup[info.eyebrow_color]);
bf.eyebrow_scale.Assign(info.eyebrow_scale);
bf.eyebrow_aspect.Assign(info.eyebrow_aspect);
bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
bf.eyebrow_x.Assign(info.eyebrow_x);
bf.eyebrow_y.Assign(info.eyebrow_y - 3);
bf.nose_type.Assign(info.nose_type);
bf.nose_scale.Assign(info.nose_scale);
bf.nose_y.Assign(info.nose_y);
bf.mouth_type.Assign(info.mouth_type);
bf.mouth_color.Assign(MouthColorLookup[info.mouth_color]);
bf.mouth_scale.Assign(info.mouth_scale);
bf.mouth_aspect.Assign(info.mouth_aspect);
bf.mouth_y.Assign(info.mouth_y);
bf.beard_color.Assign(HairColorLookup[info.beard_color]);
bf.beard_type.Assign(static_cast<BeardType>(info.beard_type));
bf.mustache_type.Assign(static_cast<MustacheType>(info.mustache_type));
bf.mustache_scale.Assign(info.mustache_scale);
bf.mustache_y.Assign(info.mustache_y);
bf.glasses_type.Assign(info.glasses_type);
bf.glasses_color.Assign(GlassesColorLookup[info.glasses_color]);
bf.glasses_scale.Assign(info.glasses_scale);
bf.glasses_y.Assign(info.glasses_y);
bf.mole_type.Assign(info.mole_type);
bf.mole_scale.Assign(info.mole_scale);
bf.mole_x.Assign(info.mole_x);
bf.mole_y.Assign(info.mole_y);
return {DefaultMiiName, bf, user_id};
}
} // namespace
MiiStoreData::MiiStoreData() = default;
MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields,
const Common::UUID& user_id) {
data.name = name;
data.uuid = Common::UUID::MakeRandomRFC4122V4();
std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields));
data_crc = GenerateCrc16(data.data.data(), sizeof(data));
device_crc = GenerateCrc16(&user_id, sizeof(Common::UUID));
}
MiiManager::MiiManager() : user_id{Service::Account::ProfileManager().GetLastOpenedUser()} {}
bool MiiManager::CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter) {
bool MiiManager::IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return false;
}
const bool result{current_update_counter != update_counter};
current_update_counter = update_counter;
return result;
const auto metadata_update_counter = metadata.update_counter;
metadata.update_counter = update_counter;
return metadata_update_counter != update_counter;
}
bool MiiManager::IsFullDatabase() const {
@ -396,306 +35,138 @@ bool MiiManager::IsFullDatabase() const {
return false;
}
u32 MiiManager::GetCount(SourceFlag source_flag) const {
std::size_t count{};
u32 MiiManager::GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const {
u32 mii_count{};
if ((source_flag & SourceFlag::Default) != SourceFlag::None) {
mii_count += DefaultMiiCount;
}
if ((source_flag & SourceFlag::Database) != SourceFlag::None) {
// TODO(bunnei): We don't implement the Mii database, but when we do, update this
count += 0;
}
if ((source_flag & SourceFlag::Default) != SourceFlag::None) {
count += DefaultMiiCount;
}
return static_cast<u32>(count);
return mii_count;
}
Result MiiManager::UpdateLatest(CharInfo* out_info, const CharInfo& info, SourceFlag source_flag) {
Result MiiManager::UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
const CharInfo& char_info, SourceFlag source_flag) {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return ERROR_CANNOT_FIND_ENTRY;
return ResultNotFound;
}
// TODO(bunnei): We don't implement the Mii database, so we can't have an entry
return ERROR_CANNOT_FIND_ENTRY;
return ResultNotFound;
}
CharInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id));
void MiiManager::BuildDefault(CharInfo& out_char_info, u32 index) const {
StoreData store_data{};
store_data.BuildDefault(index);
out_char_info.SetFromStoreData(store_data);
}
CharInfo MiiManager::BuildBase(Gender gender) {
const std::size_t index = gender == Gender::Female ? 1 : 0;
return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::BaseMii.at(index), user_id));
void MiiManager::BuildBase(CharInfo& out_char_info, Gender gender) const {
StoreData store_data{};
store_data.BuildBase(gender);
out_char_info.SetFromStoreData(store_data);
}
CharInfo MiiManager::BuildDefault(std::size_t index) {
return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id));
void MiiManager::BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const {
StoreData store_data{};
store_data.BuildRandom(age, gender, race);
out_char_info.SetFromStoreData(store_data);
}
CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const {
Service::Mii::MiiManager manager;
auto mii = manager.BuildBase(Mii::Gender::Male);
void MiiManager::ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const {
StoreData store_data{};
mii_v3.BuildToStoreData(store_data);
out_char_info.SetFromStoreData(store_data);
}
if (!ValidateV3Info(mii_v3)) {
return mii;
Result MiiManager::Get(const DatabaseSessionMetadata& metadata,
std::span<CharInfoElement> out_elements, u32& out_count,
SourceFlag source_flag) {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return BuildDefault(out_elements, out_count, source_flag);
}
// TODO: We are ignoring a bunch of data from the mii_v3
// TODO(bunnei): We don't implement the Mii database, so we can't have an entry
mii.gender = static_cast<u8>(mii_v3.mii_information.gender);
mii.favorite_color = static_cast<u8>(mii_v3.mii_information.favorite_color);
mii.height = mii_v3.height;
mii.build = mii_v3.build;
// Include default Mii at the end of the list
return BuildDefault(out_elements, out_count, source_flag);
}
// Copy name until string terminator
mii.name = {};
for (std::size_t index = 0; index < mii.name.size() - 1; index++) {
mii.name[index] = mii_v3.mii_name[index];
if (mii.name[index] == 0) {
break;
}
Result MiiManager::Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info,
u32& out_count, SourceFlag source_flag) {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return BuildDefault(out_char_info, out_count, source_flag);
}
mii.font_region = mii_v3.region_information.character_set;
// TODO(bunnei): We don't implement the Mii database, so we can't have an entry
mii.faceline_type = mii_v3.appearance_bits1.face_shape;
mii.faceline_color = mii_v3.appearance_bits1.skin_color;
mii.faceline_wrinkle = mii_v3.appearance_bits2.wrinkles;
mii.faceline_make = mii_v3.appearance_bits2.makeup;
mii.hair_type = mii_v3.hair_style;
mii.hair_color = mii_v3.appearance_bits3.hair_color;
mii.hair_flip = mii_v3.appearance_bits3.flip_hair;
mii.eye_type = static_cast<u8>(mii_v3.appearance_bits4.eye_type);
mii.eye_color = static_cast<u8>(mii_v3.appearance_bits4.eye_color);
mii.eye_scale = static_cast<u8>(mii_v3.appearance_bits4.eye_scale);
mii.eye_aspect = static_cast<u8>(mii_v3.appearance_bits4.eye_vertical_stretch);
mii.eye_rotate = static_cast<u8>(mii_v3.appearance_bits4.eye_rotation);
mii.eye_x = static_cast<u8>(mii_v3.appearance_bits4.eye_spacing);
mii.eye_y = static_cast<u8>(mii_v3.appearance_bits4.eye_y_position);
mii.eyebrow_type = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_style);
mii.eyebrow_color = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_color);
mii.eyebrow_scale = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_scale);
mii.eyebrow_aspect = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_yscale);
mii.eyebrow_rotate = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_rotation);
mii.eyebrow_x = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_spacing);
mii.eyebrow_y = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_y_position);
mii.nose_type = static_cast<u8>(mii_v3.appearance_bits6.nose_type);
mii.nose_scale = static_cast<u8>(mii_v3.appearance_bits6.nose_scale);
mii.nose_y = static_cast<u8>(mii_v3.appearance_bits6.nose_y_position);
mii.mouth_type = static_cast<u8>(mii_v3.appearance_bits7.mouth_type);
mii.mouth_color = static_cast<u8>(mii_v3.appearance_bits7.mouth_color);
mii.mouth_scale = static_cast<u8>(mii_v3.appearance_bits7.mouth_scale);
mii.mouth_aspect = static_cast<u8>(mii_v3.appearance_bits7.mouth_horizontal_stretch);
mii.mouth_y = static_cast<u8>(mii_v3.appearance_bits8.mouth_y_position);
mii.mustache_type = static_cast<u8>(mii_v3.appearance_bits8.mustache_type);
mii.mustache_scale = static_cast<u8>(mii_v3.appearance_bits9.mustache_scale);
mii.mustache_y = static_cast<u8>(mii_v3.appearance_bits9.mustache_y_position);
mii.beard_type = static_cast<u8>(mii_v3.appearance_bits9.bear_type);
mii.beard_color = static_cast<u8>(mii_v3.appearance_bits9.facial_hair_color);
mii.glasses_type = static_cast<u8>(mii_v3.appearance_bits10.glasses_type);
mii.glasses_color = static_cast<u8>(mii_v3.appearance_bits10.glasses_color);
mii.glasses_scale = static_cast<u8>(mii_v3.appearance_bits10.glasses_scale);
mii.glasses_y = static_cast<u8>(mii_v3.appearance_bits10.glasses_y_position);
mii.mole_type = static_cast<u8>(mii_v3.appearance_bits11.mole_enabled);
mii.mole_scale = static_cast<u8>(mii_v3.appearance_bits11.mole_scale);
mii.mole_x = static_cast<u8>(mii_v3.appearance_bits11.mole_x_position);
mii.mole_y = static_cast<u8>(mii_v3.appearance_bits11.mole_y_position);
// TODO: Validate mii data
return mii;
// Include default Mii at the end of the list
return BuildDefault(out_char_info, out_count, source_flag);
}
Ver3StoreData MiiManager::BuildFromStoreData(const CharInfo& mii) const {
Service::Mii::MiiManager manager;
Ver3StoreData mii_v3{};
// TODO: We are ignoring a bunch of data from the mii_v3
mii_v3.version = 1;
mii_v3.mii_information.gender.Assign(mii.gender);
mii_v3.mii_information.favorite_color.Assign(mii.favorite_color);
mii_v3.height = mii.height;
mii_v3.build = mii.build;
// Copy name until string terminator
mii_v3.mii_name = {};
for (std::size_t index = 0; index < mii.name.size() - 1; index++) {
mii_v3.mii_name[index] = mii.name[index];
if (mii_v3.mii_name[index] == 0) {
break;
}
}
mii_v3.region_information.character_set.Assign(mii.font_region);
mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type);
mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle);
mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make);
mii_v3.hair_style = mii.hair_type;
mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip);
mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type);
mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale);
mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect);
mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate);
mii_v3.appearance_bits4.eye_spacing.Assign(mii.eye_x);
mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y);
mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type);
mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale);
mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect);
mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate);
mii_v3.appearance_bits5.eyebrow_spacing.Assign(mii.eyebrow_x);
mii_v3.appearance_bits5.eyebrow_y_position.Assign(mii.eyebrow_y);
mii_v3.appearance_bits6.nose_type.Assign(mii.nose_type);
mii_v3.appearance_bits6.nose_scale.Assign(mii.nose_scale);
mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y);
mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type);
mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale);
mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect);
mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y);
mii_v3.appearance_bits8.mustache_type.Assign(mii.mustache_type);
mii_v3.appearance_bits9.mustache_scale.Assign(mii.mustache_scale);
mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y);
mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type);
mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale);
mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y);
mii_v3.appearance_bits11.mole_enabled.Assign(mii.mole_type);
mii_v3.appearance_bits11.mole_scale.Assign(mii.mole_scale);
mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x);
mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y);
// These types are converted to V3 from a table
mii_v3.appearance_bits1.skin_color.Assign(Ver3FacelineColorTable[mii.faceline_color]);
mii_v3.appearance_bits3.hair_color.Assign(Ver3HairColorTable[mii.hair_color]);
mii_v3.appearance_bits4.eye_color.Assign(Ver3EyeColorTable[mii.eye_color]);
mii_v3.appearance_bits5.eyebrow_color.Assign(Ver3HairColorTable[mii.eyebrow_color]);
mii_v3.appearance_bits7.mouth_color.Assign(Ver3MouthlineColorTable[mii.mouth_color]);
mii_v3.appearance_bits9.facial_hair_color.Assign(Ver3HairColorTable[mii.beard_color]);
mii_v3.appearance_bits10.glasses_color.Assign(Ver3GlassColorTable[mii.glasses_color]);
mii_v3.appearance_bits10.glasses_type.Assign(Ver3GlassTypeTable[mii.glasses_type]);
mii_v3.crc = GenerateCrc16(&mii_v3, sizeof(Ver3StoreData) - sizeof(u16));
// TODO: Validate mii_v3 data
return mii_v3;
}
NfpStoreDataExtension MiiManager::SetFromStoreData(const CharInfo& mii) const {
return {
.faceline_color = static_cast<u8>(mii.faceline_color & 0xf),
.hair_color = static_cast<u8>(mii.hair_color & 0x7f),
.eye_color = static_cast<u8>(mii.eyebrow_color & 0x7f),
.eyebrow_color = static_cast<u8>(mii.eyebrow_color & 0x7f),
.mouth_color = static_cast<u8>(mii.mouth_color & 0x7f),
.beard_color = static_cast<u8>(mii.beard_color & 0x7f),
.glass_color = static_cast<u8>(mii.glasses_color & 0x7f),
.glass_type = static_cast<u8>(mii.glasses_type & 0x1f),
};
}
bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const {
bool is_valid = mii_v3.version == 0 || mii_v3.version == 3;
is_valid = is_valid && (mii_v3.mii_name[0] != 0);
is_valid = is_valid && (mii_v3.mii_information.birth_month < 13);
is_valid = is_valid && (mii_v3.mii_information.birth_day < 32);
is_valid = is_valid && (mii_v3.mii_information.favorite_color < 12);
is_valid = is_valid && (mii_v3.height < 128);
is_valid = is_valid && (mii_v3.build < 128);
is_valid = is_valid && (mii_v3.appearance_bits1.face_shape < 12);
is_valid = is_valid && (mii_v3.appearance_bits1.skin_color < 7);
is_valid = is_valid && (mii_v3.appearance_bits2.wrinkles < 12);
is_valid = is_valid && (mii_v3.appearance_bits2.makeup < 12);
is_valid = is_valid && (mii_v3.hair_style < 132);
is_valid = is_valid && (mii_v3.appearance_bits3.hair_color < 8);
is_valid = is_valid && (mii_v3.appearance_bits4.eye_type < 60);
is_valid = is_valid && (mii_v3.appearance_bits4.eye_color < 6);
is_valid = is_valid && (mii_v3.appearance_bits4.eye_scale < 8);
is_valid = is_valid && (mii_v3.appearance_bits4.eye_vertical_stretch < 7);
is_valid = is_valid && (mii_v3.appearance_bits4.eye_rotation < 8);
is_valid = is_valid && (mii_v3.appearance_bits4.eye_spacing < 13);
is_valid = is_valid && (mii_v3.appearance_bits4.eye_y_position < 19);
is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_style < 25);
is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_color < 8);
is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_scale < 9);
is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_yscale < 7);
is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_rotation < 12);
is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_spacing < 12);
is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_y_position < 19);
is_valid = is_valid && (mii_v3.appearance_bits6.nose_type < 18);
is_valid = is_valid && (mii_v3.appearance_bits6.nose_scale < 9);
is_valid = is_valid && (mii_v3.appearance_bits6.nose_y_position < 19);
is_valid = is_valid && (mii_v3.appearance_bits7.mouth_type < 36);
is_valid = is_valid && (mii_v3.appearance_bits7.mouth_color < 5);
is_valid = is_valid && (mii_v3.appearance_bits7.mouth_scale < 9);
is_valid = is_valid && (mii_v3.appearance_bits7.mouth_horizontal_stretch < 7);
is_valid = is_valid && (mii_v3.appearance_bits8.mouth_y_position < 19);
is_valid = is_valid && (mii_v3.appearance_bits8.mustache_type < 6);
is_valid = is_valid && (mii_v3.appearance_bits9.mustache_scale < 7);
is_valid = is_valid && (mii_v3.appearance_bits9.mustache_y_position < 17);
is_valid = is_valid && (mii_v3.appearance_bits9.bear_type < 6);
is_valid = is_valid && (mii_v3.appearance_bits9.facial_hair_color < 8);
is_valid = is_valid && (mii_v3.appearance_bits10.glasses_type < 9);
is_valid = is_valid && (mii_v3.appearance_bits10.glasses_color < 6);
is_valid = is_valid && (mii_v3.appearance_bits10.glasses_scale < 8);
is_valid = is_valid && (mii_v3.appearance_bits10.glasses_y_position < 21);
is_valid = is_valid && (mii_v3.appearance_bits11.mole_enabled < 2);
is_valid = is_valid && (mii_v3.appearance_bits11.mole_scale < 9);
is_valid = is_valid && (mii_v3.appearance_bits11.mole_x_position < 17);
is_valid = is_valid && (mii_v3.appearance_bits11.mole_y_position < 31);
return is_valid;
}
std::vector<MiiInfoElement> MiiManager::GetDefault(SourceFlag source_flag) {
std::vector<MiiInfoElement> result;
Result MiiManager::BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count,
SourceFlag source_flag) {
if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
return result;
return ResultSuccess;
}
for (std::size_t index = 0; index < DefaultMiiCount; index++) {
result.emplace_back(BuildDefault(index), Source::Default);
StoreData store_data{};
for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
if (out_elements.size() <= static_cast<std::size_t>(out_count)) {
return ResultInvalidArgumentSize;
}
store_data.BuildDefault(static_cast<u32>(index));
out_elements[out_count].source = Source::Default;
out_elements[out_count].char_info.SetFromStoreData(store_data);
out_count++;
}
return result;
return ResultSuccess;
}
Result MiiManager::GetIndex([[maybe_unused]] const CharInfo& info, u32& index) {
Result MiiManager::BuildDefault(std::span<CharInfo> out_char_info, u32& out_count,
SourceFlag source_flag) {
if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
return ResultSuccess;
}
StoreData store_data{};
for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
if (out_char_info.size() <= static_cast<std::size_t>(out_count)) {
return ResultInvalidArgumentSize;
}
store_data.BuildDefault(static_cast<u32>(index));
out_char_info[out_count].SetFromStoreData(store_data);
out_count++;
}
return ResultSuccess;
}
Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
s32& out_index) {
if (char_info.Verify() != ValidationResult::NoErrors) {
return ResultInvalidCharInfo;
}
constexpr u32 INVALID_INDEX{0xFFFFFFFF};
index = INVALID_INDEX;
out_index = INVALID_INDEX;
// TODO(bunnei): We don't implement the Mii database, so we can't have an index
return ERROR_CANNOT_FIND_ENTRY;
return ResultNotFound;
}
void MiiManager::SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) {
metadata.interface_version = version;
}
} // namespace Service::Mii

View file

@ -6,7 +6,10 @@
#include <vector>
#include "core/hle/result.h"
#include "core/hle/service/mii/types.h"
#include "core/hle/service/mii/mii_types.h"
#include "core/hle/service/mii/types/char_info.h"
#include "core/hle/service/mii/types/store_data.h"
#include "core/hle/service/mii/types/ver3_store_data.h"
namespace Service::Mii {
@ -16,26 +19,30 @@ class MiiManager {
public:
MiiManager();
bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter);
bool IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
bool IsFullDatabase() const;
u32 GetCount(SourceFlag source_flag) const;
Result UpdateLatest(CharInfo* out_info, const CharInfo& info, SourceFlag source_flag);
CharInfo BuildRandom(Age age, Gender gender, Race race);
CharInfo BuildBase(Gender gender);
CharInfo BuildDefault(std::size_t index);
CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const;
bool ValidateV3Info(const Ver3StoreData& mii_v3) const;
std::vector<MiiInfoElement> GetDefault(SourceFlag source_flag);
Result GetIndex(const CharInfo& info, u32& index);
// This is nn::mii::detail::Ver::StoreDataRaw::BuildFromStoreData
Ver3StoreData BuildFromStoreData(const CharInfo& mii) const;
// This is nn::mii::detail::NfpStoreDataExtentionRaw::SetFromStoreData
NfpStoreDataExtension SetFromStoreData(const CharInfo& mii) const;
u32 GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
Result UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
const CharInfo& char_info, SourceFlag source_flag);
Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfoElement> out_elements,
u32& out_count, SourceFlag source_flag);
Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info,
u32& out_count, SourceFlag source_flag);
void BuildDefault(CharInfo& out_char_info, u32 index) const;
void BuildBase(CharInfo& out_char_info, Gender gender) const;
void BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const;
void ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const;
std::vector<CharInfoElement> GetDefault(SourceFlag source_flag);
Result GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
s32& out_index);
void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version);
private:
const Common::UUID user_id{};
Result BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count,
SourceFlag source_flag);
Result BuildDefault(std::span<CharInfo> out_char_info, u32& out_count, SourceFlag source_flag);
u64 update_counter{};
};

View file

@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::Mii {
constexpr Result ResultInvalidArgument{ErrorModule::Mii, 1};
constexpr Result ResultInvalidArgumentSize{ErrorModule::Mii, 2};
constexpr Result ResultNotUpdated{ErrorModule::Mii, 3};
constexpr Result ResultNotFound{ErrorModule::Mii, 4};
constexpr Result ResultDatabaseFull{ErrorModule::Mii, 5};
constexpr Result ResultInvalidCharInfo{ErrorModule::Mii, 100};
constexpr Result ResultInvalidStoreData{ErrorModule::Mii, 109};
constexpr Result ResultInvalidOperation{ErrorModule::Mii, 202};
constexpr Result ResultPermissionDenied{ErrorModule::Mii, 203};
}; // namespace Service::Mii

View file

@ -0,0 +1,694 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <type_traits>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/uuid.h"
namespace Service::Mii {
constexpr u8 MaxHeight = 127;
constexpr u8 MaxBuild = 127;
constexpr u8 MaxType = 1;
constexpr u8 MaxRegionMove = 3;
constexpr u8 MaxEyeScale = 7;
constexpr u8 MaxEyeAspect = 6;
constexpr u8 MaxEyeRotate = 7;
constexpr u8 MaxEyeX = 12;
constexpr u8 MaxEyeY = 18;
constexpr u8 MaxEyebrowScale = 8;
constexpr u8 MaxEyebrowAspect = 6;
constexpr u8 MaxEyebrowRotate = 11;
constexpr u8 MaxEyebrowX = 12;
constexpr u8 MaxEyebrowY = 18;
constexpr u8 MaxNoseScale = 8;
constexpr u8 MaxNoseY = 18;
constexpr u8 MaxMouthScale = 8;
constexpr u8 MaxMoutAspect = 6;
constexpr u8 MaxMouthY = 18;
constexpr u8 MaxMustacheScale = 8;
constexpr u8 MasMustacheY = 16;
constexpr u8 MaxGlassScale = 7;
constexpr u8 MaxGlassY = 20;
constexpr u8 MaxMoleScale = 8;
constexpr u8 MaxMoleX = 16;
constexpr u8 MaxMoleY = 30;
constexpr u8 MaxVer3CommonColor = 7;
constexpr u8 MaxVer3GlassType = 8;
enum class Age : u8 {
Young,
Normal,
Old,
All, // Default
Max = All,
};
enum class Gender : u8 {
Male,
Female,
All, // Default
Max = Female,
};
enum class Race : u8 {
Black,
White,
Asian,
All, // Default
Max = All,
};
enum class HairType : u8 {
NormalLong, // Default
NormalShort,
NormalMedium,
NormalExtraLong,
NormalLongBottom,
NormalTwoPeaks,
PartingLong,
FrontLock,
PartingShort,
PartingExtraLongCurved,
PartingExtraLong,
PartingMiddleLong,
PartingSquared,
PartingLongBottom,
PeaksTop,
PeaksSquared,
PartingPeaks,
PeaksLongBottom,
Peaks,
PeaksRounded,
PeaksSide,
PeaksMedium,
PeaksLong,
PeaksRoundedLong,
PartingFrontPeaks,
PartingLongFront,
PartingLongRounded,
PartingFrontPeaksLong,
PartingExtraLongRounded,
LongRounded,
NormalUnknown1,
NormalUnknown2,
NormalUnknown3,
NormalUnknown4,
NormalUnknown5,
NormalUnknown6,
DreadLocks,
PlatedMats,
Caps,
Afro,
PlatedMatsLong,
Beanie,
Short,
ShortTopLongSide,
ShortUnknown1,
ShortUnknown2,
MilitaryParting,
Military,
ShortUnknown3,
ShortUnknown4,
ShortUnknown5,
ShortUnknown6,
NoneTop,
None,
LongUnknown1,
LongUnknown2,
LongUnknown3,
LongUnknown4,
LongUnknown5,
LongUnknown6,
LongUnknown7,
LongUnknown8,
LongUnknown9,
LongUnknown10,
LongUnknown11,
LongUnknown12,
LongUnknown13,
LongUnknown14,
LongUnknown15,
LongUnknown16,
LongUnknown17,
LongUnknown18,
LongUnknown19,
LongUnknown20,
LongUnknown21,
LongUnknown22,
LongUnknown23,
LongUnknown24,
LongUnknown25,
LongUnknown26,
LongUnknown27,
LongUnknown28,
LongUnknown29,
LongUnknown30,
LongUnknown31,
LongUnknown32,
LongUnknown33,
LongUnknown34,
LongUnknown35,
LongUnknown36,
LongUnknown37,
LongUnknown38,
LongUnknown39,
LongUnknown40,
LongUnknown41,
LongUnknown42,
LongUnknown43,
LongUnknown44,
LongUnknown45,
LongUnknown46,
LongUnknown47,
LongUnknown48,
LongUnknown49,
LongUnknown50,
LongUnknown51,
LongUnknown52,
LongUnknown53,
LongUnknown54,
LongUnknown55,
LongUnknown56,
LongUnknown57,
LongUnknown58,
LongUnknown59,
LongUnknown60,
LongUnknown61,
LongUnknown62,
LongUnknown63,
LongUnknown64,
LongUnknown65,
LongUnknown66,
TwoMediumFrontStrandsOneLongBackPonyTail,
TwoFrontStrandsLongBackPonyTail,
PartingFrontTwoLongBackPonyTails,
TwoFrontStrandsOneLongBackPonyTail,
LongBackPonyTail,
LongFrontTwoLongBackPonyTails,
StrandsTwoShortSidedPonyTails,
TwoMediumSidedPonyTails,
ShortFrontTwoBackPonyTails,
TwoShortSidedPonyTails,
TwoLongSidedPonyTails,
LongFrontTwoBackPonyTails,
Max = LongFrontTwoBackPonyTails,
};
enum class MoleType : u8 {
None, // Default
OneDot,
Max = OneDot,
};
enum class HairFlip : u8 {
Left, // Default
Right,
Max = Right,
};
enum class CommonColor : u8 {
// For simplicity common colors aren't listed
Max = 99,
Count = 100,
};
enum class FavoriteColor : u8 {
Red, // Default
Orange,
Yellow,
LimeGreen,
Green,
Blue,
LightBlue,
Pink,
Purple,
Brown,
White,
Black,
Max = Black,
};
enum class EyeType : u8 {
Normal, // Default
NormalLash,
WhiteLash,
WhiteNoBottom,
OvalAngledWhite,
AngryWhite,
DotLashType1,
Line,
DotLine,
OvalWhite,
RoundedWhite,
NormalShadow,
CircleWhite,
Circle,
CircleWhiteStroke,
NormalOvalNoBottom,
NormalOvalLarge,
NormalRoundedNoBottom,
SmallLash,
Small,
TwoSmall,
NormalLongLash,
WhiteTwoLashes,
WhiteThreeLashes,
DotAngry,
DotAngled,
Oval,
SmallWhite,
WhiteAngledNoBottom,
WhiteAngledNoLeft,
SmallWhiteTwoLashes,
LeafWhiteLash,
WhiteLargeNoBottom,
Dot,
DotLashType2,
DotThreeLashes,
WhiteOvalTop,
WhiteOvalBottom,
WhiteOvalBottomFlat,
WhiteOvalTwoLashes,
WhiteOvalThreeLashes,
WhiteOvalNoBottomTwoLashes,
DotWhite,
WhiteOvalTopFlat,
WhiteThinLeaf,
StarThreeLashes,
LineTwoLashes,
CrowsFeet,
WhiteNoBottomFlat,
WhiteNoBottomRounded,
WhiteSmallBottomLine,
WhiteNoBottomLash,
WhiteNoPartialBottomLash,
WhiteOvalBottomLine,
WhiteNoBottomLashTopLine,
WhiteNoPartialBottomTwoLashes,
NormalTopLine,
WhiteOvalLash,
RoundTired,
WhiteLarge,
Max = WhiteLarge,
};
enum class MouthType : u8 {
Neutral, // Default
NeutralLips,
Smile,
SmileStroke,
SmileTeeth,
LipsSmall,
LipsLarge,
Wave,
WaveAngrySmall,
NeutralStrokeLarge,
TeethSurprised,
LipsExtraLarge,
LipsUp,
NeutralDown,
Surprised,
TeethMiddle,
NeutralStroke,
LipsExtraSmall,
Malicious,
LipsDual,
NeutralComma,
NeutralUp,
TeethLarge,
WaveAngry,
LipsSexy,
SmileInverted,
LipsSexyOutline,
SmileRounded,
LipsTeeth,
NeutralOpen,
TeethRounded,
WaveAngrySmallInverted,
NeutralCommaInverted,
TeethFull,
SmileDownLine,
Kiss,
Max = Kiss,
};
enum class FontRegion : u8 {
Standard, // Default
China,
Korea,
Taiwan,
Max = Taiwan,
};
enum class FacelineType : u8 {
Sharp, // Default
Rounded,
SharpRounded,
SharpRoundedSmall,
Large,
LargeRounded,
SharpSmall,
Flat,
Bump,
Angular,
FlatRounded,
AngularSmall,
Max = AngularSmall,
};
enum class FacelineColor : u8 {
Beige, // Default
WarmBeige,
Natural,
Honey,
Chestnut,
Porcelain,
Ivory,
WarmIvory,
Almond,
Espresso,
Max = Espresso,
Count = Max + 1,
};
enum class FacelineWrinkle : u8 {
None, // Default
TearTroughs,
FacialPain,
Cheeks,
Folds,
UnderTheEyes,
SplitChin,
Chin,
BrowDroop,
MouthFrown,
CrowsFeet,
FoldsCrowsFrown,
Max = FoldsCrowsFrown,
};
enum class FacelineMake : u8 {
None, // Default
CheekPorcelain,
CheekNatural,
EyeShadowBlue,
CheekBlushPorcelain,
CheekBlushNatural,
CheekPorcelainEyeShadowBlue,
CheekPorcelainEyeShadowNatural,
CheekBlushPorcelainEyeShadowEspresso,
Freckles,
LionsManeBeard,
StubbleBeard,
Max = StubbleBeard,
};
enum class EyebrowType : u8 {
FlatAngledLarge, // Default
LowArchRoundedThin,
SoftAngledLarge,
MediumArchRoundedThin,
RoundedMedium,
LowArchMedium,
RoundedThin,
UpThin,
MediumArchRoundedMedium,
RoundedLarge,
UpLarge,
FlatAngledLargeInverted,
MediumArchFlat,
AngledThin,
HorizontalLarge,
HighArchFlat,
Flat,
MediumArchLarge,
LowArchThin,
RoundedThinInverted,
HighArchLarge,
Hairy,
Dotted,
None,
Max = None,
};
enum class NoseType : u8 {
Normal, // Default
Rounded,
Dot,
Arrow,
Roman,
Triangle,
Button,
RoundedInverted,
Potato,
Grecian,
Snub,
Aquiline,
ArrowLeft,
RoundedLarge,
Hooked,
Fat,
Droopy,
ArrowLarge,
Max = ArrowLarge,
};
enum class BeardType : u8 {
None,
Goatee,
GoateeLong,
LionsManeLong,
LionsMane,
Full,
Min = Goatee,
Max = Full,
};
enum class MustacheType : u8 {
None,
Walrus,
Pencil,
Horseshoe,
Normal,
Toothbrush,
Min = Walrus,
Max = Toothbrush,
};
enum class GlassType : u8 {
None,
Oval,
Wayfarer,
Rectangle,
TopRimless,
Rounded,
Oversized,
CatEye,
Square,
BottomRimless,
SemiOpaqueRounded,
SemiOpaqueCatEye,
SemiOpaqueOval,
SemiOpaqueRectangle,
SemiOpaqueAviator,
OpaqueRounded,
OpaqueCatEye,
OpaqueOval,
OpaqueRectangle,
OpaqueAviator,
Max = OpaqueAviator,
Count = Max + 1,
};
enum class BeardAndMustacheFlag : u32 {
Beard = 1,
Mustache,
All = Beard | Mustache,
};
DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
enum class Source : u32 {
Database = 0,
Default = 1,
Account = 2,
Friend = 3,
};
enum class SourceFlag : u32 {
None = 0,
Database = 1 << 0,
Default = 1 << 1,
};
DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
enum class ValidationResult : u32 {
NoErrors = 0x0,
InvalidBeardColor = 0x1,
InvalidBeardType = 0x2,
InvalidBuild = 0x3,
InvalidEyeAspect = 0x4,
InvalidEyeColor = 0x5,
InvalidEyeRotate = 0x6,
InvalidEyeScale = 0x7,
InvalidEyeType = 0x8,
InvalidEyeX = 0x9,
InvalidEyeY = 0xa,
InvalidEyebrowAspect = 0xb,
InvalidEyebrowColor = 0xc,
InvalidEyebrowRotate = 0xd,
InvalidEyebrowScale = 0xe,
InvalidEyebrowType = 0xf,
InvalidEyebrowX = 0x10,
InvalidEyebrowY = 0x11,
InvalidFacelineColor = 0x12,
InvalidFacelineMake = 0x13,
InvalidFacelineWrinkle = 0x14,
InvalidFacelineType = 0x15,
InvalidColor = 0x16,
InvalidFont = 0x17,
InvalidGender = 0x18,
InvalidGlassColor = 0x19,
InvalidGlassScale = 0x1a,
InvalidGlassType = 0x1b,
InvalidGlassY = 0x1c,
InvalidHairColor = 0x1d,
InvalidHairFlip = 0x1e,
InvalidHairType = 0x1f,
InvalidHeight = 0x20,
InvalidMoleScale = 0x21,
InvalidMoleType = 0x22,
InvalidMoleX = 0x23,
InvalidMoleY = 0x24,
InvalidMouthAspect = 0x25,
InvalidMouthColor = 0x26,
InvalidMouthScale = 0x27,
InvalidMouthType = 0x28,
InvalidMouthY = 0x29,
InvalidMustacheScale = 0x2a,
InvalidMustacheType = 0x2b,
InvalidMustacheY = 0x2c,
InvalidNoseScale = 0x2e,
InvalidNoseType = 0x2f,
InvalidNoseY = 0x30,
InvalidRegionMove = 0x31,
InvalidCreateId = 0x32,
InvalidName = 0x33,
InvalidType = 0x35,
};
struct Nickname {
static constexpr std::size_t MaxNameSize = 10;
std::array<char16_t, MaxNameSize> data;
// Checks for null, non-zero terminated or dirty strings
bool IsValid() const {
if (data[0] == 0) {
return false;
}
if (data[MaxNameSize] != 0) {
return false;
}
std::size_t index = 1;
while (data[index] != 0) {
index++;
}
while (index < MaxNameSize && data[index] == 0) {
index++;
}
return index == MaxNameSize;
}
};
static_assert(sizeof(Nickname) == 0x14, "Nickname is an invalid size");
struct DefaultMii {
u32 face_type{};
u32 face_color{};
u32 face_wrinkle{};
u32 face_makeup{};
u32 hair_type{};
u32 hair_color{};
u32 hair_flip{};
u32 eye_type{};
u32 eye_color{};
u32 eye_scale{};
u32 eye_aspect{};
u32 eye_rotate{};
u32 eye_x{};
u32 eye_y{};
u32 eyebrow_type{};
u32 eyebrow_color{};
u32 eyebrow_scale{};
u32 eyebrow_aspect{};
u32 eyebrow_rotate{};
u32 eyebrow_x{};
u32 eyebrow_y{};
u32 nose_type{};
u32 nose_scale{};
u32 nose_y{};
u32 mouth_type{};
u32 mouth_color{};
u32 mouth_scale{};
u32 mouth_aspect{};
u32 mouth_y{};
u32 mustache_type{};
u32 beard_type{};
u32 beard_color{};
u32 mustache_scale{};
u32 mustache_y{};
u32 glasses_type{};
u32 glasses_color{};
u32 glasses_scale{};
u32 glasses_y{};
u32 mole_type{};
u32 mole_scale{};
u32 mole_x{};
u32 mole_y{};
u32 height{};
u32 weight{};
u32 gender{};
u32 favorite_color{};
u32 region_move{};
u32 font_region{};
u32 type{};
Nickname nickname;
};
static_assert(sizeof(DefaultMii) == 0xd8, "DefaultMii has incorrect size.");
struct DatabaseSessionMetadata {
u32 interface_version;
u32 magic;
u64 update_counter;
bool IsInterfaceVersionSupported(u32 version) const {
return version <= interface_version;
}
};
} // namespace Service::Mii

View file

@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <random>
#include <span>
#include "common/common_types.h"
#include "common/swap.h"
#include "common/uuid.h"
#include "core/hle/service/mii/mii_types.h"
namespace Service::Mii {
class MiiUtil {
public:
static u16 CalculateCrc16(const void* data, std::size_t size) {
s32 crc{};
for (std::size_t i = 0; i < size; i++) {
crc ^= static_cast<const u8*>(data)[i] << 8;
for (std::size_t j = 0; j < 8; j++) {
crc <<= 1;
if ((crc & 0x10000) != 0) {
crc = (crc ^ 0x1021) & 0xFFFF;
}
}
}
return Common::swap16(static_cast<u16>(crc));
}
static Common::UUID MakeCreateId() {
return Common::UUID::MakeRandomRFC4122V4();
}
static Common::UUID GetDeviceId() {
// This should be nn::settings::detail::GetMiiAuthorId()
return Common::UUID::MakeDefault();
}
template <typename T>
static T GetRandomValue(T min, T max) {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(static_cast<u64>(min),
static_cast<u64>(max));
return static_cast<T>(distribution(gen));
}
template <typename T>
static T GetRandomValue(T max) {
return GetRandomValue<T>({}, max);
}
static bool IsFontRegionValid(FontRegion font, std::span<const char16_t> text) {
// TODO: This function needs to check against the font tables
return true;
}
};
} // namespace Service::Mii

View file

@ -1,27 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "core/hle/service/mii/types.h"
namespace Service::Mii::RawData {
extern const std::array<Service::Mii::DefaultMii, 2> BaseMii;
extern const std::array<Service::Mii::DefaultMii, 6> DefaultMii;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFaceline;
extern const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineWrinkle;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineMakeup;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType;
extern const std::array<Service::Mii::RandomMiiData3, 9> RandomMiiHairColor;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyeType;
extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiEyeColor;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyebrowType;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiNoseType;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiMouthType;
extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiGlassType;
} // namespace Service::Mii::RawData

View file

@ -1,553 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <type_traits>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/uuid.h"
namespace Service::Mii {
enum class Age : u32 {
Young,
Normal,
Old,
All,
};
enum class BeardType : u32 {
None,
Beard1,
Beard2,
Beard3,
Beard4,
Beard5,
};
enum class BeardAndMustacheFlag : u32 {
Beard = 1,
Mustache,
All = Beard | Mustache,
};
DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
enum class FontRegion : u32 {
Standard,
China,
Korea,
Taiwan,
};
enum class Gender : u32 {
Male,
Female,
All,
Maximum = Female,
};
enum class HairFlip : u32 {
Left,
Right,
Maximum = Right,
};
enum class MustacheType : u32 {
None,
Mustache1,
Mustache2,
Mustache3,
Mustache4,
Mustache5,
};
enum class Race : u32 {
Black,
White,
Asian,
All,
};
enum class Source : u32 {
Database = 0,
Default = 1,
Account = 2,
Friend = 3,
};
enum class SourceFlag : u32 {
None = 0,
Database = 1 << 0,
Default = 1 << 1,
};
DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
// nn::mii::CharInfo
struct CharInfo {
Common::UUID uuid;
std::array<char16_t, 11> name;
u8 font_region;
u8 favorite_color;
u8 gender;
u8 height;
u8 build;
u8 type;
u8 region_move;
u8 faceline_type;
u8 faceline_color;
u8 faceline_wrinkle;
u8 faceline_make;
u8 hair_type;
u8 hair_color;
u8 hair_flip;
u8 eye_type;
u8 eye_color;
u8 eye_scale;
u8 eye_aspect;
u8 eye_rotate;
u8 eye_x;
u8 eye_y;
u8 eyebrow_type;
u8 eyebrow_color;
u8 eyebrow_scale;
u8 eyebrow_aspect;
u8 eyebrow_rotate;
u8 eyebrow_x;
u8 eyebrow_y;
u8 nose_type;
u8 nose_scale;
u8 nose_y;
u8 mouth_type;
u8 mouth_color;
u8 mouth_scale;
u8 mouth_aspect;
u8 mouth_y;
u8 beard_color;
u8 beard_type;
u8 mustache_type;
u8 mustache_scale;
u8 mustache_y;
u8 glasses_type;
u8 glasses_color;
u8 glasses_scale;
u8 glasses_y;
u8 mole_type;
u8 mole_scale;
u8 mole_x;
u8 mole_y;
u8 padding;
};
static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size.");
static_assert(std::has_unique_object_representations_v<CharInfo>,
"All bits of CharInfo must contribute to its value.");
#pragma pack(push, 4)
struct MiiInfoElement {
MiiInfoElement(const CharInfo& info_, Source source_) : info{info_}, source{source_} {}
CharInfo info{};
Source source{};
};
static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
struct MiiStoreBitFields {
union {
u32 word_0{};
BitField<0, 8, u32> hair_type;
BitField<8, 7, u32> height;
BitField<15, 1, u32> mole_type;
BitField<16, 7, u32> build;
BitField<23, 1, HairFlip> hair_flip;
BitField<24, 7, u32> hair_color;
BitField<31, 1, u32> type;
};
union {
u32 word_1{};
BitField<0, 7, u32> eye_color;
BitField<7, 1, Gender> gender;
BitField<8, 7, u32> eyebrow_color;
BitField<16, 7, u32> mouth_color;
BitField<24, 7, u32> beard_color;
};
union {
u32 word_2{};
BitField<0, 7, u32> glasses_color;
BitField<8, 6, u32> eye_type;
BitField<14, 2, u32> region_move;
BitField<16, 6, u32> mouth_type;
BitField<22, 2, FontRegion> font_region;
BitField<24, 5, u32> eye_y;
BitField<29, 3, u32> glasses_scale;
};
union {
u32 word_3{};
BitField<0, 5, u32> eyebrow_type;
BitField<5, 3, MustacheType> mustache_type;
BitField<8, 5, u32> nose_type;
BitField<13, 3, BeardType> beard_type;
BitField<16, 5, u32> nose_y;
BitField<21, 3, u32> mouth_aspect;
BitField<24, 5, u32> mouth_y;
BitField<29, 3, u32> eyebrow_aspect;
};
union {
u32 word_4{};
BitField<0, 5, u32> mustache_y;
BitField<5, 3, u32> eye_rotate;
BitField<8, 5, u32> glasses_y;
BitField<13, 3, u32> eye_aspect;
BitField<16, 5, u32> mole_x;
BitField<21, 3, u32> eye_scale;
BitField<24, 5, u32> mole_y;
};
union {
u32 word_5{};
BitField<0, 5, u32> glasses_type;
BitField<8, 4, u32> favorite_color;
BitField<12, 4, u32> faceline_type;
BitField<16, 4, u32> faceline_color;
BitField<20, 4, u32> faceline_wrinkle;
BitField<24, 4, u32> faceline_makeup;
BitField<28, 4, u32> eye_x;
};
union {
u32 word_6{};
BitField<0, 4, u32> eyebrow_scale;
BitField<4, 4, u32> eyebrow_rotate;
BitField<8, 4, u32> eyebrow_x;
BitField<12, 4, u32> eyebrow_y;
BitField<16, 4, u32> nose_scale;
BitField<20, 4, u32> mouth_scale;
BitField<24, 4, u32> mustache_scale;
BitField<28, 4, u32> mole_scale;
};
};
static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
"MiiStoreBitFields is not trivially copyable.");
// This is nn::mii::Ver3StoreData
// Based on citra HLE::Applets::MiiData and PretendoNetwork.
// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48
// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299
struct Ver3StoreData {
u8 version;
union {
u8 raw;
BitField<0, 1, u8> allow_copying;
BitField<1, 1, u8> profanity_flag;
BitField<2, 2, u8> region_lock;
BitField<4, 2, u8> character_set;
} region_information;
u16_be mii_id;
u64_be system_id;
u32_be specialness_and_creation_date;
std::array<u8, 0x6> creator_mac;
u16_be padding;
union {
u16 raw;
BitField<0, 1, u16> gender;
BitField<1, 4, u16> birth_month;
BitField<5, 5, u16> birth_day;
BitField<10, 4, u16> favorite_color;
BitField<14, 1, u16> favorite;
} mii_information;
std::array<char16_t, 0xA> mii_name;
u8 height;
u8 build;
union {
u8 raw;
BitField<0, 1, u8> disable_sharing;
BitField<1, 4, u8> face_shape;
BitField<5, 3, u8> skin_color;
} appearance_bits1;
union {
u8 raw;
BitField<0, 4, u8> wrinkles;
BitField<4, 4, u8> makeup;
} appearance_bits2;
u8 hair_style;
union {
u8 raw;
BitField<0, 3, u8> hair_color;
BitField<3, 1, u8> flip_hair;
} appearance_bits3;
union {
u32 raw;
BitField<0, 6, u32> eye_type;
BitField<6, 3, u32> eye_color;
BitField<9, 4, u32> eye_scale;
BitField<13, 3, u32> eye_vertical_stretch;
BitField<16, 5, u32> eye_rotation;
BitField<21, 4, u32> eye_spacing;
BitField<25, 5, u32> eye_y_position;
} appearance_bits4;
union {
u32 raw;
BitField<0, 5, u32> eyebrow_style;
BitField<5, 3, u32> eyebrow_color;
BitField<8, 4, u32> eyebrow_scale;
BitField<12, 3, u32> eyebrow_yscale;
BitField<16, 4, u32> eyebrow_rotation;
BitField<21, 4, u32> eyebrow_spacing;
BitField<25, 5, u32> eyebrow_y_position;
} appearance_bits5;
union {
u16 raw;
BitField<0, 5, u16> nose_type;
BitField<5, 4, u16> nose_scale;
BitField<9, 5, u16> nose_y_position;
} appearance_bits6;
union {
u16 raw;
BitField<0, 6, u16> mouth_type;
BitField<6, 3, u16> mouth_color;
BitField<9, 4, u16> mouth_scale;
BitField<13, 3, u16> mouth_horizontal_stretch;
} appearance_bits7;
union {
u8 raw;
BitField<0, 5, u8> mouth_y_position;
BitField<5, 3, u8> mustache_type;
} appearance_bits8;
u8 allow_copying;
union {
u16 raw;
BitField<0, 3, u16> bear_type;
BitField<3, 3, u16> facial_hair_color;
BitField<6, 4, u16> mustache_scale;
BitField<10, 5, u16> mustache_y_position;
} appearance_bits9;
union {
u16 raw;
BitField<0, 4, u16> glasses_type;
BitField<4, 3, u16> glasses_color;
BitField<7, 4, u16> glasses_scale;
BitField<11, 5, u16> glasses_y_position;
} appearance_bits10;
union {
u16 raw;
BitField<0, 1, u16> mole_enabled;
BitField<1, 4, u16> mole_scale;
BitField<5, 5, u16> mole_x_position;
BitField<10, 5, u16> mole_y_position;
} appearance_bits11;
std::array<u16_le, 0xA> author_name;
INSERT_PADDING_BYTES(0x2);
u16_be crc;
};
static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size");
struct NfpStoreDataExtension {
u8 faceline_color;
u8 hair_color;
u8 eye_color;
u8 eyebrow_color;
u8 mouth_color;
u8 beard_color;
u8 glass_color;
u8 glass_type;
};
static_assert(sizeof(NfpStoreDataExtension) == 0x8, "NfpStoreDataExtension is an invalid size");
constexpr std::array<u8, 0x10> Ver3FacelineColorTable{
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x0, 0x1, 0x5, 0x5,
};
constexpr std::array<u8, 100> Ver3HairColorTable{
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x4, 0x3, 0x5, 0x4, 0x4, 0x6, 0x2, 0x0,
0x6, 0x4, 0x3, 0x2, 0x2, 0x7, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x4,
0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5,
0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x7, 0x5, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x7,
0x7, 0x7, 0x7, 0x7, 0x3, 0x7, 0x7, 0x7, 0x7, 0x7, 0x0, 0x4, 0x4, 0x4, 0x4,
};
constexpr std::array<u8, 100> Ver3EyeColorTable{
0x0, 0x2, 0x2, 0x2, 0x1, 0x3, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x2, 0x2, 0x4,
0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x0, 0x4, 0x4,
0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5,
0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2,
0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
};
constexpr std::array<u8, 100> Ver3MouthlineColorTable{
0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4,
0x4, 0x4, 0x0, 0x1, 0x2, 0x3, 0x4, 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4,
0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4,
0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4,
0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3,
0x3, 0x3, 0x3, 0x3, 0x4, 0x0, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, 0x3, 0x3,
};
constexpr std::array<u8, 100> Ver3GlassColorTable{
0x0, 0x1, 0x1, 0x1, 0x5, 0x1, 0x1, 0x4, 0x0, 0x5, 0x1, 0x1, 0x3, 0x5, 0x1, 0x2, 0x3,
0x4, 0x5, 0x4, 0x2, 0x2, 0x4, 0x4, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3,
0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x0, 0x5, 0x5,
0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x1, 0x4,
0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5,
};
constexpr std::array<u8, 20> Ver3GlassTypeTable{
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1,
0x2, 0x1, 0x3, 0x7, 0x7, 0x6, 0x7, 0x8, 0x7, 0x7,
};
struct MiiStoreData {
using Name = std::array<char16_t, 10>;
MiiStoreData();
MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
const Common::UUID& user_id);
// This corresponds to the above structure MiiStoreBitFields. I did it like this because the
// BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
// not suitable for our uses.
struct {
std::array<u8, 0x1C> data{};
static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
Name name{};
Common::UUID uuid{};
} data;
u16 data_crc{};
u16 device_crc{};
};
static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
struct MiiStoreDataElement {
MiiStoreData data{};
Source source{};
};
static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
struct MiiDatabase {
u32 magic{}; // 'NFDB'
std::array<MiiStoreData, 0x64> miis{};
INSERT_PADDING_BYTES(1);
u8 count{};
u16 crc{};
};
static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
struct RandomMiiValues {
std::array<u8, 0xbc> values{};
};
static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
struct RandomMiiData4 {
Gender gender{};
Age age{};
Race race{};
u32 values_count{};
std::array<u32, 47> values{};
};
static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
struct RandomMiiData3 {
u32 arg_1;
u32 arg_2;
u32 values_count;
std::array<u32, 47> values{};
};
static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
struct RandomMiiData2 {
u32 arg_1;
u32 values_count;
std::array<u32, 47> values{};
};
static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
struct DefaultMii {
u32 face_type{};
u32 face_color{};
u32 face_wrinkle{};
u32 face_makeup{};
u32 hair_type{};
u32 hair_color{};
u32 hair_flip{};
u32 eye_type{};
u32 eye_color{};
u32 eye_scale{};
u32 eye_aspect{};
u32 eye_rotate{};
u32 eye_x{};
u32 eye_y{};
u32 eyebrow_type{};
u32 eyebrow_color{};
u32 eyebrow_scale{};
u32 eyebrow_aspect{};
u32 eyebrow_rotate{};
u32 eyebrow_x{};
u32 eyebrow_y{};
u32 nose_type{};
u32 nose_scale{};
u32 nose_y{};
u32 mouth_type{};
u32 mouth_color{};
u32 mouth_scale{};
u32 mouth_aspect{};
u32 mouth_y{};
u32 mustache_type{};
u32 beard_type{};
u32 beard_color{};
u32 mustache_scale{};
u32 mustache_y{};
u32 glasses_type{};
u32 glasses_color{};
u32 glasses_scale{};
u32 glasses_y{};
u32 mole_type{};
u32 mole_scale{};
u32 mole_x{};
u32 mole_y{};
u32 height{};
u32 weight{};
Gender gender{};
u32 favorite_color{};
u32 region{};
FontRegion font_region{};
u32 type{};
INSERT_PADDING_WORDS(5);
};
static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
#pragma pack(pop)
} // namespace Service::Mii

View file

@ -0,0 +1,482 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/mii/types/char_info.h"
#include "core/hle/service/mii/types/store_data.h"
namespace Service::Mii {
void CharInfo::SetFromStoreData(const StoreData& store_data) {
name = store_data.GetNickname();
null_terminator = '\0';
create_id = store_data.GetCreateId();
font_region = store_data.GetFontRegion();
favorite_color = store_data.GetFavoriteColor();
gender = store_data.GetGender();
height = store_data.GetHeight();
build = store_data.GetBuild();
type = store_data.GetType();
region_move = store_data.GetRegionMove();
faceline_type = store_data.GetFacelineType();
faceline_color = store_data.GetFacelineColor();
faceline_wrinkle = store_data.GetFacelineWrinkle();
faceline_make = store_data.GetFacelineMake();
hair_type = store_data.GetHairType();
hair_color = store_data.GetHairColor();
hair_flip = store_data.GetHairFlip();
eye_type = store_data.GetEyeType();
eye_color = store_data.GetEyeColor();
eye_scale = store_data.GetEyeScale();
eye_aspect = store_data.GetEyeAspect();
eye_rotate = store_data.GetEyeRotate();
eye_x = store_data.GetEyeX();
eye_y = store_data.GetEyeY();
eyebrow_type = store_data.GetEyebrowType();
eyebrow_color = store_data.GetEyebrowColor();
eyebrow_scale = store_data.GetEyebrowScale();
eyebrow_aspect = store_data.GetEyebrowAspect();
eyebrow_rotate = store_data.GetEyebrowRotate();
eyebrow_x = store_data.GetEyebrowX();
eyebrow_y = store_data.GetEyebrowY();
nose_type = store_data.GetNoseType();
nose_scale = store_data.GetNoseScale();
nose_y = store_data.GetNoseY();
mouth_type = store_data.GetMouthType();
mouth_color = store_data.GetMouthColor();
mouth_scale = store_data.GetMouthScale();
mouth_aspect = store_data.GetMouthAspect();
mouth_y = store_data.GetMouthY();
beard_color = store_data.GetBeardColor();
beard_type = store_data.GetBeardType();
mustache_type = store_data.GetMustacheType();
mustache_scale = store_data.GetMustacheScale();
mustache_y = store_data.GetMustacheY();
glass_type = store_data.GetGlassType();
glass_color = store_data.GetGlassColor();
glass_scale = store_data.GetGlassScale();
glass_y = store_data.GetGlassY();
mole_type = store_data.GetMoleType();
mole_scale = store_data.GetMoleScale();
mole_x = store_data.GetMoleX();
mole_y = store_data.GetMoleY();
padding = '\0';
}
ValidationResult CharInfo::Verify() const {
if (!create_id.IsValid()) {
return ValidationResult::InvalidCreateId;
}
if (!name.IsValid()) {
return ValidationResult::InvalidName;
}
if (font_region > FontRegion::Max) {
return ValidationResult::InvalidFont;
}
if (favorite_color > FavoriteColor::Max) {
return ValidationResult::InvalidColor;
}
if (gender > Gender::Max) {
return ValidationResult::InvalidGender;
}
if (height > MaxHeight) {
return ValidationResult::InvalidHeight;
}
if (build > MaxBuild) {
return ValidationResult::InvalidBuild;
}
if (type > MaxType) {
return ValidationResult::InvalidType;
}
if (region_move > MaxRegionMove) {
return ValidationResult::InvalidRegionMove;
}
if (faceline_type > FacelineType::Max) {
return ValidationResult::InvalidFacelineType;
}
if (faceline_color > FacelineColor::Max) {
return ValidationResult::InvalidFacelineColor;
}
if (faceline_wrinkle > FacelineWrinkle::Max) {
return ValidationResult::InvalidFacelineWrinkle;
}
if (faceline_make > FacelineMake::Max) {
return ValidationResult::InvalidFacelineMake;
}
if (hair_type > HairType::Max) {
return ValidationResult::InvalidHairType;
}
if (hair_color > CommonColor::Max) {
return ValidationResult::InvalidHairColor;
}
if (hair_flip > HairFlip::Max) {
return ValidationResult::InvalidHairFlip;
}
if (eye_type > EyeType::Max) {
return ValidationResult::InvalidEyeType;
}
if (eye_color > CommonColor::Max) {
return ValidationResult::InvalidEyeColor;
}
if (eye_scale > MaxEyeScale) {
return ValidationResult::InvalidEyeScale;
}
if (eye_aspect > MaxEyeAspect) {
return ValidationResult::InvalidEyeAspect;
}
if (eye_rotate > MaxEyeX) {
return ValidationResult::InvalidEyeRotate;
}
if (eye_x > MaxEyeX) {
return ValidationResult::InvalidEyeX;
}
if (eye_y > MaxEyeY) {
return ValidationResult::InvalidEyeY;
}
if (eyebrow_type > EyebrowType::Max) {
return ValidationResult::InvalidEyebrowType;
}
if (eyebrow_color > CommonColor::Max) {
return ValidationResult::InvalidEyebrowColor;
}
if (eyebrow_scale > MaxEyebrowScale) {
return ValidationResult::InvalidEyebrowScale;
}
if (eyebrow_aspect > MaxEyebrowAspect) {
return ValidationResult::InvalidEyebrowAspect;
}
if (eyebrow_rotate > MaxEyebrowRotate) {
return ValidationResult::InvalidEyebrowRotate;
}
if (eyebrow_x > MaxEyebrowX) {
return ValidationResult::InvalidEyebrowX;
}
if (eyebrow_y > MaxEyebrowY) {
return ValidationResult::InvalidEyebrowY;
}
if (nose_type > NoseType::Max) {
return ValidationResult::InvalidNoseType;
}
if (nose_scale > MaxNoseScale) {
return ValidationResult::InvalidNoseScale;
}
if (nose_y > MaxNoseY) {
return ValidationResult::InvalidNoseY;
}
if (mouth_type > MouthType::Max) {
return ValidationResult::InvalidMouthType;
}
if (mouth_color > CommonColor::Max) {
return ValidationResult::InvalidMouthColor;
}
if (mouth_scale > MaxMouthScale) {
return ValidationResult::InvalidMouthScale;
}
if (mouth_aspect > MaxMoutAspect) {
return ValidationResult::InvalidMouthAspect;
}
if (mouth_y > MaxMouthY) {
return ValidationResult::InvalidMoleY;
}
if (beard_color > CommonColor::Max) {
return ValidationResult::InvalidBeardColor;
}
if (beard_type > BeardType::Max) {
return ValidationResult::InvalidBeardType;
}
if (mustache_type > MustacheType::Max) {
return ValidationResult::InvalidMustacheType;
}
if (mustache_scale > MaxMustacheScale) {
return ValidationResult::InvalidMustacheScale;
}
if (mustache_y > MasMustacheY) {
return ValidationResult::InvalidMustacheY;
}
if (glass_type > GlassType::Max) {
return ValidationResult::InvalidGlassType;
}
if (glass_color > CommonColor::Max) {
return ValidationResult::InvalidGlassColor;
}
if (glass_scale > MaxGlassScale) {
return ValidationResult::InvalidGlassScale;
}
if (glass_y > MaxGlassY) {
return ValidationResult::InvalidGlassY;
}
if (mole_type > MoleType::Max) {
return ValidationResult::InvalidMoleType;
}
if (mole_scale > MaxMoleScale) {
return ValidationResult::InvalidMoleScale;
}
if (mole_x > MaxMoleX) {
return ValidationResult::InvalidMoleX;
}
if (mole_y > MaxMoleY) {
return ValidationResult::InvalidMoleY;
}
return ValidationResult::NoErrors;
}
Common::UUID CharInfo::GetCreateId() const {
return create_id;
}
Nickname CharInfo::GetNickname() const {
return name;
}
FontRegion CharInfo::GetFontRegion() const {
return font_region;
}
FavoriteColor CharInfo::GetFavoriteColor() const {
return favorite_color;
}
Gender CharInfo::GetGender() const {
return gender;
}
u8 CharInfo::GetHeight() const {
return height;
}
u8 CharInfo::GetBuild() const {
return build;
}
u8 CharInfo::GetType() const {
return type;
}
u8 CharInfo::GetRegionMove() const {
return region_move;
}
FacelineType CharInfo::GetFacelineType() const {
return faceline_type;
}
FacelineColor CharInfo::GetFacelineColor() const {
return faceline_color;
}
FacelineWrinkle CharInfo::GetFacelineWrinkle() const {
return faceline_wrinkle;
}
FacelineMake CharInfo::GetFacelineMake() const {
return faceline_make;
}
HairType CharInfo::GetHairType() const {
return hair_type;
}
CommonColor CharInfo::GetHairColor() const {
return hair_color;
}
HairFlip CharInfo::GetHairFlip() const {
return hair_flip;
}
EyeType CharInfo::GetEyeType() const {
return eye_type;
}
CommonColor CharInfo::GetEyeColor() const {
return eye_color;
}
u8 CharInfo::GetEyeScale() const {
return eye_scale;
}
u8 CharInfo::GetEyeAspect() const {
return eye_aspect;
}
u8 CharInfo::GetEyeRotate() const {
return eye_rotate;
}
u8 CharInfo::GetEyeX() const {
return eye_x;
}
u8 CharInfo::GetEyeY() const {
return eye_y;
}
EyebrowType CharInfo::GetEyebrowType() const {
return eyebrow_type;
}
CommonColor CharInfo::GetEyebrowColor() const {
return eyebrow_color;
}
u8 CharInfo::GetEyebrowScale() const {
return eyebrow_scale;
}
u8 CharInfo::GetEyebrowAspect() const {
return eyebrow_aspect;
}
u8 CharInfo::GetEyebrowRotate() const {
return eyebrow_rotate;
}
u8 CharInfo::GetEyebrowX() const {
return eyebrow_x;
}
u8 CharInfo::GetEyebrowY() const {
return eyebrow_y;
}
NoseType CharInfo::GetNoseType() const {
return nose_type;
}
u8 CharInfo::GetNoseScale() const {
return nose_scale;
}
u8 CharInfo::GetNoseY() const {
return nose_y;
}
MouthType CharInfo::GetMouthType() const {
return mouth_type;
}
CommonColor CharInfo::GetMouthColor() const {
return mouth_color;
}
u8 CharInfo::GetMouthScale() const {
return mouth_scale;
}
u8 CharInfo::GetMouthAspect() const {
return mouth_aspect;
}
u8 CharInfo::GetMouthY() const {
return mouth_y;
}
CommonColor CharInfo::GetBeardColor() const {
return beard_color;
}
BeardType CharInfo::GetBeardType() const {
return beard_type;
}
MustacheType CharInfo::GetMustacheType() const {
return mustache_type;
}
u8 CharInfo::GetMustacheScale() const {
return mustache_scale;
}
u8 CharInfo::GetMustacheY() const {
return mustache_y;
}
GlassType CharInfo::GetGlassType() const {
return glass_type;
}
CommonColor CharInfo::GetGlassColor() const {
return glass_color;
}
u8 CharInfo::GetGlassScale() const {
return glass_scale;
}
u8 CharInfo::GetGlassY() const {
return glass_y;
}
MoleType CharInfo::GetMoleType() const {
return mole_type;
}
u8 CharInfo::GetMoleScale() const {
return mole_scale;
}
u8 CharInfo::GetMoleX() const {
return mole_x;
}
u8 CharInfo::GetMoleY() const {
return mole_y;
}
bool CharInfo::operator==(const CharInfo& info) {
bool is_identical = info.Verify() == ValidationResult::NoErrors;
is_identical &= name.data == info.GetNickname().data;
is_identical &= create_id == info.GetCreateId();
is_identical &= font_region == info.GetFontRegion();
is_identical &= favorite_color == info.GetFavoriteColor();
is_identical &= gender == info.GetGender();
is_identical &= height == info.GetHeight();
is_identical &= build == info.GetBuild();
is_identical &= type == info.GetType();
is_identical &= region_move == info.GetRegionMove();
is_identical &= faceline_type == info.GetFacelineType();
is_identical &= faceline_color == info.GetFacelineColor();
is_identical &= faceline_wrinkle == info.GetFacelineWrinkle();
is_identical &= faceline_make == info.GetFacelineMake();
is_identical &= hair_type == info.GetHairType();
is_identical &= hair_color == info.GetHairColor();
is_identical &= hair_flip == info.GetHairFlip();
is_identical &= eye_type == info.GetEyeType();
is_identical &= eye_color == info.GetEyeColor();
is_identical &= eye_scale == info.GetEyeScale();
is_identical &= eye_aspect == info.GetEyeAspect();
is_identical &= eye_rotate == info.GetEyeRotate();
is_identical &= eye_x == info.GetEyeX();
is_identical &= eye_y == info.GetEyeY();
is_identical &= eyebrow_type == info.GetEyebrowType();
is_identical &= eyebrow_color == info.GetEyebrowColor();
is_identical &= eyebrow_scale == info.GetEyebrowScale();
is_identical &= eyebrow_aspect == info.GetEyebrowAspect();
is_identical &= eyebrow_rotate == info.GetEyebrowRotate();
is_identical &= eyebrow_x == info.GetEyebrowX();
is_identical &= eyebrow_y == info.GetEyebrowY();
is_identical &= nose_type == info.GetNoseType();
is_identical &= nose_scale == info.GetNoseScale();
is_identical &= nose_y == info.GetNoseY();
is_identical &= mouth_type == info.GetMouthType();
is_identical &= mouth_color == info.GetMouthColor();
is_identical &= mouth_scale == info.GetMouthScale();
is_identical &= mouth_aspect == info.GetMouthAspect();
is_identical &= mouth_y == info.GetMouthY();
is_identical &= beard_color == info.GetBeardColor();
is_identical &= beard_type == info.GetBeardType();
is_identical &= mustache_type == info.GetMustacheType();
is_identical &= mustache_scale == info.GetMustacheScale();
is_identical &= mustache_y == info.GetMustacheY();
is_identical &= glass_type == info.GetGlassType();
is_identical &= glass_color == info.GetGlassColor();
is_identical &= glass_scale == info.GetGlassScale();
is_identical &= glass_y == info.GetGlassY();
is_identical &= mole_type == info.GetMoleType();
is_identical &= mole_scale == info.GetMoleScale();
is_identical &= mole_x == info.GetMoleX();
is_identical &= mole_y == info.GetMoleY();
return is_identical;
}
} // namespace Service::Mii

View file

@ -0,0 +1,137 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/mii/mii_types.h"
namespace Service::Mii {
class StoreData;
// This is nn::mii::detail::CharInfoRaw
class CharInfo {
public:
void SetFromStoreData(const StoreData& store_data_raw);
ValidationResult Verify() const;
Common::UUID GetCreateId() const;
Nickname GetNickname() const;
FontRegion GetFontRegion() const;
FavoriteColor GetFavoriteColor() const;
Gender GetGender() const;
u8 GetHeight() const;
u8 GetBuild() const;
u8 GetType() const;
u8 GetRegionMove() const;
FacelineType GetFacelineType() const;
FacelineColor GetFacelineColor() const;
FacelineWrinkle GetFacelineWrinkle() const;
FacelineMake GetFacelineMake() const;
HairType GetHairType() const;
CommonColor GetHairColor() const;
HairFlip GetHairFlip() const;
EyeType GetEyeType() const;
CommonColor GetEyeColor() const;
u8 GetEyeScale() const;
u8 GetEyeAspect() const;
u8 GetEyeRotate() const;
u8 GetEyeX() const;
u8 GetEyeY() const;
EyebrowType GetEyebrowType() const;
CommonColor GetEyebrowColor() const;
u8 GetEyebrowScale() const;
u8 GetEyebrowAspect() const;
u8 GetEyebrowRotate() const;
u8 GetEyebrowX() const;
u8 GetEyebrowY() const;
NoseType GetNoseType() const;
u8 GetNoseScale() const;
u8 GetNoseY() const;
MouthType GetMouthType() const;
CommonColor GetMouthColor() const;
u8 GetMouthScale() const;
u8 GetMouthAspect() const;
u8 GetMouthY() const;
CommonColor GetBeardColor() const;
BeardType GetBeardType() const;
MustacheType GetMustacheType() const;
u8 GetMustacheScale() const;
u8 GetMustacheY() const;
GlassType GetGlassType() const;
CommonColor GetGlassColor() const;
u8 GetGlassScale() const;
u8 GetGlassY() const;
MoleType GetMoleType() const;
u8 GetMoleScale() const;
u8 GetMoleX() const;
u8 GetMoleY() const;
bool operator==(const CharInfo& info);
private:
Common::UUID create_id;
Nickname name;
u16 null_terminator;
FontRegion font_region;
FavoriteColor favorite_color;
Gender gender;
u8 height;
u8 build;
u8 type;
u8 region_move;
FacelineType faceline_type;
FacelineColor faceline_color;
FacelineWrinkle faceline_wrinkle;
FacelineMake faceline_make;
HairType hair_type;
CommonColor hair_color;
HairFlip hair_flip;
EyeType eye_type;
CommonColor eye_color;
u8 eye_scale;
u8 eye_aspect;
u8 eye_rotate;
u8 eye_x;
u8 eye_y;
EyebrowType eyebrow_type;
CommonColor eyebrow_color;
u8 eyebrow_scale;
u8 eyebrow_aspect;
u8 eyebrow_rotate;
u8 eyebrow_x;
u8 eyebrow_y;
NoseType nose_type;
u8 nose_scale;
u8 nose_y;
MouthType mouth_type;
CommonColor mouth_color;
u8 mouth_scale;
u8 mouth_aspect;
u8 mouth_y;
CommonColor beard_color;
BeardType beard_type;
MustacheType mustache_type;
u8 mustache_scale;
u8 mustache_y;
GlassType glass_type;
CommonColor glass_color;
u8 glass_scale;
u8 glass_y;
MoleType mole_type;
u8 mole_scale;
u8 mole_x;
u8 mole_y;
u8 padding;
};
static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size.");
static_assert(std::has_unique_object_representations_v<CharInfo>,
"All bits of CharInfo must contribute to its value.");
struct CharInfoElement {
CharInfo char_info{};
Source source{};
};
static_assert(sizeof(CharInfoElement) == 0x5c, "CharInfoElement has incorrect size.");
}; // namespace Service::Mii

View file

@ -0,0 +1,601 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/hle/service/mii/mii_util.h"
#include "core/hle/service/mii/types/core_data.h"
#include "core/hle/service/mii/types/raw_data.h"
namespace Service::Mii {
void CoreData::SetDefault() {
data = {};
name = GetDefaultNickname();
}
void CoreData::BuildRandom(Age age, Gender gender, Race race) {
if (gender == Gender::All) {
gender = MiiUtil::GetRandomValue(Gender::Max);
}
if (age == Age::All) {
const auto random{MiiUtil::GetRandomValue<int>(10)};
if (random >= 8) {
age = Age::Old;
} else if (random >= 4) {
age = Age::Normal;
} else {
age = Age::Young;
}
}
if (race == Race::All) {
const auto random{MiiUtil::GetRandomValue<int>(10)};
if (random >= 8) {
race = Race::Black;
} else if (random >= 4) {
race = Race::White;
} else {
race = Race::Asian;
}
}
SetGender(gender);
SetFavoriteColor(MiiUtil::GetRandomValue(FavoriteColor::Max));
SetRegionMove(0);
SetFontRegion(FontRegion::Standard);
SetType(0);
SetHeight(64);
SetBuild(64);
u32 axis_y{};
if (gender == Gender::Female && age == Age::Young) {
axis_y = MiiUtil::GetRandomValue<u32>(3);
}
const std::size_t index{3 * static_cast<std::size_t>(age) +
9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)};
const auto& faceline_type_info{RawData::RandomMiiFaceline.at(index)};
const auto& faceline_color_info{RawData::RandomMiiFacelineColor.at(
3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))};
const auto& faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)};
const auto& faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)};
const auto& hair_type_info{RawData::RandomMiiHairType.at(index)};
const auto& hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast<std::size_t>(race) +
static_cast<std::size_t>(age))};
const auto& eye_type_info{RawData::RandomMiiEyeType.at(index)};
const auto& eye_color_info{RawData::RandomMiiEyeColor.at(static_cast<std::size_t>(race))};
const auto& eyebrow_type_info{RawData::RandomMiiEyebrowType.at(index)};
const auto& nose_type_info{RawData::RandomMiiNoseType.at(index)};
const auto& mouth_type_info{RawData::RandomMiiMouthType.at(index)};
const auto& glasses_type_info{RawData::RandomMiiGlassType.at(static_cast<std::size_t>(age))};
data.faceline_type.Assign(
faceline_type_info
.values[MiiUtil::GetRandomValue<std::size_t>(faceline_type_info.values_count)]);
data.faceline_color.Assign(
faceline_color_info
.values[MiiUtil::GetRandomValue<std::size_t>(faceline_color_info.values_count)]);
data.faceline_wrinkle.Assign(
faceline_wrinkle_info
.values[MiiUtil::GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]);
data.faceline_makeup.Assign(
faceline_makeup_info
.values[MiiUtil::GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]);
data.hair_type.Assign(
hair_type_info.values[MiiUtil::GetRandomValue<std::size_t>(hair_type_info.values_count)]);
SetHairColor(RawData::GetHairColorFromVer3(
hair_color_info
.values[MiiUtil::GetRandomValue<std::size_t>(hair_color_info.values_count)]));
SetHairFlip(MiiUtil::GetRandomValue(HairFlip::Max));
data.eye_type.Assign(
eye_type_info.values[MiiUtil::GetRandomValue<std::size_t>(eye_type_info.values_count)]);
const auto eye_rotate_1{gender != Gender::Male ? 4 : 2};
const auto eye_rotate_2{gender != Gender::Male ? 3 : 4};
const auto eye_rotate_offset{32 - RawData::EyeRotateLookup[eye_rotate_1] + eye_rotate_2};
const auto eye_rotate{32 - RawData::EyeRotateLookup[data.eye_type]};
SetEyeColor(RawData::GetEyeColorFromVer3(
eye_color_info.values[MiiUtil::GetRandomValue<std::size_t>(eye_color_info.values_count)]));
SetEyeScale(4);
SetEyeAspect(3);
SetEyeRotate(static_cast<u8>(eye_rotate_offset - eye_rotate));
SetEyeX(2);
SetEyeY(static_cast<u8>(axis_y + 12));
data.eyebrow_type.Assign(
eyebrow_type_info
.values[MiiUtil::GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]);
const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0};
const auto eyebrow_y{race == Race::Asian ? 9 : 10};
const auto eyebrow_rotate_offset{32 - RawData::EyebrowRotateLookup[eyebrow_rotate_1] + 6};
const auto eyebrow_rotate{
32 - RawData::EyebrowRotateLookup[static_cast<std::size_t>(data.eyebrow_type.Value())]};
SetEyebrowColor(GetHairColor());
SetEyebrowScale(4);
SetEyebrowAspect(3);
SetEyebrowRotate(static_cast<u8>(eyebrow_rotate_offset - eyebrow_rotate));
SetEyebrowX(2);
SetEyebrowY(static_cast<u8>(axis_y + eyebrow_y));
data.nose_type.Assign(
nose_type_info.values[MiiUtil::GetRandomValue<std::size_t>(nose_type_info.values_count)]);
SetNoseScale(gender == Gender::Female ? 3 : 4);
SetNoseY(static_cast<u8>(axis_y + 9));
const auto mouth_color{gender == Gender::Female ? MiiUtil::GetRandomValue<int>(4) : 0};
data.mouth_type.Assign(
mouth_type_info.values[MiiUtil::GetRandomValue<std::size_t>(mouth_type_info.values_count)]);
SetMouthColor(RawData::GetMouthColorFromVer3(mouth_color));
SetMouthScale(4);
SetMouthAspect(3);
SetMouthY(static_cast<u8>(axis_y + 13));
SetBeardColor(GetHairColor());
SetMustacheScale(4);
if (gender == Gender::Male && age != Age::Young && MiiUtil::GetRandomValue<int>(10) < 2) {
const auto mustache_and_beard_flag{MiiUtil::GetRandomValue(BeardAndMustacheFlag::All)};
auto beard_type{BeardType::None};
auto mustache_type{MustacheType::None};
if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) ==
BeardAndMustacheFlag::Beard) {
beard_type = MiiUtil::GetRandomValue(BeardType::Min, BeardType::Max);
}
if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) ==
BeardAndMustacheFlag::Mustache) {
mustache_type = MiiUtil::GetRandomValue(MustacheType::Min, MustacheType::Max);
}
SetMustacheType(mustache_type);
SetBeardType(beard_type);
SetMustacheY(10);
} else {
SetMustacheType(MustacheType::None);
SetBeardType(BeardType::None);
SetMustacheY(static_cast<u8>(axis_y + 10));
}
const auto glasses_type_start{MiiUtil::GetRandomValue<std::size_t>(100)};
u8 glasses_type{};
while (glasses_type_start < glasses_type_info.values[glasses_type]) {
if (++glasses_type >= glasses_type_info.values_count) {
ASSERT(false);
break;
}
}
SetGlassType(static_cast<GlassType>(glasses_type));
SetGlassColor(RawData::GetGlassColorFromVer3(0));
SetGlassScale(4);
SetMoleType(MoleType::None);
SetMoleScale(4);
SetMoleX(2);
SetMoleY(20);
}
u32 CoreData::IsValid() const {
// TODO: Complete this
return 0;
}
void CoreData::SetFontRegion(FontRegion value) {
data.font_region.Assign(static_cast<u32>(value));
}
void CoreData::SetFavoriteColor(FavoriteColor value) {
data.favorite_color.Assign(static_cast<u32>(value));
}
void CoreData::SetGender(Gender value) {
data.gender.Assign(static_cast<u32>(value));
}
void CoreData::SetHeight(u8 value) {
data.height.Assign(value);
}
void CoreData::SetBuild(u8 value) {
data.build.Assign(value);
}
void CoreData::SetType(u8 value) {
data.type.Assign(value);
}
void CoreData::SetRegionMove(u8 value) {
data.region_move.Assign(value);
}
void CoreData::SetFacelineType(FacelineType value) {
data.faceline_type.Assign(static_cast<u32>(value));
}
void CoreData::SetFacelineColor(FacelineColor value) {
data.faceline_color.Assign(static_cast<u32>(value));
}
void CoreData::SetFacelineWrinkle(FacelineWrinkle value) {
data.faceline_wrinkle.Assign(static_cast<u32>(value));
}
void CoreData::SetFacelineMake(FacelineMake value) {
data.faceline_makeup.Assign(static_cast<u32>(value));
}
void CoreData::SetHairType(HairType value) {
data.hair_type.Assign(static_cast<u32>(value));
}
void CoreData::SetHairColor(CommonColor value) {
data.hair_color.Assign(static_cast<u32>(value));
}
void CoreData::SetHairFlip(HairFlip value) {
data.hair_flip.Assign(static_cast<u32>(value));
}
void CoreData::SetEyeType(EyeType value) {
data.eye_type.Assign(static_cast<u32>(value));
}
void CoreData::SetEyeColor(CommonColor value) {
data.eye_color.Assign(static_cast<u32>(value));
}
void CoreData::SetEyeScale(u8 value) {
data.eye_scale.Assign(value);
}
void CoreData::SetEyeAspect(u8 value) {
data.eye_aspect.Assign(value);
}
void CoreData::SetEyeRotate(u8 value) {
data.eye_rotate.Assign(value);
}
void CoreData::SetEyeX(u8 value) {
data.eye_x.Assign(value);
}
void CoreData::SetEyeY(u8 value) {
data.eye_y.Assign(value);
}
void CoreData::SetEyebrowType(EyebrowType value) {
data.eyebrow_type.Assign(static_cast<u32>(value));
}
void CoreData::SetEyebrowColor(CommonColor value) {
data.eyebrow_color.Assign(static_cast<u32>(value));
}
void CoreData::SetEyebrowScale(u8 value) {
data.eyebrow_scale.Assign(value);
}
void CoreData::SetEyebrowAspect(u8 value) {
data.eyebrow_aspect.Assign(value);
}
void CoreData::SetEyebrowRotate(u8 value) {
data.eyebrow_rotate.Assign(value);
}
void CoreData::SetEyebrowX(u8 value) {
data.eyebrow_x.Assign(value);
}
void CoreData::SetEyebrowY(u8 value) {
data.eyebrow_y.Assign(value);
}
void CoreData::SetNoseType(NoseType value) {
data.nose_type.Assign(static_cast<u32>(value));
}
void CoreData::SetNoseScale(u8 value) {
data.nose_scale.Assign(value);
}
void CoreData::SetNoseY(u8 value) {
data.nose_y.Assign(value);
}
void CoreData::SetMouthType(u8 value) {
data.mouth_type.Assign(value);
}
void CoreData::SetMouthColor(CommonColor value) {
data.mouth_color.Assign(static_cast<u32>(value));
}
void CoreData::SetMouthScale(u8 value) {
data.mouth_scale.Assign(value);
}
void CoreData::SetMouthAspect(u8 value) {
data.mouth_aspect.Assign(value);
}
void CoreData::SetMouthY(u8 value) {
data.mouth_y.Assign(value);
}
void CoreData::SetBeardColor(CommonColor value) {
data.beard_color.Assign(static_cast<u32>(value));
}
void CoreData::SetBeardType(BeardType value) {
data.beard_type.Assign(static_cast<u32>(value));
}
void CoreData::SetMustacheType(MustacheType value) {
data.mustache_type.Assign(static_cast<u32>(value));
}
void CoreData::SetMustacheScale(u8 value) {
data.mustache_scale.Assign(value);
}
void CoreData::SetMustacheY(u8 value) {
data.mustache_y.Assign(value);
}
void CoreData::SetGlassType(GlassType value) {
data.glasses_type.Assign(static_cast<u32>(value));
}
void CoreData::SetGlassColor(CommonColor value) {
data.glasses_color.Assign(static_cast<u32>(value));
}
void CoreData::SetGlassScale(u8 value) {
data.glasses_scale.Assign(value);
}
void CoreData::SetGlassY(u8 value) {
data.glasses_y.Assign(value);
}
void CoreData::SetMoleType(MoleType value) {
data.mole_type.Assign(static_cast<u32>(value));
}
void CoreData::SetMoleScale(u8 value) {
data.mole_scale.Assign(value);
}
void CoreData::SetMoleX(u8 value) {
data.mole_x.Assign(value);
}
void CoreData::SetMoleY(u8 value) {
data.mole_y.Assign(value);
}
void CoreData::SetNickname(Nickname nickname) {
name = nickname;
}
FontRegion CoreData::GetFontRegion() const {
return static_cast<FontRegion>(data.font_region.Value());
}
FavoriteColor CoreData::GetFavoriteColor() const {
return static_cast<FavoriteColor>(data.favorite_color.Value());
}
Gender CoreData::GetGender() const {
return static_cast<Gender>(data.gender.Value());
}
u8 CoreData::GetHeight() const {
return static_cast<u8>(data.height.Value());
}
u8 CoreData::GetBuild() const {
return static_cast<u8>(data.build.Value());
}
u8 CoreData::GetType() const {
return static_cast<u8>(data.type.Value());
}
u8 CoreData::GetRegionMove() const {
return static_cast<u8>(data.region_move.Value());
}
FacelineType CoreData::GetFacelineType() const {
return static_cast<FacelineType>(data.faceline_type.Value());
}
FacelineColor CoreData::GetFacelineColor() const {
return static_cast<FacelineColor>(data.faceline_color.Value());
}
FacelineWrinkle CoreData::GetFacelineWrinkle() const {
return static_cast<FacelineWrinkle>(data.faceline_wrinkle.Value());
}
FacelineMake CoreData::GetFacelineMake() const {
return static_cast<FacelineMake>(data.faceline_makeup.Value());
}
HairType CoreData::GetHairType() const {
return static_cast<HairType>(data.hair_type.Value());
}
CommonColor CoreData::GetHairColor() const {
return static_cast<CommonColor>(data.hair_color.Value());
}
HairFlip CoreData::GetHairFlip() const {
return static_cast<HairFlip>(data.hair_flip.Value());
}
EyeType CoreData::GetEyeType() const {
return static_cast<EyeType>(data.eye_type.Value());
}
CommonColor CoreData::GetEyeColor() const {
return static_cast<CommonColor>(data.eye_color.Value());
}
u8 CoreData::GetEyeScale() const {
return static_cast<u8>(data.eye_scale.Value());
}
u8 CoreData::GetEyeAspect() const {
return static_cast<u8>(data.eye_aspect.Value());
}
u8 CoreData::GetEyeRotate() const {
return static_cast<u8>(data.eye_rotate.Value());
}
u8 CoreData::GetEyeX() const {
return static_cast<u8>(data.eye_x.Value());
}
u8 CoreData::GetEyeY() const {
return static_cast<u8>(data.eye_y.Value());
}
EyebrowType CoreData::GetEyebrowType() const {
return static_cast<EyebrowType>(data.eyebrow_type.Value());
}
CommonColor CoreData::GetEyebrowColor() const {
return static_cast<CommonColor>(data.eyebrow_color.Value());
}
u8 CoreData::GetEyebrowScale() const {
return static_cast<u8>(data.eyebrow_scale.Value());
}
u8 CoreData::GetEyebrowAspect() const {
return static_cast<u8>(data.eyebrow_aspect.Value());
}
u8 CoreData::GetEyebrowRotate() const {
return static_cast<u8>(data.eyebrow_rotate.Value());
}
u8 CoreData::GetEyebrowX() const {
return static_cast<u8>(data.eyebrow_x.Value());
}
u8 CoreData::GetEyebrowY() const {
return static_cast<u8>(data.eyebrow_y.Value());
}
NoseType CoreData::GetNoseType() const {
return static_cast<NoseType>(data.nose_type.Value());
}
u8 CoreData::GetNoseScale() const {
return static_cast<u8>(data.nose_scale.Value());
}
u8 CoreData::GetNoseY() const {
return static_cast<u8>(data.nose_y.Value());
}
MouthType CoreData::GetMouthType() const {
return static_cast<MouthType>(data.mouth_type.Value());
}
CommonColor CoreData::GetMouthColor() const {
return static_cast<CommonColor>(data.mouth_color.Value());
}
u8 CoreData::GetMouthScale() const {
return static_cast<u8>(data.mouth_scale.Value());
}
u8 CoreData::GetMouthAspect() const {
return static_cast<u8>(data.mouth_aspect.Value());
}
u8 CoreData::GetMouthY() const {
return static_cast<u8>(data.mouth_y.Value());
}
CommonColor CoreData::GetBeardColor() const {
return static_cast<CommonColor>(data.beard_color.Value());
}
BeardType CoreData::GetBeardType() const {
return static_cast<BeardType>(data.beard_type.Value());
}
MustacheType CoreData::GetMustacheType() const {
return static_cast<MustacheType>(data.mustache_type.Value());
}
u8 CoreData::GetMustacheScale() const {
return static_cast<u8>(data.mustache_scale.Value());
}
u8 CoreData::GetMustacheY() const {
return static_cast<u8>(data.mustache_y.Value());
}
GlassType CoreData::GetGlassType() const {
return static_cast<GlassType>(data.glasses_type.Value());
}
CommonColor CoreData::GetGlassColor() const {
return static_cast<CommonColor>(data.glasses_color.Value());
}
u8 CoreData::GetGlassScale() const {
return static_cast<u8>(data.glasses_scale.Value());
}
u8 CoreData::GetGlassY() const {
return static_cast<u8>(data.glasses_y.Value());
}
MoleType CoreData::GetMoleType() const {
return static_cast<MoleType>(data.mole_type.Value());
}
u8 CoreData::GetMoleScale() const {
return static_cast<u8>(data.mole_scale.Value());
}
u8 CoreData::GetMoleX() const {
return static_cast<u8>(data.mole_x.Value());
}
u8 CoreData::GetMoleY() const {
return static_cast<u8>(data.mole_y.Value());
}
Nickname CoreData::GetNickname() const {
return name;
}
Nickname CoreData::GetDefaultNickname() const {
return {u'n', u'o', u' ', u'n', u'a', u'm', u'e'};
}
Nickname CoreData::GetInvalidNickname() const {
return {u'?', u'?', u'?'};
}
} // namespace Service::Mii

View file

@ -0,0 +1,216 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/mii/mii_types.h"
namespace Service::Mii {
struct StoreDataBitFields {
union {
u32 word_0{};
BitField<0, 8, u32> hair_type;
BitField<8, 7, u32> height;
BitField<15, 1, u32> mole_type;
BitField<16, 7, u32> build;
BitField<23, 1, u32> hair_flip;
BitField<24, 7, u32> hair_color;
BitField<31, 1, u32> type;
};
union {
u32 word_1{};
BitField<0, 7, u32> eye_color;
BitField<7, 1, u32> gender;
BitField<8, 7, u32> eyebrow_color;
BitField<16, 7, u32> mouth_color;
BitField<24, 7, u32> beard_color;
};
union {
u32 word_2{};
BitField<0, 7, u32> glasses_color;
BitField<8, 6, u32> eye_type;
BitField<14, 2, u32> region_move;
BitField<16, 6, u32> mouth_type;
BitField<22, 2, u32> font_region;
BitField<24, 5, u32> eye_y;
BitField<29, 3, u32> glasses_scale;
};
union {
u32 word_3{};
BitField<0, 5, u32> eyebrow_type;
BitField<5, 3, u32> mustache_type;
BitField<8, 5, u32> nose_type;
BitField<13, 3, u32> beard_type;
BitField<16, 5, u32> nose_y;
BitField<21, 3, u32> mouth_aspect;
BitField<24, 5, u32> mouth_y;
BitField<29, 3, u32> eyebrow_aspect;
};
union {
u32 word_4{};
BitField<0, 5, u32> mustache_y;
BitField<5, 3, u32> eye_rotate;
BitField<8, 5, u32> glasses_y;
BitField<13, 3, u32> eye_aspect;
BitField<16, 5, u32> mole_x;
BitField<21, 3, u32> eye_scale;
BitField<24, 5, u32> mole_y;
};
union {
u32 word_5{};
BitField<0, 5, u32> glasses_type;
BitField<8, 4, u32> favorite_color;
BitField<12, 4, u32> faceline_type;
BitField<16, 4, u32> faceline_color;
BitField<20, 4, u32> faceline_wrinkle;
BitField<24, 4, u32> faceline_makeup;
BitField<28, 4, u32> eye_x;
};
union {
u32 word_6{};
BitField<0, 4, u32> eyebrow_scale;
BitField<4, 4, u32> eyebrow_rotate;
BitField<8, 4, u32> eyebrow_x;
BitField<12, 4, u32> eyebrow_y;
BitField<16, 4, u32> nose_scale;
BitField<20, 4, u32> mouth_scale;
BitField<24, 4, u32> mustache_scale;
BitField<28, 4, u32> mole_scale;
};
};
static_assert(sizeof(StoreDataBitFields) == 0x1c, "StoreDataBitFields has incorrect size.");
static_assert(std::is_trivially_copyable_v<StoreDataBitFields>,
"StoreDataBitFields is not trivially copyable.");
class CoreData {
public:
void SetDefault();
void BuildRandom(Age age, Gender gender, Race race);
u32 IsValid() const;
void SetFontRegion(FontRegion value);
void SetFavoriteColor(FavoriteColor value);
void SetGender(Gender value);
void SetHeight(u8 value);
void SetBuild(u8 value);
void SetType(u8 value);
void SetRegionMove(u8 value);
void SetFacelineType(FacelineType value);
void SetFacelineColor(FacelineColor value);
void SetFacelineWrinkle(FacelineWrinkle value);
void SetFacelineMake(FacelineMake value);
void SetHairType(HairType value);
void SetHairColor(CommonColor value);
void SetHairFlip(HairFlip value);
void SetEyeType(EyeType value);
void SetEyeColor(CommonColor value);
void SetEyeScale(u8 value);
void SetEyeAspect(u8 value);
void SetEyeRotate(u8 value);
void SetEyeX(u8 value);
void SetEyeY(u8 value);
void SetEyebrowType(EyebrowType value);
void SetEyebrowColor(CommonColor value);
void SetEyebrowScale(u8 value);
void SetEyebrowAspect(u8 value);
void SetEyebrowRotate(u8 value);
void SetEyebrowX(u8 value);
void SetEyebrowY(u8 value);
void SetNoseType(NoseType value);
void SetNoseScale(u8 value);
void SetNoseY(u8 value);
void SetMouthType(u8 value);
void SetMouthColor(CommonColor value);
void SetMouthScale(u8 value);
void SetMouthAspect(u8 value);
void SetMouthY(u8 value);
void SetBeardColor(CommonColor value);
void SetBeardType(BeardType value);
void SetMustacheType(MustacheType value);
void SetMustacheScale(u8 value);
void SetMustacheY(u8 value);
void SetGlassType(GlassType value);
void SetGlassColor(CommonColor value);
void SetGlassScale(u8 value);
void SetGlassY(u8 value);
void SetMoleType(MoleType value);
void SetMoleScale(u8 value);
void SetMoleX(u8 value);
void SetMoleY(u8 value);
void SetNickname(Nickname nickname);
FontRegion GetFontRegion() const;
FavoriteColor GetFavoriteColor() const;
Gender GetGender() const;
u8 GetHeight() const;
u8 GetBuild() const;
u8 GetType() const;
u8 GetRegionMove() const;
FacelineType GetFacelineType() const;
FacelineColor GetFacelineColor() const;
FacelineWrinkle GetFacelineWrinkle() const;
FacelineMake GetFacelineMake() const;
HairType GetHairType() const;
CommonColor GetHairColor() const;
HairFlip GetHairFlip() const;
EyeType GetEyeType() const;
CommonColor GetEyeColor() const;
u8 GetEyeScale() const;
u8 GetEyeAspect() const;
u8 GetEyeRotate() const;
u8 GetEyeX() const;
u8 GetEyeY() const;
EyebrowType GetEyebrowType() const;
CommonColor GetEyebrowColor() const;
u8 GetEyebrowScale() const;
u8 GetEyebrowAspect() const;
u8 GetEyebrowRotate() const;
u8 GetEyebrowX() const;
u8 GetEyebrowY() const;
NoseType GetNoseType() const;
u8 GetNoseScale() const;
u8 GetNoseY() const;
MouthType GetMouthType() const;
CommonColor GetMouthColor() const;
u8 GetMouthScale() const;
u8 GetMouthAspect() const;
u8 GetMouthY() const;
CommonColor GetBeardColor() const;
BeardType GetBeardType() const;
MustacheType GetMustacheType() const;
u8 GetMustacheScale() const;
u8 GetMustacheY() const;
GlassType GetGlassType() const;
CommonColor GetGlassColor() const;
u8 GetGlassScale() const;
u8 GetGlassY() const;
MoleType GetMoleType() const;
u8 GetMoleScale() const;
u8 GetMoleX() const;
u8 GetMoleY() const;
Nickname GetNickname() const;
Nickname GetDefaultNickname() const;
Nickname GetInvalidNickname() const;
private:
StoreDataBitFields data{};
Nickname name{};
};
static_assert(sizeof(CoreData) == 0x30, "CoreData has incorrect size.");
}; // namespace Service::Mii

View file

@ -0,0 +1,73 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "core/hle/service/mii/mii_types.h"
namespace Service::Mii::RawData {
struct RandomMiiValues {
std::array<u8, 188> values{};
};
static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
struct RandomMiiData4 {
u32 gender{};
u32 age{};
u32 race{};
u32 values_count{};
std::array<u32, 47> values{};
};
static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
struct RandomMiiData3 {
u32 arg_1;
u32 arg_2;
u32 values_count;
std::array<u32, 47> values{};
};
static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
struct RandomMiiData2 {
u32 arg_1;
u32 values_count;
std::array<u32, 47> values{};
};
static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
extern const std::array<Service::Mii::DefaultMii, 2> BaseMii;
extern const std::array<Service::Mii::DefaultMii, 6> DefaultMii;
extern const std::array<u8, 62> EyeRotateLookup;
extern const std::array<u8, 24> EyebrowRotateLookup;
extern const std::array<RandomMiiData4, 18> RandomMiiFaceline;
extern const std::array<RandomMiiData3, 6> RandomMiiFacelineColor;
extern const std::array<RandomMiiData4, 18> RandomMiiFacelineWrinkle;
extern const std::array<RandomMiiData4, 18> RandomMiiFacelineMakeup;
extern const std::array<RandomMiiData4, 18> RandomMiiHairType;
extern const std::array<RandomMiiData3, 9> RandomMiiHairColor;
extern const std::array<RandomMiiData4, 18> RandomMiiEyeType;
extern const std::array<RandomMiiData2, 3> RandomMiiEyeColor;
extern const std::array<RandomMiiData4, 18> RandomMiiEyebrowType;
extern const std::array<RandomMiiData4, 18> RandomMiiNoseType;
extern const std::array<RandomMiiData4, 18> RandomMiiMouthType;
extern const std::array<RandomMiiData2, 3> RandomMiiGlassType;
u8 FromVer3GetFacelineColor(u8 color);
u8 FromVer3GetHairColor(u8 color);
u8 FromVer3GetEyeColor(u8 color);
u8 FromVer3GetMouthlineColor(u8 color);
u8 FromVer3GetGlassColor(u8 color);
u8 FromVer3GetGlassType(u8 type);
FacelineColor GetFacelineColorFromVer3(u32 color);
CommonColor GetHairColorFromVer3(u32 color);
CommonColor GetEyeColorFromVer3(u32 color);
CommonColor GetMouthColorFromVer3(u32 color);
CommonColor GetGlassColorFromVer3(u32 color);
} // namespace Service::Mii::RawData

View file

@ -0,0 +1,643 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/mii/mii_util.h"
#include "core/hle/service/mii/types/raw_data.h"
#include "core/hle/service/mii/types/store_data.h"
namespace Service::Mii {
void StoreData::BuildDefault(u32 mii_index) {
const auto& default_mii = RawData::DefaultMii[mii_index];
core_data.SetDefault();
core_data.SetFacelineType(static_cast<FacelineType>(default_mii.face_type));
core_data.SetFacelineColor(
RawData::GetFacelineColorFromVer3(static_cast<u8>(default_mii.face_color)));
core_data.SetFacelineWrinkle(static_cast<FacelineWrinkle>(default_mii.face_wrinkle));
core_data.SetFacelineMake(static_cast<FacelineMake>(default_mii.face_makeup));
core_data.SetHairType(static_cast<HairType>(default_mii.hair_type));
core_data.SetHairColor(RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.hair_color)));
core_data.SetHairFlip(static_cast<HairFlip>(default_mii.hair_flip));
core_data.SetEyeType(static_cast<EyeType>(default_mii.eye_type));
core_data.SetEyeColor(RawData::GetEyeColorFromVer3(static_cast<u8>(default_mii.eye_color)));
core_data.SetEyeScale(static_cast<u8>(default_mii.eye_scale));
core_data.SetEyeAspect(static_cast<u8>(default_mii.eye_aspect));
core_data.SetEyeRotate(static_cast<u8>(default_mii.eye_rotate));
core_data.SetEyeX(static_cast<u8>(default_mii.eye_x));
core_data.SetEyeY(static_cast<u8>(default_mii.eye_y));
core_data.SetEyebrowType(static_cast<EyebrowType>(default_mii.eyebrow_type));
core_data.SetEyebrowColor(
RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.eyebrow_color)));
core_data.SetEyebrowScale(static_cast<u8>(default_mii.eyebrow_scale));
core_data.SetEyebrowAspect(static_cast<u8>(default_mii.eyebrow_aspect));
core_data.SetEyebrowRotate(static_cast<u8>(default_mii.eyebrow_rotate));
core_data.SetEyebrowX(static_cast<u8>(default_mii.eyebrow_x));
core_data.SetEyebrowY(static_cast<u8>(default_mii.eyebrow_y));
core_data.SetNoseType(static_cast<NoseType>(default_mii.nose_type));
core_data.SetNoseScale(static_cast<u8>(default_mii.nose_scale));
core_data.SetNoseY(static_cast<u8>(default_mii.nose_y));
core_data.SetMouthType(static_cast<u8>(default_mii.mouth_type));
core_data.SetMouthColor(
RawData::GetMouthColorFromVer3(static_cast<u8>(default_mii.mouth_color)));
core_data.SetMouthScale(static_cast<u8>(default_mii.mouth_scale));
core_data.SetMouthAspect(static_cast<u8>(default_mii.mouth_aspect));
core_data.SetMouthY(static_cast<u8>(default_mii.mouth_y));
core_data.SetMustacheType(static_cast<MustacheType>(default_mii.mustache_type));
core_data.SetBeardType(static_cast<BeardType>(default_mii.beard_type));
core_data.SetBeardColor(
RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.beard_color)));
core_data.SetMustacheScale(static_cast<u8>(default_mii.mustache_scale));
core_data.SetMustacheY(static_cast<u8>(default_mii.mustache_y));
core_data.SetGlassType(static_cast<GlassType>(default_mii.glasses_type));
core_data.SetGlassColor(
RawData::GetGlassColorFromVer3(static_cast<u8>(default_mii.glasses_color)));
core_data.SetGlassScale(static_cast<u8>(default_mii.glasses_scale));
core_data.SetGlassY(static_cast<u8>(default_mii.glasses_y));
core_data.SetMoleType(static_cast<MoleType>(default_mii.mole_type));
core_data.SetMoleScale(static_cast<u8>(default_mii.mole_scale));
core_data.SetMoleX(static_cast<u8>(default_mii.mole_x));
core_data.SetMoleY(static_cast<u8>(default_mii.mole_y));
core_data.SetHeight(static_cast<u8>(default_mii.height));
core_data.SetBuild(static_cast<u8>(default_mii.weight));
core_data.SetGender(static_cast<Gender>(default_mii.gender));
core_data.SetFavoriteColor(static_cast<FavoriteColor>(default_mii.favorite_color));
core_data.SetRegionMove(static_cast<u8>(default_mii.region_move));
core_data.SetFontRegion(static_cast<FontRegion>(default_mii.font_region));
core_data.SetType(static_cast<u8>(default_mii.type));
core_data.SetNickname(default_mii.nickname);
const auto device_id = MiiUtil::GetDeviceId();
create_id = MiiUtil::MakeCreateId();
device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID));
data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
}
void StoreData::BuildBase(Gender gender) {
const auto& default_mii = RawData::BaseMii[gender == Gender::Female ? 1 : 0];
core_data.SetDefault();
core_data.SetFacelineType(static_cast<FacelineType>(default_mii.face_type));
core_data.SetFacelineColor(
RawData::GetFacelineColorFromVer3(static_cast<u8>(default_mii.face_color)));
core_data.SetFacelineWrinkle(static_cast<FacelineWrinkle>(default_mii.face_wrinkle));
core_data.SetFacelineMake(static_cast<FacelineMake>(default_mii.face_makeup));
core_data.SetHairType(static_cast<HairType>(default_mii.hair_type));
core_data.SetHairColor(RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.hair_color)));
core_data.SetHairFlip(static_cast<HairFlip>(default_mii.hair_flip));
core_data.SetEyeType(static_cast<EyeType>(default_mii.eye_type));
core_data.SetEyeColor(RawData::GetEyeColorFromVer3(static_cast<u8>(default_mii.eye_color)));
core_data.SetEyeScale(static_cast<u8>(default_mii.eye_scale));
core_data.SetEyeAspect(static_cast<u8>(default_mii.eye_aspect));
core_data.SetEyeRotate(static_cast<u8>(default_mii.eye_rotate));
core_data.SetEyeX(static_cast<u8>(default_mii.eye_x));
core_data.SetEyeY(static_cast<u8>(default_mii.eye_y));
core_data.SetEyebrowType(static_cast<EyebrowType>(default_mii.eyebrow_type));
core_data.SetEyebrowColor(
RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.eyebrow_color)));
core_data.SetEyebrowScale(static_cast<u8>(default_mii.eyebrow_scale));
core_data.SetEyebrowAspect(static_cast<u8>(default_mii.eyebrow_aspect));
core_data.SetEyebrowRotate(static_cast<u8>(default_mii.eyebrow_rotate));
core_data.SetEyebrowX(static_cast<u8>(default_mii.eyebrow_x));
core_data.SetEyebrowY(static_cast<u8>(default_mii.eyebrow_y));
core_data.SetNoseType(static_cast<NoseType>(default_mii.nose_type));
core_data.SetNoseScale(static_cast<u8>(default_mii.nose_scale));
core_data.SetNoseY(static_cast<u8>(default_mii.nose_y));
core_data.SetMouthType(static_cast<u8>(default_mii.mouth_type));
core_data.SetMouthColor(
RawData::GetMouthColorFromVer3(static_cast<u8>(default_mii.mouth_color)));
core_data.SetMouthScale(static_cast<u8>(default_mii.mouth_scale));
core_data.SetMouthAspect(static_cast<u8>(default_mii.mouth_aspect));
core_data.SetMouthY(static_cast<u8>(default_mii.mouth_y));
core_data.SetMustacheType(static_cast<MustacheType>(default_mii.mustache_type));
core_data.SetBeardType(static_cast<BeardType>(default_mii.beard_type));
core_data.SetBeardColor(
RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.beard_color)));
core_data.SetMustacheScale(static_cast<u8>(default_mii.mustache_scale));
core_data.SetMustacheY(static_cast<u8>(default_mii.mustache_y));
core_data.SetGlassType(static_cast<GlassType>(default_mii.glasses_type));
core_data.SetGlassColor(
RawData::GetGlassColorFromVer3(static_cast<u8>(default_mii.glasses_color)));
core_data.SetGlassScale(static_cast<u8>(default_mii.glasses_scale));
core_data.SetGlassY(static_cast<u8>(default_mii.glasses_y));
core_data.SetMoleType(static_cast<MoleType>(default_mii.mole_type));
core_data.SetMoleScale(static_cast<u8>(default_mii.mole_scale));
core_data.SetMoleX(static_cast<u8>(default_mii.mole_x));
core_data.SetMoleY(static_cast<u8>(default_mii.mole_y));
core_data.SetHeight(static_cast<u8>(default_mii.height));
core_data.SetBuild(static_cast<u8>(default_mii.weight));
core_data.SetGender(static_cast<Gender>(default_mii.gender));
core_data.SetFavoriteColor(static_cast<FavoriteColor>(default_mii.favorite_color));
core_data.SetRegionMove(static_cast<u8>(default_mii.region_move));
core_data.SetFontRegion(static_cast<FontRegion>(default_mii.font_region));
core_data.SetType(static_cast<u8>(default_mii.type));
core_data.SetNickname(default_mii.nickname);
const auto device_id = MiiUtil::GetDeviceId();
create_id = MiiUtil::MakeCreateId();
device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID));
data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
}
void StoreData::BuildRandom(Age age, Gender gender, Race race) {
core_data.BuildRandom(age, gender, race);
const auto device_id = MiiUtil::GetDeviceId();
create_id = MiiUtil::MakeCreateId();
device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID));
data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
}
void StoreData::SetInvalidName() {
const auto& invalid_name = core_data.GetInvalidNickname();
const auto device_id = MiiUtil::GetDeviceId();
core_data.SetNickname(invalid_name);
device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID));
data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
}
bool StoreData::IsSpecial() const {
return GetType() == 1;
}
u32 StoreData::IsValid() const {
// TODO: complete this
return 0;
}
void StoreData::SetFontRegion(FontRegion value) {
core_data.SetFontRegion(value);
}
void StoreData::SetFavoriteColor(FavoriteColor value) {
core_data.SetFavoriteColor(value);
}
void StoreData::SetGender(Gender value) {
core_data.SetGender(value);
}
void StoreData::SetHeight(u8 value) {
core_data.SetHeight(value);
}
void StoreData::SetBuild(u8 value) {
core_data.SetBuild(value);
}
void StoreData::SetType(u8 value) {
core_data.SetType(value);
}
void StoreData::SetRegionMove(u8 value) {
core_data.SetRegionMove(value);
}
void StoreData::SetFacelineType(FacelineType value) {
core_data.SetFacelineType(value);
}
void StoreData::SetFacelineColor(FacelineColor value) {
core_data.SetFacelineColor(value);
}
void StoreData::SetFacelineWrinkle(FacelineWrinkle value) {
core_data.SetFacelineWrinkle(value);
}
void StoreData::SetFacelineMake(FacelineMake value) {
core_data.SetFacelineMake(value);
}
void StoreData::SetHairType(HairType value) {
core_data.SetHairType(value);
}
void StoreData::SetHairColor(CommonColor value) {
core_data.SetHairColor(value);
}
void StoreData::SetHairFlip(HairFlip value) {
core_data.SetHairFlip(value);
}
void StoreData::SetEyeType(EyeType value) {
core_data.SetEyeType(value);
}
void StoreData::SetEyeColor(CommonColor value) {
core_data.SetEyeColor(value);
}
void StoreData::SetEyeScale(u8 value) {
core_data.SetEyeScale(value);
}
void StoreData::SetEyeAspect(u8 value) {
core_data.SetEyeAspect(value);
}
void StoreData::SetEyeRotate(u8 value) {
core_data.SetEyeRotate(value);
}
void StoreData::SetEyeX(u8 value) {
core_data.SetEyeX(value);
}
void StoreData::SetEyeY(u8 value) {
core_data.SetEyeY(value);
}
void StoreData::SetEyebrowType(EyebrowType value) {
core_data.SetEyebrowType(value);
}
void StoreData::SetEyebrowColor(CommonColor value) {
core_data.SetEyebrowColor(value);
}
void StoreData::SetEyebrowScale(u8 value) {
core_data.SetEyebrowScale(value);
}
void StoreData::SetEyebrowAspect(u8 value) {
core_data.SetEyebrowAspect(value);
}
void StoreData::SetEyebrowRotate(u8 value) {
core_data.SetEyebrowRotate(value);
}
void StoreData::SetEyebrowX(u8 value) {
core_data.SetEyebrowX(value);
}
void StoreData::SetEyebrowY(u8 value) {
core_data.SetEyebrowY(value);
}
void StoreData::SetNoseType(NoseType value) {
core_data.SetNoseType(value);
}
void StoreData::SetNoseScale(u8 value) {
core_data.SetNoseScale(value);
}
void StoreData::SetNoseY(u8 value) {
core_data.SetNoseY(value);
}
void StoreData::SetMouthType(u8 value) {
core_data.SetMouthType(value);
}
void StoreData::SetMouthColor(CommonColor value) {
core_data.SetMouthColor(value);
}
void StoreData::SetMouthScale(u8 value) {
core_data.SetMouthScale(value);
}
void StoreData::SetMouthAspect(u8 value) {
core_data.SetMouthAspect(value);
}
void StoreData::SetMouthY(u8 value) {
core_data.SetMouthY(value);
}
void StoreData::SetBeardColor(CommonColor value) {
core_data.SetBeardColor(value);
}
void StoreData::SetBeardType(BeardType value) {
core_data.SetBeardType(value);
}
void StoreData::SetMustacheType(MustacheType value) {
core_data.SetMustacheType(value);
}
void StoreData::SetMustacheScale(u8 value) {
core_data.SetMustacheScale(value);
}
void StoreData::SetMustacheY(u8 value) {
core_data.SetMustacheY(value);
}
void StoreData::SetGlassType(GlassType value) {
core_data.SetGlassType(value);
}
void StoreData::SetGlassColor(CommonColor value) {
core_data.SetGlassColor(value);
}
void StoreData::SetGlassScale(u8 value) {
core_data.SetGlassScale(value);
}
void StoreData::SetGlassY(u8 value) {
core_data.SetGlassY(value);
}
void StoreData::SetMoleType(MoleType value) {
core_data.SetMoleType(value);
}
void StoreData::SetMoleScale(u8 value) {
core_data.SetMoleScale(value);
}
void StoreData::SetMoleX(u8 value) {
core_data.SetMoleX(value);
}
void StoreData::SetMoleY(u8 value) {
core_data.SetMoleY(value);
}
void StoreData::SetNickname(Nickname value) {
core_data.SetNickname(value);
}
Common::UUID StoreData::GetCreateId() const {
return create_id;
}
FontRegion StoreData::GetFontRegion() const {
return static_cast<FontRegion>(core_data.GetFontRegion());
}
FavoriteColor StoreData::GetFavoriteColor() const {
return core_data.GetFavoriteColor();
}
Gender StoreData::GetGender() const {
return core_data.GetGender();
}
u8 StoreData::GetHeight() const {
return core_data.GetHeight();
}
u8 StoreData::GetBuild() const {
return core_data.GetBuild();
}
u8 StoreData::GetType() const {
return core_data.GetType();
}
u8 StoreData::GetRegionMove() const {
return core_data.GetRegionMove();
}
FacelineType StoreData::GetFacelineType() const {
return core_data.GetFacelineType();
}
FacelineColor StoreData::GetFacelineColor() const {
return core_data.GetFacelineColor();
}
FacelineWrinkle StoreData::GetFacelineWrinkle() const {
return core_data.GetFacelineWrinkle();
}
FacelineMake StoreData::GetFacelineMake() const {
return core_data.GetFacelineMake();
}
HairType StoreData::GetHairType() const {
return core_data.GetHairType();
}
CommonColor StoreData::GetHairColor() const {
return core_data.GetHairColor();
}
HairFlip StoreData::GetHairFlip() const {
return core_data.GetHairFlip();
}
EyeType StoreData::GetEyeType() const {
return core_data.GetEyeType();
}
CommonColor StoreData::GetEyeColor() const {
return core_data.GetEyeColor();
}
u8 StoreData::GetEyeScale() const {
return core_data.GetEyeScale();
}
u8 StoreData::GetEyeAspect() const {
return core_data.GetEyeAspect();
}
u8 StoreData::GetEyeRotate() const {
return core_data.GetEyeRotate();
}
u8 StoreData::GetEyeX() const {
return core_data.GetEyeX();
}
u8 StoreData::GetEyeY() const {
return core_data.GetEyeY();
}
EyebrowType StoreData::GetEyebrowType() const {
return core_data.GetEyebrowType();
}
CommonColor StoreData::GetEyebrowColor() const {
return core_data.GetEyebrowColor();
}
u8 StoreData::GetEyebrowScale() const {
return core_data.GetEyebrowScale();
}
u8 StoreData::GetEyebrowAspect() const {
return core_data.GetEyebrowAspect();
}
u8 StoreData::GetEyebrowRotate() const {
return core_data.GetEyebrowRotate();
}
u8 StoreData::GetEyebrowX() const {
return core_data.GetEyebrowX();
}
u8 StoreData::GetEyebrowY() const {
return core_data.GetEyebrowY();
}
NoseType StoreData::GetNoseType() const {
return core_data.GetNoseType();
}
u8 StoreData::GetNoseScale() const {
return core_data.GetNoseScale();
}
u8 StoreData::GetNoseY() const {
return core_data.GetNoseY();
}
MouthType StoreData::GetMouthType() const {
return core_data.GetMouthType();
}
CommonColor StoreData::GetMouthColor() const {
return core_data.GetMouthColor();
}
u8 StoreData::GetMouthScale() const {
return core_data.GetMouthScale();
}
u8 StoreData::GetMouthAspect() const {
return core_data.GetMouthAspect();
}
u8 StoreData::GetMouthY() const {
return core_data.GetMouthY();
}
CommonColor StoreData::GetBeardColor() const {
return core_data.GetBeardColor();
}
BeardType StoreData::GetBeardType() const {
return core_data.GetBeardType();
}
MustacheType StoreData::GetMustacheType() const {
return core_data.GetMustacheType();
}
u8 StoreData::GetMustacheScale() const {
return core_data.GetMustacheScale();
}
u8 StoreData::GetMustacheY() const {
return core_data.GetMustacheY();
}
GlassType StoreData::GetGlassType() const {
return core_data.GetGlassType();
}
CommonColor StoreData::GetGlassColor() const {
return core_data.GetGlassColor();
}
u8 StoreData::GetGlassScale() const {
return core_data.GetGlassScale();
}
u8 StoreData::GetGlassY() const {
return core_data.GetGlassY();
}
MoleType StoreData::GetMoleType() const {
return core_data.GetMoleType();
}
u8 StoreData::GetMoleScale() const {
return core_data.GetMoleScale();
}
u8 StoreData::GetMoleX() const {
return core_data.GetMoleX();
}
u8 StoreData::GetMoleY() const {
return core_data.GetMoleY();
}
Nickname StoreData::GetNickname() const {
return core_data.GetNickname();
}
bool StoreData::operator==(const StoreData& data) {
bool is_identical = data.core_data.IsValid() == 0;
is_identical &= core_data.GetNickname().data == data.core_data.GetNickname().data;
is_identical &= GetCreateId() == data.GetCreateId();
is_identical &= GetFontRegion() == data.GetFontRegion();
is_identical &= GetFavoriteColor() == data.GetFavoriteColor();
is_identical &= GetGender() == data.GetGender();
is_identical &= GetHeight() == data.GetHeight();
is_identical &= GetBuild() == data.GetBuild();
is_identical &= GetType() == data.GetType();
is_identical &= GetRegionMove() == data.GetRegionMove();
is_identical &= GetFacelineType() == data.GetFacelineType();
is_identical &= GetFacelineColor() == data.GetFacelineColor();
is_identical &= GetFacelineWrinkle() == data.GetFacelineWrinkle();
is_identical &= GetFacelineMake() == data.GetFacelineMake();
is_identical &= GetHairType() == data.GetHairType();
is_identical &= GetHairColor() == data.GetHairColor();
is_identical &= GetHairFlip() == data.GetHairFlip();
is_identical &= GetEyeType() == data.GetEyeType();
is_identical &= GetEyeColor() == data.GetEyeColor();
is_identical &= GetEyeScale() == data.GetEyeScale();
is_identical &= GetEyeAspect() == data.GetEyeAspect();
is_identical &= GetEyeRotate() == data.GetEyeRotate();
is_identical &= GetEyeX() == data.GetEyeX();
is_identical &= GetEyeY() == data.GetEyeY();
is_identical &= GetEyebrowType() == data.GetEyebrowType();
is_identical &= GetEyebrowColor() == data.GetEyebrowColor();
is_identical &= GetEyebrowScale() == data.GetEyebrowScale();
is_identical &= GetEyebrowAspect() == data.GetEyebrowAspect();
is_identical &= GetEyebrowRotate() == data.GetEyebrowRotate();
is_identical &= GetEyebrowX() == data.GetEyebrowX();
is_identical &= GetEyebrowY() == data.GetEyebrowY();
is_identical &= GetNoseType() == data.GetNoseType();
is_identical &= GetNoseScale() == data.GetNoseScale();
is_identical &= GetNoseY() == data.GetNoseY();
is_identical &= GetMouthType() == data.GetMouthType();
is_identical &= GetMouthColor() == data.GetMouthColor();
is_identical &= GetMouthScale() == data.GetMouthScale();
is_identical &= GetMouthAspect() == data.GetMouthAspect();
is_identical &= GetMouthY() == data.GetMouthY();
is_identical &= GetBeardColor() == data.GetBeardColor();
is_identical &= GetBeardType() == data.GetBeardType();
is_identical &= GetMustacheType() == data.GetMustacheType();
is_identical &= GetMustacheScale() == data.GetMustacheScale();
is_identical &= GetMustacheY() == data.GetMustacheY();
is_identical &= GetGlassType() == data.GetGlassType();
is_identical &= GetGlassColor() == data.GetGlassColor();
is_identical &= GetGlassScale() == data.GetGlassScale();
is_identical &= GetGlassY() == data.GetGlassY();
is_identical &= GetMoleType() == data.GetMoleType();
is_identical &= GetMoleScale() == data.GetMoleScale();
is_identical &= GetMoleX() == data.GetMoleX();
is_identical &= data.GetMoleY() == data.GetMoleY();
return is_identical;
}
} // namespace Service::Mii

View file

@ -0,0 +1,145 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/mii/mii_types.h"
#include "core/hle/service/mii/types/core_data.h"
namespace Service::Mii {
class StoreData {
public:
// nn::mii::detail::StoreDataRaw::BuildDefault
void BuildDefault(u32 mii_index);
// nn::mii::detail::StoreDataRaw::BuildDefault
void BuildBase(Gender gender);
// nn::mii::detail::StoreDataRaw::BuildRandom
void BuildRandom(Age age, Gender gender, Race race);
bool IsSpecial() const;
u32 IsValid() const;
void SetFontRegion(FontRegion value);
void SetFavoriteColor(FavoriteColor value);
void SetGender(Gender value);
void SetHeight(u8 value);
void SetBuild(u8 value);
void SetType(u8 value);
void SetRegionMove(u8 value);
void SetFacelineType(FacelineType value);
void SetFacelineColor(FacelineColor value);
void SetFacelineWrinkle(FacelineWrinkle value);
void SetFacelineMake(FacelineMake value);
void SetHairType(HairType value);
void SetHairColor(CommonColor value);
void SetHairFlip(HairFlip value);
void SetEyeType(EyeType value);
void SetEyeColor(CommonColor value);
void SetEyeScale(u8 value);
void SetEyeAspect(u8 value);
void SetEyeRotate(u8 value);
void SetEyeX(u8 value);
void SetEyeY(u8 value);
void SetEyebrowType(EyebrowType value);
void SetEyebrowColor(CommonColor value);
void SetEyebrowScale(u8 value);
void SetEyebrowAspect(u8 value);
void SetEyebrowRotate(u8 value);
void SetEyebrowX(u8 value);
void SetEyebrowY(u8 value);
void SetNoseType(NoseType value);
void SetNoseScale(u8 value);
void SetNoseY(u8 value);
void SetMouthType(u8 value);
void SetMouthColor(CommonColor value);
void SetMouthScale(u8 value);
void SetMouthAspect(u8 value);
void SetMouthY(u8 value);
void SetBeardColor(CommonColor value);
void SetBeardType(BeardType value);
void SetMustacheType(MustacheType value);
void SetMustacheScale(u8 value);
void SetMustacheY(u8 value);
void SetGlassType(GlassType value);
void SetGlassColor(CommonColor value);
void SetGlassScale(u8 value);
void SetGlassY(u8 value);
void SetMoleType(MoleType value);
void SetMoleScale(u8 value);
void SetMoleX(u8 value);
void SetMoleY(u8 value);
void SetNickname(Nickname nickname);
void SetInvalidName();
Common::UUID GetCreateId() const;
FontRegion GetFontRegion() const;
FavoriteColor GetFavoriteColor() const;
Gender GetGender() const;
u8 GetHeight() const;
u8 GetBuild() const;
u8 GetType() const;
u8 GetRegionMove() const;
FacelineType GetFacelineType() const;
FacelineColor GetFacelineColor() const;
FacelineWrinkle GetFacelineWrinkle() const;
FacelineMake GetFacelineMake() const;
HairType GetHairType() const;
CommonColor GetHairColor() const;
HairFlip GetHairFlip() const;
EyeType GetEyeType() const;
CommonColor GetEyeColor() const;
u8 GetEyeScale() const;
u8 GetEyeAspect() const;
u8 GetEyeRotate() const;
u8 GetEyeX() const;
u8 GetEyeY() const;
EyebrowType GetEyebrowType() const;
CommonColor GetEyebrowColor() const;
u8 GetEyebrowScale() const;
u8 GetEyebrowAspect() const;
u8 GetEyebrowRotate() const;
u8 GetEyebrowX() const;
u8 GetEyebrowY() const;
NoseType GetNoseType() const;
u8 GetNoseScale() const;
u8 GetNoseY() const;
MouthType GetMouthType() const;
CommonColor GetMouthColor() const;
u8 GetMouthScale() const;
u8 GetMouthAspect() const;
u8 GetMouthY() const;
CommonColor GetBeardColor() const;
BeardType GetBeardType() const;
MustacheType GetMustacheType() const;
u8 GetMustacheScale() const;
u8 GetMustacheY() const;
GlassType GetGlassType() const;
CommonColor GetGlassColor() const;
u8 GetGlassScale() const;
u8 GetGlassY() const;
MoleType GetMoleType() const;
u8 GetMoleScale() const;
u8 GetMoleX() const;
u8 GetMoleY() const;
Nickname GetNickname() const;
bool operator==(const StoreData& data);
private:
CoreData core_data{};
Common::UUID create_id{};
u16 data_crc{};
u16 device_crc{};
};
static_assert(sizeof(StoreData) == 0x44, "StoreData has incorrect size.");
struct StoreDataElement {
StoreData store_data{};
Source source{};
};
static_assert(sizeof(StoreDataElement) == 0x48, "StoreDataElement has incorrect size.");
}; // namespace Service::Mii

View file

@ -0,0 +1,241 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/mii/mii_util.h"
#include "core/hle/service/mii/types/raw_data.h"
#include "core/hle/service/mii/types/store_data.h"
#include "core/hle/service/mii/types/ver3_store_data.h"
namespace Service::Mii {
void NfpStoreDataExtension::SetFromStoreData(const StoreData& store_data) {
faceline_color = static_cast<u8>(store_data.GetFacelineColor()) & 0xf;
hair_color = static_cast<u8>(store_data.GetHairColor()) & 0x7f;
eye_color = static_cast<u8>(store_data.GetEyeColor()) & 0x7f;
eyebrow_color = static_cast<u8>(store_data.GetEyebrowColor()) & 0x7f;
mouth_color = static_cast<u8>(store_data.GetMouthColor()) & 0x7f;
beard_color = static_cast<u8>(store_data.GetBeardColor()) & 0x7f;
glass_color = static_cast<u8>(store_data.GetGlassColor()) & 0x7f;
glass_type = static_cast<u8>(store_data.GetGlassType()) & 0x1f;
}
void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const {
out_store_data.BuildBase(Gender::Male);
if (!IsValid()) {
return;
}
// TODO: We are ignoring a bunch of data from the mii_v3
out_store_data.SetGender(static_cast<Gender>(mii_information.gender.Value()));
out_store_data.SetFavoriteColor(
static_cast<FavoriteColor>(mii_information.favorite_color.Value()));
out_store_data.SetHeight(height);
out_store_data.SetBuild(build);
out_store_data.SetNickname(mii_name);
out_store_data.SetFontRegion(
static_cast<FontRegion>(static_cast<u8>(region_information.font_region)));
out_store_data.SetFacelineType(
static_cast<FacelineType>(appearance_bits1.faceline_type.Value()));
out_store_data.SetFacelineColor(
static_cast<FacelineColor>(appearance_bits1.faceline_color.Value()));
out_store_data.SetFacelineWrinkle(
static_cast<FacelineWrinkle>(appearance_bits2.faceline_wrinkle.Value()));
out_store_data.SetFacelineMake(
static_cast<FacelineMake>(appearance_bits2.faceline_make.Value()));
out_store_data.SetHairType(static_cast<HairType>(hair_type));
out_store_data.SetHairColor(static_cast<CommonColor>(appearance_bits3.hair_color.Value()));
out_store_data.SetHairFlip(static_cast<HairFlip>(appearance_bits3.hair_flip.Value()));
out_store_data.SetEyeType(static_cast<EyeType>(appearance_bits4.eye_type.Value()));
out_store_data.SetEyeColor(static_cast<CommonColor>(appearance_bits4.eye_color.Value()));
out_store_data.SetEyeScale(static_cast<u8>(appearance_bits4.eye_scale));
out_store_data.SetEyeAspect(static_cast<u8>(appearance_bits4.eye_aspect));
out_store_data.SetEyeRotate(static_cast<u8>(appearance_bits4.eye_rotate));
out_store_data.SetEyeX(static_cast<u8>(appearance_bits4.eye_x));
out_store_data.SetEyeY(static_cast<u8>(appearance_bits4.eye_y));
out_store_data.SetEyebrowType(static_cast<EyebrowType>(appearance_bits5.eyebrow_type.Value()));
out_store_data.SetEyebrowColor(
static_cast<CommonColor>(appearance_bits5.eyebrow_color.Value()));
out_store_data.SetEyebrowScale(static_cast<u8>(appearance_bits5.eyebrow_scale));
out_store_data.SetEyebrowAspect(static_cast<u8>(appearance_bits5.eyebrow_aspect));
out_store_data.SetEyebrowRotate(static_cast<u8>(appearance_bits5.eyebrow_rotate));
out_store_data.SetEyebrowX(static_cast<u8>(appearance_bits5.eyebrow_x));
out_store_data.SetEyebrowY(static_cast<u8>(appearance_bits5.eyebrow_y));
out_store_data.SetNoseType(static_cast<NoseType>(appearance_bits6.nose_type.Value()));
out_store_data.SetNoseScale(static_cast<u8>(appearance_bits6.nose_scale));
out_store_data.SetNoseY(static_cast<u8>(appearance_bits6.nose_y));
out_store_data.SetMouthType(static_cast<u8>(appearance_bits7.mouth_type));
out_store_data.SetMouthColor(static_cast<CommonColor>(appearance_bits7.mouth_color.Value()));
out_store_data.SetMouthScale(static_cast<u8>(appearance_bits7.mouth_scale));
out_store_data.SetMouthAspect(static_cast<u8>(appearance_bits7.mouth_aspect));
out_store_data.SetMouthY(static_cast<u8>(appearance_bits8.mouth_y));
out_store_data.SetMustacheType(
static_cast<MustacheType>(appearance_bits8.mustache_type.Value()));
out_store_data.SetMustacheScale(static_cast<u8>(appearance_bits9.mustache_scale));
out_store_data.SetMustacheY(static_cast<u8>(appearance_bits9.mustache_y));
out_store_data.SetBeardType(static_cast<BeardType>(appearance_bits9.beard_type.Value()));
out_store_data.SetBeardColor(static_cast<CommonColor>(appearance_bits9.beard_color.Value()));
out_store_data.SetGlassType(static_cast<GlassType>(appearance_bits10.glass_type.Value()));
out_store_data.SetGlassColor(static_cast<CommonColor>(appearance_bits10.glass_color.Value()));
out_store_data.SetGlassScale(static_cast<u8>(appearance_bits10.glass_scale));
out_store_data.SetGlassY(static_cast<u8>(appearance_bits10.glass_y));
out_store_data.SetMoleType(static_cast<MoleType>(appearance_bits11.mole_type.Value()));
out_store_data.SetMoleScale(static_cast<u8>(appearance_bits11.mole_scale));
out_store_data.SetMoleX(static_cast<u8>(appearance_bits11.mole_x));
out_store_data.SetMoleY(static_cast<u8>(appearance_bits11.mole_y));
}
void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) {
version = 1;
mii_information.gender.Assign(static_cast<u8>(store_data.GetGender()));
mii_information.favorite_color.Assign(static_cast<u8>(store_data.GetFavoriteColor()));
height = store_data.GetHeight();
build = store_data.GetBuild();
mii_name = store_data.GetNickname();
region_information.font_region.Assign(static_cast<u8>(store_data.GetFontRegion()));
appearance_bits1.faceline_type.Assign(static_cast<u8>(store_data.GetFacelineType()));
appearance_bits2.faceline_wrinkle.Assign(static_cast<u8>(store_data.GetFacelineWrinkle()));
appearance_bits2.faceline_make.Assign(static_cast<u8>(store_data.GetFacelineMake()));
hair_type = static_cast<u8>(store_data.GetHairType());
appearance_bits3.hair_flip.Assign(static_cast<u8>(store_data.GetHairFlip()));
appearance_bits4.eye_type.Assign(static_cast<u8>(store_data.GetEyeType()));
appearance_bits4.eye_scale.Assign(store_data.GetEyeScale());
appearance_bits4.eye_aspect.Assign(store_data.GetEyebrowAspect());
appearance_bits4.eye_rotate.Assign(store_data.GetEyeRotate());
appearance_bits4.eye_x.Assign(store_data.GetEyeX());
appearance_bits4.eye_y.Assign(store_data.GetEyeY());
appearance_bits5.eyebrow_type.Assign(static_cast<u8>(store_data.GetEyebrowType()));
appearance_bits5.eyebrow_scale.Assign(store_data.GetEyebrowScale());
appearance_bits5.eyebrow_aspect.Assign(store_data.GetEyebrowAspect());
appearance_bits5.eyebrow_rotate.Assign(store_data.GetEyebrowRotate());
appearance_bits5.eyebrow_x.Assign(store_data.GetEyebrowX());
appearance_bits5.eyebrow_y.Assign(store_data.GetEyebrowY());
appearance_bits6.nose_type.Assign(static_cast<u8>(store_data.GetNoseType()));
appearance_bits6.nose_scale.Assign(store_data.GetNoseScale());
appearance_bits6.nose_y.Assign(store_data.GetNoseY());
appearance_bits7.mouth_type.Assign(static_cast<u8>(store_data.GetMouthType()));
appearance_bits7.mouth_scale.Assign(store_data.GetMouthScale());
appearance_bits7.mouth_aspect.Assign(store_data.GetMouthAspect());
appearance_bits8.mouth_y.Assign(store_data.GetMouthY());
appearance_bits8.mustache_type.Assign(static_cast<u8>(store_data.GetMustacheType()));
appearance_bits9.mustache_scale.Assign(store_data.GetMustacheScale());
appearance_bits9.mustache_y.Assign(store_data.GetMustacheY());
appearance_bits9.beard_type.Assign(static_cast<u8>(store_data.GetBeardType()));
appearance_bits10.glass_scale.Assign(store_data.GetGlassScale());
appearance_bits10.glass_y.Assign(store_data.GetGlassY());
appearance_bits11.mole_type.Assign(static_cast<u8>(store_data.GetMoleType()));
appearance_bits11.mole_scale.Assign(store_data.GetMoleScale());
appearance_bits11.mole_x.Assign(store_data.GetMoleX());
appearance_bits11.mole_y.Assign(store_data.GetMoleY());
// These types are converted to V3 from a table
appearance_bits1.faceline_color.Assign(
RawData::FromVer3GetFacelineColor(static_cast<u8>(store_data.GetFacelineColor())));
appearance_bits3.hair_color.Assign(
RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetHairColor())));
appearance_bits4.eye_color.Assign(
RawData::FromVer3GetEyeColor(static_cast<u8>(store_data.GetEyeColor())));
appearance_bits5.eyebrow_color.Assign(
RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetEyebrowColor())));
appearance_bits7.mouth_color.Assign(
RawData::FromVer3GetMouthlineColor(static_cast<u8>(store_data.GetMouthColor())));
appearance_bits9.beard_color.Assign(
RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetBeardColor())));
appearance_bits10.glass_color.Assign(
RawData::FromVer3GetGlassColor(static_cast<u8>(store_data.GetGlassColor())));
appearance_bits10.glass_type.Assign(
RawData::FromVer3GetGlassType(static_cast<u8>(store_data.GetGlassType())));
crc = MiiUtil::CalculateCrc16(&version, sizeof(Ver3StoreData) - sizeof(u16));
}
u32 Ver3StoreData::IsValid() const {
bool is_valid = version == 0 || version == 3;
is_valid = is_valid && (mii_name.data[0] != '\0');
is_valid = is_valid && (mii_information.birth_month < 13);
is_valid = is_valid && (mii_information.birth_day < 32);
is_valid = is_valid && (mii_information.favorite_color <= static_cast<u8>(FavoriteColor::Max));
is_valid = is_valid && (height <= MaxHeight);
is_valid = is_valid && (build <= MaxBuild);
is_valid = is_valid && (appearance_bits1.faceline_type <= static_cast<u8>(FacelineType::Max));
is_valid = is_valid && (appearance_bits1.faceline_color <= MaxVer3CommonColor - 2);
is_valid =
is_valid && (appearance_bits2.faceline_wrinkle <= static_cast<u8>(FacelineWrinkle::Max));
is_valid = is_valid && (appearance_bits2.faceline_make <= static_cast<u8>(FacelineMake::Max));
is_valid = is_valid && (hair_type <= static_cast<u8>(HairType::Max));
is_valid = is_valid && (appearance_bits3.hair_color <= MaxVer3CommonColor);
is_valid = is_valid && (appearance_bits4.eye_type <= static_cast<u8>(EyeType::Max));
is_valid = is_valid && (appearance_bits4.eye_color <= MaxVer3CommonColor - 2);
is_valid = is_valid && (appearance_bits4.eye_scale <= MaxEyeScale);
is_valid = is_valid && (appearance_bits4.eye_aspect <= MaxEyeAspect);
is_valid = is_valid && (appearance_bits4.eye_rotate <= MaxEyeRotate);
is_valid = is_valid && (appearance_bits4.eye_x <= MaxEyeX);
is_valid = is_valid && (appearance_bits4.eye_y <= MaxEyeY);
is_valid = is_valid && (appearance_bits5.eyebrow_type <= static_cast<u8>(EyebrowType::Max));
is_valid = is_valid && (appearance_bits5.eyebrow_color <= MaxVer3CommonColor);
is_valid = is_valid && (appearance_bits5.eyebrow_scale <= MaxEyebrowScale);
is_valid = is_valid && (appearance_bits5.eyebrow_aspect <= MaxEyebrowAspect);
is_valid = is_valid && (appearance_bits5.eyebrow_rotate <= MaxEyebrowRotate);
is_valid = is_valid && (appearance_bits5.eyebrow_x <= MaxEyebrowX);
is_valid = is_valid && (appearance_bits5.eyebrow_y <= MaxEyebrowY);
is_valid = is_valid && (appearance_bits6.nose_type <= static_cast<u8>(NoseType::Max));
is_valid = is_valid && (appearance_bits6.nose_scale <= MaxNoseScale);
is_valid = is_valid && (appearance_bits6.nose_y <= MaxNoseY);
is_valid = is_valid && (appearance_bits7.mouth_type <= static_cast<u8>(MouthType::Max));
is_valid = is_valid && (appearance_bits7.mouth_color <= MaxVer3CommonColor - 3);
is_valid = is_valid && (appearance_bits7.mouth_scale <= MaxMouthScale);
is_valid = is_valid && (appearance_bits7.mouth_aspect <= MaxMoutAspect);
is_valid = is_valid && (appearance_bits8.mouth_y <= MaxMouthY);
is_valid = is_valid && (appearance_bits8.mustache_type <= static_cast<u8>(MustacheType::Max));
is_valid = is_valid && (appearance_bits9.mustache_scale < MaxMustacheScale);
is_valid = is_valid && (appearance_bits9.mustache_y <= MasMustacheY);
is_valid = is_valid && (appearance_bits9.beard_type <= static_cast<u8>(BeardType::Max));
is_valid = is_valid && (appearance_bits9.beard_color <= MaxVer3CommonColor);
is_valid = is_valid && (appearance_bits10.glass_type <= MaxVer3GlassType);
is_valid = is_valid && (appearance_bits10.glass_color <= MaxVer3CommonColor - 2);
is_valid = is_valid && (appearance_bits10.glass_scale <= MaxGlassScale);
is_valid = is_valid && (appearance_bits10.glass_y <= MaxGlassScale);
is_valid = is_valid && (appearance_bits11.mole_type <= static_cast<u8>(MoleType::Max));
is_valid = is_valid && (appearance_bits11.mole_scale <= MaxMoleScale);
is_valid = is_valid && (appearance_bits11.mole_x <= MaxMoleX);
is_valid = is_valid && (appearance_bits11.mole_y <= MaxMoleY);
return is_valid;
}
} // namespace Service::Mii

View file

@ -0,0 +1,160 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/mii/mii_types.h"
namespace Service::Mii {
class StoreData;
// This is nn::mii::Ver3StoreData
// Based on citra HLE::Applets::MiiData and PretendoNetwork.
// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48
// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299
struct NfpStoreDataExtension {
void SetFromStoreData(const StoreData& store_data);
u8 faceline_color;
u8 hair_color;
u8 eye_color;
u8 eyebrow_color;
u8 mouth_color;
u8 beard_color;
u8 glass_color;
u8 glass_type;
};
static_assert(sizeof(NfpStoreDataExtension) == 0x8, "NfpStoreDataExtension is an invalid size");
#pragma pack(push, 4)
class Ver3StoreData {
public:
void BuildToStoreData(StoreData& out_store_data) const;
void BuildFromStoreData(const StoreData& store_data);
u32 IsValid() const;
u8 version;
union {
u8 raw;
BitField<0, 1, u8> allow_copying;
BitField<1, 1, u8> profanity_flag;
BitField<2, 2, u8> region_lock;
BitField<4, 2, u8> font_region;
} region_information;
u16_be mii_id;
u64_be system_id;
u32_be specialness_and_creation_date;
std::array<u8, 6> creator_mac;
u16_be padding;
union {
u16 raw;
BitField<0, 1, u16> gender;
BitField<1, 4, u16> birth_month;
BitField<5, 5, u16> birth_day;
BitField<10, 4, u16> favorite_color;
BitField<14, 1, u16> favorite;
} mii_information;
Nickname mii_name;
u8 height;
u8 build;
union {
u8 raw;
BitField<0, 1, u8> disable_sharing;
BitField<1, 4, u8> faceline_type;
BitField<5, 3, u8> faceline_color;
} appearance_bits1;
union {
u8 raw;
BitField<0, 4, u8> faceline_wrinkle;
BitField<4, 4, u8> faceline_make;
} appearance_bits2;
u8 hair_type;
union {
u8 raw;
BitField<0, 3, u8> hair_color;
BitField<3, 1, u8> hair_flip;
} appearance_bits3;
union {
u32 raw;
BitField<0, 6, u32> eye_type;
BitField<6, 3, u32> eye_color;
BitField<9, 4, u32> eye_scale;
BitField<13, 3, u32> eye_aspect;
BitField<16, 5, u32> eye_rotate;
BitField<21, 4, u32> eye_x;
BitField<25, 5, u32> eye_y;
} appearance_bits4;
union {
u32 raw;
BitField<0, 5, u32> eyebrow_type;
BitField<5, 3, u32> eyebrow_color;
BitField<8, 4, u32> eyebrow_scale;
BitField<12, 3, u32> eyebrow_aspect;
BitField<16, 4, u32> eyebrow_rotate;
BitField<21, 4, u32> eyebrow_x;
BitField<25, 5, u32> eyebrow_y;
} appearance_bits5;
union {
u16 raw;
BitField<0, 5, u16> nose_type;
BitField<5, 4, u16> nose_scale;
BitField<9, 5, u16> nose_y;
} appearance_bits6;
union {
u16 raw;
BitField<0, 6, u16> mouth_type;
BitField<6, 3, u16> mouth_color;
BitField<9, 4, u16> mouth_scale;
BitField<13, 3, u16> mouth_aspect;
} appearance_bits7;
union {
u8 raw;
BitField<0, 5, u8> mouth_y;
BitField<5, 3, u8> mustache_type;
} appearance_bits8;
u8 allow_copying;
union {
u16 raw;
BitField<0, 3, u16> beard_type;
BitField<3, 3, u16> beard_color;
BitField<6, 4, u16> mustache_scale;
BitField<10, 5, u16> mustache_y;
} appearance_bits9;
union {
u16 raw;
BitField<0, 4, u16> glass_type;
BitField<4, 3, u16> glass_color;
BitField<7, 4, u16> glass_scale;
BitField<11, 5, u16> glass_y;
} appearance_bits10;
union {
u16 raw;
BitField<0, 1, u16> mole_type;
BitField<1, 4, u16> mole_scale;
BitField<5, 5, u16> mole_x;
BitField<10, 5, u16> mole_y;
} appearance_bits11;
Nickname author_name;
INSERT_PADDING_BYTES(0x2);
u16_be crc;
};
static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size");
#pragma pack(pop)
}; // namespace Service::Mii

View file

@ -28,7 +28,6 @@
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/mii/types.h"
#include "core/hle/service/nfc/common/amiibo_crypto.h"
#include "core/hle/service/nfc/common/device.h"
#include "core/hle/service/nfc/mifare_result.h"
@ -681,12 +680,16 @@ Result NfcDevice::GetRegisterInfo(NFP::RegisterInfo& register_info) const {
return ResultRegistrationIsNotInitialized;
}
Service::Mii::MiiManager manager;
Mii::CharInfo char_info{};
Mii::StoreData store_data{};
tag_data.owner_mii.BuildToStoreData(store_data);
char_info.SetFromStoreData(store_data);
const auto& settings = tag_data.settings;
// TODO: Validate this data
register_info = {
.mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii),
.mii_char_info = char_info,
.creation_date = settings.init_date.GetWriteDate(),
.amiibo_name = GetAmiiboName(settings),
.font_region = settings.settings.font_region,
@ -825,8 +828,11 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe
return ResultWrongDeviceState;
}
Service::Mii::MiiManager manager;
const auto mii = manager.BuildBase(Mii::Gender::Male);
Service::Mii::StoreData store_data{};
Service::Mii::NfpStoreDataExtension extension{};
store_data.BuildBase(Mii::Gender::Male);
extension.SetFromStoreData(store_data);
auto& settings = tag_data.settings;
if (tag_data.settings.settings.amiibo_initialized == 0) {
@ -835,8 +841,8 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe
}
SetAmiiboName(settings, register_info.amiibo_name);
tag_data.owner_mii = manager.BuildFromStoreData(mii);
tag_data.mii_extension = manager.SetFromStoreData(mii);
tag_data.owner_mii.BuildFromStoreData(store_data);
tag_data.mii_extension = extension;
tag_data.unknown = 0;
tag_data.unknown2 = {};
settings.country_code_id = 0;
@ -1453,7 +1459,7 @@ void NfcDevice::UpdateRegisterInfoCrc() {
void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data,
const NFP::EncryptedNTAG215File& encrypted_file) const {
Service::Mii::MiiManager manager;
Service::Mii::StoreData store_data{};
auto& settings = stubbed_tag_data.settings;
stubbed_tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_file);
@ -1467,7 +1473,8 @@ void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data,
SetAmiiboName(settings, {'y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o'});
settings.settings.font_region.Assign(0);
settings.init_date = GetAmiiboDate(GetCurrentPosixTime());
stubbed_tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildBase(Mii::Gender::Male));
store_data.BuildBase(Mii::Gender::Male);
stubbed_tag_data.owner_mii.BuildFromStoreData(store_data);
// Admin info
settings.settings.amiibo_initialized.Assign(1);

View file

@ -6,7 +6,9 @@
#include <array>
#include "common/swap.h"
#include "core/hle/service/mii/types.h"
#include "core/hle/service/mii/types/char_info.h"
#include "core/hle/service/mii/types/store_data.h"
#include "core/hle/service/mii/types/ver3_store_data.h"
#include "core/hle/service/nfc/nfc_types.h"
namespace Service::NFP {
@ -322,7 +324,7 @@ static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
// This is nn::nfp::RegisterInfoPrivate
struct RegisterInfoPrivate {
Service::Mii::MiiStoreData mii_store_data;
Service::Mii::StoreData mii_store_data;
WriteDate creation_date;
AmiiboName amiibo_name;
u8 font_region;

View file

@ -160,7 +160,8 @@ void QtAmiiboSettingsDialog::LoadAmiiboData() {
}
const auto amiibo_name = std::string(register_info.amiibo_name.data());
const auto owner_name = Common::UTF16ToUTF8(register_info.mii_char_info.name.data());
const auto owner_name =
Common::UTF16ToUTF8(register_info.mii_char_info.GetNickname().data.data());
const auto creation_date =
QDate(register_info.creation_date.year, register_info.creation_date.month,
register_info.creation_date.day);