another try
This commit is contained in:
@@ -1,320 +1,320 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/mii/mii.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.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"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IDatabaseService::IsUpdated, "IsUpdated"},
|
||||
{1, &IDatabaseService::IsFullDatabase, "IsFullDatabase"},
|
||||
{2, &IDatabaseService::GetCount, "GetCount"},
|
||||
{3, &IDatabaseService::Get, "Get"},
|
||||
{4, &IDatabaseService::Get1, "Get1"},
|
||||
{5, &IDatabaseService::UpdateLatest, "UpdateLatest"},
|
||||
{6, &IDatabaseService::BuildRandom, "BuildRandom"},
|
||||
{7, &IDatabaseService::BuildDefault, "BuildDefault"},
|
||||
{8, nullptr, "Get2"},
|
||||
{9, nullptr, "Get3"},
|
||||
{10, nullptr, "UpdateLatest1"},
|
||||
{11, nullptr, "FindIndex"},
|
||||
{12, nullptr, "Move"},
|
||||
{13, nullptr, "AddOrReplace"},
|
||||
{14, nullptr, "Delete"},
|
||||
{15, nullptr, "DestroyFile"},
|
||||
{16, nullptr, "DeleteFile"},
|
||||
{17, nullptr, "Format"},
|
||||
{18, nullptr, "Import"},
|
||||
{19, nullptr, "Export"},
|
||||
{20, nullptr, "IsBrokenDatabaseWithClearFlag"},
|
||||
{21, &IDatabaseService::GetIndex, "GetIndex"},
|
||||
{22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"},
|
||||
{23, &IDatabaseService::Convert, "Convert"},
|
||||
{24, nullptr, "ConvertCoreDataToCharInfo"},
|
||||
{25, nullptr, "ConvertCharInfoToCoreData"},
|
||||
{26, nullptr, "Append"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
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(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(manager.CheckAndResetUpdateCounter(source_flag, current_update_counter));
|
||||
}
|
||||
|
||||
void IsFullDatabase(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(manager.IsFullDatabase());
|
||||
}
|
||||
|
||||
void GetCount(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(manager.GetCount(source_flag));
|
||||
}
|
||||
|
||||
void Get(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
const auto result{manager.GetDefault(source_flag)};
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
if (result->size() > 0) {
|
||||
ctx.WriteBuffer(SerializeArray(*result));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(static_cast<u32>(result->size()));
|
||||
}
|
||||
|
||||
void Get1(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
const auto result{manager.GetDefault(source_flag)};
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<CharInfo> values;
|
||||
for (const auto& element : *result) {
|
||||
values.emplace_back(element.info);
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(SerializeArray(values));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(static_cast<u32>(result->size()));
|
||||
}
|
||||
|
||||
void UpdateLatest(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto info{rp.PopRaw<CharInfo>()};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
const auto result{manager.UpdateLatest(info, source_flag)};
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<CharInfo>(*result);
|
||||
}
|
||||
|
||||
void BuildRandom(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto age{rp.PopRaw<Age>()};
|
||||
const auto gender{rp.PopRaw<Gender>()};
|
||||
const auto race{rp.PopRaw<Race>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with age={}, gender={}, race={}", age, gender, race);
|
||||
|
||||
if (age > Age::All) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "invalid age={}", age);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gender > Gender::All) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "invalid gender={}", gender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (race > Race::All) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "invalid race={}", race);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<CharInfo>(manager.BuildRandom(age, gender, race));
|
||||
}
|
||||
|
||||
void BuildDefault(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto index{rp.Pop<u32>()};
|
||||
|
||||
LOG_DEBUG(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);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<CharInfo>(manager.BuildDefault(index));
|
||||
}
|
||||
|
||||
void GetIndex(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto info{rp.PopRaw<CharInfo>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
u32 index{};
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(manager.GetIndex(info, index));
|
||||
rb.Push(index);
|
||||
}
|
||||
|
||||
void SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
current_interface_version = rp.PopRaw<u32>();
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called, interface_version={:08X}", current_interface_version);
|
||||
|
||||
UNIMPLEMENTED_IF(current_interface_version != 1);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Convert(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto mii_v3{rp.PopRaw<Ver3StoreData>()};
|
||||
|
||||
LOG_INFO(Service_Mii, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<CharInfo>(manager.ConvertV3ToCharInfo(mii_v3));
|
||||
}
|
||||
|
||||
constexpr bool IsInterfaceVersionSupported(u32 interface_version) const {
|
||||
return current_interface_version >= interface_version;
|
||||
}
|
||||
|
||||
MiiManager manager;
|
||||
|
||||
u32 current_interface_version{};
|
||||
u64 current_update_counter{};
|
||||
};
|
||||
|
||||
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
|
||||
public:
|
||||
explicit MiiDBModule(Core::System& system_, const char* name_)
|
||||
: ServiceFramework{system_, name_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetDatabaseService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IDatabaseService>(system);
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
}
|
||||
};
|
||||
|
||||
class MiiImg final : public ServiceFramework<MiiImg> {
|
||||
public:
|
||||
explicit MiiImg(Core::System& system_) : ServiceFramework{system_, "miiimg"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Initialize"},
|
||||
{10, nullptr, "Reload"},
|
||||
{11, nullptr, "GetCount"},
|
||||
{12, nullptr, "IsEmpty"},
|
||||
{13, nullptr, "IsFull"},
|
||||
{14, nullptr, "GetAttribute"},
|
||||
{15, nullptr, "LoadImage"},
|
||||
{16, nullptr, "AddOrUpdateImage"},
|
||||
{17, nullptr, "DeleteImages"},
|
||||
{100, nullptr, "DeleteFile"},
|
||||
{101, nullptr, "DestroyFile"},
|
||||
{102, nullptr, "ImportFile"},
|
||||
{103, nullptr, "ExportFile"},
|
||||
{104, nullptr, "ForceInitialize"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
|
||||
std::make_shared<MiiDBModule>(system, "mii:e")->InstallAsService(sm);
|
||||
std::make_shared<MiiDBModule>(system, "mii:u")->InstallAsService(sm);
|
||||
|
||||
std::make_shared<MiiImg>(system)->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::Mii
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/mii/mii.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.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"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IDatabaseService::IsUpdated, "IsUpdated"},
|
||||
{1, &IDatabaseService::IsFullDatabase, "IsFullDatabase"},
|
||||
{2, &IDatabaseService::GetCount, "GetCount"},
|
||||
{3, &IDatabaseService::Get, "Get"},
|
||||
{4, &IDatabaseService::Get1, "Get1"},
|
||||
{5, &IDatabaseService::UpdateLatest, "UpdateLatest"},
|
||||
{6, &IDatabaseService::BuildRandom, "BuildRandom"},
|
||||
{7, &IDatabaseService::BuildDefault, "BuildDefault"},
|
||||
{8, nullptr, "Get2"},
|
||||
{9, nullptr, "Get3"},
|
||||
{10, nullptr, "UpdateLatest1"},
|
||||
{11, nullptr, "FindIndex"},
|
||||
{12, nullptr, "Move"},
|
||||
{13, nullptr, "AddOrReplace"},
|
||||
{14, nullptr, "Delete"},
|
||||
{15, nullptr, "DestroyFile"},
|
||||
{16, nullptr, "DeleteFile"},
|
||||
{17, nullptr, "Format"},
|
||||
{18, nullptr, "Import"},
|
||||
{19, nullptr, "Export"},
|
||||
{20, nullptr, "IsBrokenDatabaseWithClearFlag"},
|
||||
{21, &IDatabaseService::GetIndex, "GetIndex"},
|
||||
{22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"},
|
||||
{23, &IDatabaseService::Convert, "Convert"},
|
||||
{24, nullptr, "ConvertCoreDataToCharInfo"},
|
||||
{25, nullptr, "ConvertCharInfoToCoreData"},
|
||||
{26, nullptr, "Append"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
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(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(manager.CheckAndResetUpdateCounter(source_flag, current_update_counter));
|
||||
}
|
||||
|
||||
void IsFullDatabase(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(manager.IsFullDatabase());
|
||||
}
|
||||
|
||||
void GetCount(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(manager.GetCount(source_flag));
|
||||
}
|
||||
|
||||
void Get(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
const auto result{manager.GetDefault(source_flag)};
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
if (result->size() > 0) {
|
||||
ctx.WriteBuffer(SerializeArray(*result));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(static_cast<u32>(result->size()));
|
||||
}
|
||||
|
||||
void Get1(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
const auto result{manager.GetDefault(source_flag)};
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<CharInfo> values;
|
||||
for (const auto& element : *result) {
|
||||
values.emplace_back(element.info);
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(SerializeArray(values));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(static_cast<u32>(result->size()));
|
||||
}
|
||||
|
||||
void UpdateLatest(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto info{rp.PopRaw<CharInfo>()};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
const auto result{manager.UpdateLatest(info, source_flag)};
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<CharInfo>(*result);
|
||||
}
|
||||
|
||||
void BuildRandom(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto age{rp.PopRaw<Age>()};
|
||||
const auto gender{rp.PopRaw<Gender>()};
|
||||
const auto race{rp.PopRaw<Race>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with age={}, gender={}, race={}", age, gender, race);
|
||||
|
||||
if (age > Age::All) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "invalid age={}", age);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gender > Gender::All) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "invalid gender={}", gender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (race > Race::All) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "invalid race={}", race);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<CharInfo>(manager.BuildRandom(age, gender, race));
|
||||
}
|
||||
|
||||
void BuildDefault(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto index{rp.Pop<u32>()};
|
||||
|
||||
LOG_DEBUG(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);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<CharInfo>(manager.BuildDefault(index));
|
||||
}
|
||||
|
||||
void GetIndex(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto info{rp.PopRaw<CharInfo>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
u32 index{};
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(manager.GetIndex(info, index));
|
||||
rb.Push(index);
|
||||
}
|
||||
|
||||
void SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
current_interface_version = rp.PopRaw<u32>();
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called, interface_version={:08X}", current_interface_version);
|
||||
|
||||
UNIMPLEMENTED_IF(current_interface_version != 1);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Convert(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto mii_v3{rp.PopRaw<Ver3StoreData>()};
|
||||
|
||||
LOG_INFO(Service_Mii, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<CharInfo>(manager.ConvertV3ToCharInfo(mii_v3));
|
||||
}
|
||||
|
||||
constexpr bool IsInterfaceVersionSupported(u32 interface_version) const {
|
||||
return current_interface_version >= interface_version;
|
||||
}
|
||||
|
||||
MiiManager manager;
|
||||
|
||||
u32 current_interface_version{};
|
||||
u64 current_update_counter{};
|
||||
};
|
||||
|
||||
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
|
||||
public:
|
||||
explicit MiiDBModule(Core::System& system_, const char* name_)
|
||||
: ServiceFramework{system_, name_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetDatabaseService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IDatabaseService>(system);
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
}
|
||||
};
|
||||
|
||||
class MiiImg final : public ServiceFramework<MiiImg> {
|
||||
public:
|
||||
explicit MiiImg(Core::System& system_) : ServiceFramework{system_, "miiimg"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Initialize"},
|
||||
{10, nullptr, "Reload"},
|
||||
{11, nullptr, "GetCount"},
|
||||
{12, nullptr, "IsEmpty"},
|
||||
{13, nullptr, "IsFull"},
|
||||
{14, nullptr, "GetAttribute"},
|
||||
{15, nullptr, "LoadImage"},
|
||||
{16, nullptr, "AddOrUpdateImage"},
|
||||
{17, nullptr, "DeleteImages"},
|
||||
{100, nullptr, "DeleteFile"},
|
||||
{101, nullptr, "DestroyFile"},
|
||||
{102, nullptr, "ImportFile"},
|
||||
{103, nullptr, "ExportFile"},
|
||||
{104, nullptr, "ForceInitialize"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
|
||||
std::make_shared<MiiDBModule>(system, "mii:e")->InstallAsService(sm);
|
||||
std::make_shared<MiiDBModule>(system, "mii:u")->InstallAsService(sm);
|
||||
|
||||
std::make_shared<MiiImg>(system)->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::Mii
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
|
||||
|
||||
} // namespace Service::Mii
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
|
||||
|
||||
} // namespace Service::Mii
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,36 +1,36 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/mii/types.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
|
||||
// with providing an easy interface for HLE emulation of the mii service.
|
||||
class MiiManager {
|
||||
public:
|
||||
MiiManager();
|
||||
|
||||
bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter);
|
||||
bool IsFullDatabase() const;
|
||||
u32 GetCount(SourceFlag source_flag) const;
|
||||
ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag);
|
||||
CharInfo BuildRandom(Age age, Gender gender, Race race);
|
||||
CharInfo BuildDefault(std::size_t index);
|
||||
CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const;
|
||||
Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const;
|
||||
bool ValidateV3Info(const Ver3StoreData& mii_v3) const;
|
||||
ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
|
||||
Result GetIndex(const CharInfo& info, u32& index);
|
||||
|
||||
private:
|
||||
const Common::UUID user_id{};
|
||||
u64 update_counter{};
|
||||
};
|
||||
|
||||
}; // namespace Service::Mii
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/mii/types.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
|
||||
// with providing an easy interface for HLE emulation of the mii service.
|
||||
class MiiManager {
|
||||
public:
|
||||
MiiManager();
|
||||
|
||||
bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter);
|
||||
bool IsFullDatabase() const;
|
||||
u32 GetCount(SourceFlag source_flag) const;
|
||||
ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag);
|
||||
CharInfo BuildRandom(Age age, Gender gender, Race race);
|
||||
CharInfo BuildDefault(std::size_t index);
|
||||
CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const;
|
||||
Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const;
|
||||
bool ValidateV3Info(const Ver3StoreData& mii_v3) const;
|
||||
ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
|
||||
Result GetIndex(const CharInfo& info, u32& index);
|
||||
|
||||
private:
|
||||
const Common::UUID user_id{};
|
||||
u64 update_counter{};
|
||||
};
|
||||
|
||||
}; // namespace Service::Mii
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,26 @@
|
||||
// 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, 8> 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
|
||||
// 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, 8> 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
|
||||
|
||||
@@ -1,495 +1,495 @@
|
||||
// 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(0x4);
|
||||
};
|
||||
static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size");
|
||||
|
||||
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
|
||||
// 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(0x4);
|
||||
};
|
||||
static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size");
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user