mii: Prepare Interface for new implementation

This commit is contained in:
german77 2023-09-11 00:58:46 -06:00
parent 571399930c
commit bd169f417f
6 changed files with 210 additions and 138 deletions

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

@ -15,8 +15,8 @@ namespace Service::Mii {
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"},
@ -53,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) {
@ -89,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.char_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;
@ -152,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,46 +160,47 @@ private:
if (age > Age::All) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultInvalidArgument);
LOG_ERROR(Service_Mii, "invalid age={}", age);
return;
}
if (gender > Gender::All) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultInvalidArgument);
LOG_ERROR(Service_Mii, "invalid gender={}", gender);
return;
}
if (race > Race::All) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultInvalidArgument);
LOG_ERROR(Service_Mii, "invalid race={}", race);
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(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) {
@ -210,19 +209,21 @@ private:
LOG_DEBUG(Service_Mii, "called");
u32 index{};
s32 index{};
const Result 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);
@ -230,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"},
@ -267,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> {
@ -302,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

@ -16,45 +16,18 @@
#include "core/hle/service/mii/types/raw_data.h"
namespace Service::Mii {
namespace {
constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
CharInfo ConvertStoreDataToInfo(const StoreData& data) {
CharInfo char_info{};
char_info.SetFromStoreData(data);
return char_info;
}
MiiManager::MiiManager() {}
StoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) {
StoreData store_data{};
store_data.BuildRandom(age, gender, race);
return store_data;
}
StoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) {
StoreData store_data{};
store_data.BuildDefault(0);
return store_data;
}
} // namespace
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 {
@ -62,19 +35,19 @@ bool MiiManager::IsFullDatabase() const {
return false;
}
u32 MiiManager::GetCount(SourceFlag source_flag) const {
std::size_t count{};
if ((source_flag & SourceFlag::Database) != SourceFlag::None) {
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 ResultNotFound;
}
@ -83,48 +56,117 @@ Result MiiManager::UpdateLatest(CharInfo* out_info, const CharInfo& info, Source
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 {
CharInfo char_info{};
void MiiManager::ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const {
StoreData store_data{};
mii_v3.BuildToStoreData(store_data);
char_info.SetFromStoreData(store_data);
return char_info;
out_char_info.SetFromStoreData(store_data);
}
std::vector<CharInfoElement> MiiManager::GetDefault(SourceFlag source_flag) {
std::vector<CharInfoElement> result;
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(bunnei): We don't implement the Mii database, so we can't have an entry
// Include default Mii at the end of the list
return BuildDefault(out_elements, out_count, source_flag);
}
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);
}
// TODO(bunnei): We don't implement the Mii database, so we can't have an entry
// Include default Mii at the end of the list
return BuildDefault(out_char_info, out_count, source_flag);
}
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;
}
return result;
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 ResultSuccess;
}
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() != 0) {
return ResultInvalidCharInfo;
}
Result MiiManager::GetIndex([[maybe_unused]] const CharInfo& info, u32& index) {
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 ResultNotFound;
}
void MiiManager::SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) {
metadata.interface_version = version;
}
} // namespace Service::Mii

View file

@ -19,16 +19,24 @@ 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;
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 CharInfo& info, u32& index);
Result GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
s32& out_index);
void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version);
struct MiiDatabase {
u32 magic{}; // 'NFDB'
@ -40,7 +48,10 @@ public:
static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
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

@ -165,4 +165,14 @@ struct DefaultMii {
};
static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData 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

@ -680,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,