service: mii: Implement the rest of the service

This commit is contained in:
german77 2023-09-17 10:50:22 -06:00
parent 9a878de33f
commit 2fb71aecb0
3 changed files with 763 additions and 115 deletions

View file

@ -8,6 +8,9 @@
#include "core/hle/service/mii/mii.h" #include "core/hle/service/mii/mii.h"
#include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/mii/mii_result.h" #include "core/hle/service/mii/mii_result.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/server_manager.h" #include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
@ -27,29 +30,31 @@ public:
{5, &IDatabaseService::UpdateLatest, "UpdateLatest"}, {5, &IDatabaseService::UpdateLatest, "UpdateLatest"},
{6, &IDatabaseService::BuildRandom, "BuildRandom"}, {6, &IDatabaseService::BuildRandom, "BuildRandom"},
{7, &IDatabaseService::BuildDefault, "BuildDefault"}, {7, &IDatabaseService::BuildDefault, "BuildDefault"},
{8, nullptr, "Get2"}, {8, &IDatabaseService::Get2, "Get2"},
{9, nullptr, "Get3"}, {9, &IDatabaseService::Get3, "Get3"},
{10, nullptr, "UpdateLatest1"}, {10, &IDatabaseService::UpdateLatest1, "UpdateLatest1"},
{11, nullptr, "FindIndex"}, {11, &IDatabaseService::FindIndex, "FindIndex"},
{12, nullptr, "Move"}, {12, &IDatabaseService::Move, "Move"},
{13, nullptr, "AddOrReplace"}, {13, &IDatabaseService::AddOrReplace, "AddOrReplace"},
{14, nullptr, "Delete"}, {14, &IDatabaseService::Delete, "Delete"},
{15, nullptr, "DestroyFile"}, {15, &IDatabaseService::DestroyFile, "DestroyFile"},
{16, nullptr, "DeleteFile"}, {16, &IDatabaseService::DeleteFile, "DeleteFile"},
{17, nullptr, "Format"}, {17, &IDatabaseService::Format, "Format"},
{18, nullptr, "Import"}, {18, nullptr, "Import"},
{19, nullptr, "Export"}, {19, nullptr, "Export"},
{20, nullptr, "IsBrokenDatabaseWithClearFlag"}, {20, &IDatabaseService::IsBrokenDatabaseWithClearFlag, "IsBrokenDatabaseWithClearFlag"},
{21, &IDatabaseService::GetIndex, "GetIndex"}, {21, &IDatabaseService::GetIndex, "GetIndex"},
{22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"}, {22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"},
{23, &IDatabaseService::Convert, "Convert"}, {23, &IDatabaseService::Convert, "Convert"},
{24, nullptr, "ConvertCoreDataToCharInfo"}, {24, &IDatabaseService::ConvertCoreDataToCharInfo, "ConvertCoreDataToCharInfo"},
{25, nullptr, "ConvertCharInfoToCoreData"}, {25, &IDatabaseService::ConvertCharInfoToCoreData, "ConvertCharInfoToCoreData"},
{26, nullptr, "Append"}, {26, &IDatabaseService::Append, "Append"},
}; };
// clang-format on // clang-format on
RegisterHandlers(functions); RegisterHandlers(functions);
manager.Initialize(metadata);
} }
private: private:
@ -80,10 +85,10 @@ private:
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto source_flag{rp.PopRaw<SourceFlag>()}; const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
const u32 mii_count = manager.GetCount(metadata, source_flag); const u32 mii_count = manager.GetCount(metadata, source_flag);
LOG_DEBUG(Service_Mii, "called with source_flag={}, mii_count={}", source_flag, mii_count);
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push(mii_count); rb.Push(mii_count);
@ -94,16 +99,17 @@ private:
const auto source_flag{rp.PopRaw<SourceFlag>()}; const auto source_flag{rp.PopRaw<SourceFlag>()};
const auto output_size{ctx.GetWriteBufferNumElements<CharInfoElement>()}; const auto output_size{ctx.GetWriteBufferNumElements<CharInfoElement>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size);
u32 mii_count{}; u32 mii_count{};
std::vector<CharInfoElement> char_info_elements(output_size); std::vector<CharInfoElement> char_info_elements(output_size);
Result result = manager.Get(metadata, char_info_elements, mii_count, source_flag); const auto result = manager.Get(metadata, char_info_elements, mii_count, source_flag);
if (mii_count != 0) { if (mii_count != 0) {
ctx.WriteBuffer(char_info_elements); ctx.WriteBuffer(char_info_elements);
} }
LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag,
output_size, mii_count);
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result); rb.Push(result);
rb.Push(mii_count); rb.Push(mii_count);
@ -114,16 +120,17 @@ private:
const auto source_flag{rp.PopRaw<SourceFlag>()}; const auto source_flag{rp.PopRaw<SourceFlag>()};
const auto output_size{ctx.GetWriteBufferNumElements<CharInfo>()}; const auto output_size{ctx.GetWriteBufferNumElements<CharInfo>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size);
u32 mii_count{}; u32 mii_count{};
std::vector<CharInfo> char_info(output_size); std::vector<CharInfo> char_info(output_size);
Result result = manager.Get(metadata, char_info, mii_count, source_flag); const auto result = manager.Get(metadata, char_info, mii_count, source_flag);
if (mii_count != 0) { if (mii_count != 0) {
ctx.WriteBuffer(char_info); ctx.WriteBuffer(char_info);
} }
LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag,
output_size, mii_count);
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result); rb.Push(result);
rb.Push(mii_count); rb.Push(mii_count);
@ -134,7 +141,7 @@ private:
const auto char_info{rp.PopRaw<CharInfo>()}; const auto char_info{rp.PopRaw<CharInfo>()};
const auto source_flag{rp.PopRaw<SourceFlag>()}; const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); LOG_INFO(Service_Mii, "called with source_flag={}", source_flag);
CharInfo new_char_info{}; CharInfo new_char_info{};
const auto result = manager.UpdateLatest(metadata, new_char_info, char_info, source_flag); const auto result = manager.UpdateLatest(metadata, new_char_info, char_info, source_flag);
@ -146,7 +153,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushRaw<CharInfo>(new_char_info); rb.PushRaw(new_char_info);
} }
void BuildRandom(HLERequestContext& ctx) { void BuildRandom(HLERequestContext& ctx) {
@ -180,14 +187,14 @@ private:
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushRaw<CharInfo>(char_info); rb.PushRaw(char_info);
} }
void BuildDefault(HLERequestContext& ctx) { void BuildDefault(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto index{rp.Pop<u32>()}; const auto index{rp.Pop<u32>()};
LOG_INFO(Service_Mii, "called with index={}", index); LOG_DEBUG(Service_Mii, "called with index={}", index);
if (index > 5) { if (index > 5) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
@ -200,7 +207,240 @@ private:
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushRaw<CharInfo>(char_info); rb.PushRaw(char_info);
}
void Get2(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto source_flag{rp.PopRaw<SourceFlag>()};
const auto output_size{ctx.GetWriteBufferNumElements<StoreDataElement>()};
u32 mii_count{};
std::vector<StoreDataElement> store_data_elements(output_size);
const auto result = manager.Get(metadata, store_data_elements, mii_count, source_flag);
if (mii_count != 0) {
ctx.WriteBuffer(store_data_elements);
}
LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag,
output_size, mii_count);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.Push(mii_count);
}
void Get3(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto source_flag{rp.PopRaw<SourceFlag>()};
const auto output_size{ctx.GetWriteBufferNumElements<StoreData>()};
u32 mii_count{};
std::vector<StoreData> store_data(output_size);
const auto result = manager.Get(metadata, store_data, mii_count, source_flag);
if (mii_count != 0) {
ctx.WriteBuffer(store_data);
}
LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag,
output_size, mii_count);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.Push(mii_count);
}
void UpdateLatest1(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto store_data{rp.PopRaw<StoreData>()};
const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_INFO(Service_Mii, "called with source_flag={}", source_flag);
Result result = ResultSuccess;
if (!is_system) {
result = ResultPermissionDenied;
}
StoreData new_store_data{};
if (result.IsSuccess()) {
result = manager.UpdateLatest(metadata, new_store_data, store_data, source_flag);
}
if (result.IsFailure()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
IPC::ResponseBuilder rb{ctx, 2 + sizeof(StoreData) / sizeof(u32)};
rb.Push(ResultSuccess);
rb.PushRaw<StoreData>(new_store_data);
}
void FindIndex(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto create_id{rp.PopRaw<Common::UUID>()};
const auto is_special{rp.PopRaw<bool>()};
LOG_INFO(Service_Mii, "called with create_id={}, is_special={}",
create_id.FormattedString(), is_special);
s32 index = manager.FindIndex(create_id, is_special);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(index);
}
void Move(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto create_id{rp.PopRaw<Common::UUID>()};
const auto new_index{rp.PopRaw<s32>()};
LOG_INFO(Service_Mii, "called with create_id={}, new_index={}", create_id.FormattedString(),
new_index);
Result result = ResultSuccess;
if (!is_system) {
result = ResultPermissionDenied;
}
if (result.IsSuccess()) {
const u32 count = manager.GetCount(metadata, SourceFlag::Database);
if (new_index < 0 || new_index >= static_cast<s32>(count)) {
result = ResultInvalidArgument;
}
}
if (result.IsSuccess()) {
result = manager.Move(metadata, new_index, create_id);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void AddOrReplace(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto store_data{rp.PopRaw<StoreData>()};
LOG_INFO(Service_Mii, "called");
Result result = ResultSuccess;
if (!is_system) {
result = ResultPermissionDenied;
}
if (result.IsSuccess()) {
result = manager.AddOrReplace(metadata, store_data);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void Delete(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto create_id{rp.PopRaw<Common::UUID>()};
LOG_INFO(Service_Mii, "called, create_id={}", create_id.FormattedString());
Result result = ResultSuccess;
if (!is_system) {
result = ResultPermissionDenied;
}
if (result.IsSuccess()) {
result = manager.Delete(metadata, create_id);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void DestroyFile(HLERequestContext& ctx) {
// This calls nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled");
bool is_db_test_mode_enabled = false;
LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
Result result = ResultSuccess;
if (!is_db_test_mode_enabled) {
result = ResultTestModeOnly;
}
if (result.IsSuccess()) {
result = manager.DestroyFile(metadata);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void DeleteFile(HLERequestContext& ctx) {
// This calls nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled");
bool is_db_test_mode_enabled = false;
LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
Result result = ResultSuccess;
if (!is_db_test_mode_enabled) {
result = ResultTestModeOnly;
}
if (result.IsSuccess()) {
result = manager.DeleteFile();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void Format(HLERequestContext& ctx) {
// This calls nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled");
bool is_db_test_mode_enabled = false;
LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
Result result = ResultSuccess;
if (!is_db_test_mode_enabled) {
result = ResultTestModeOnly;
}
if (result.IsSuccess()) {
result = manager.Format(metadata);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IsBrokenDatabaseWithClearFlag(HLERequestContext& ctx) {
LOG_DEBUG(Service_Mii, "called");
bool is_broken_with_clear_flag{};
Result result = ResultSuccess;
if (!is_system) {
result = ResultPermissionDenied;
}
if (result.IsSuccess()) {
is_broken_with_clear_flag = manager.IsBrokenWithClearFlag(metadata);
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.Push<u8>(is_broken_with_clear_flag);
} }
void GetIndex(HLERequestContext& ctx) { void GetIndex(HLERequestContext& ctx) {
@ -236,13 +476,53 @@ private:
LOG_INFO(Service_Mii, "called"); LOG_INFO(Service_Mii, "called");
CharInfo char_info{}; CharInfo char_info{};
manager.ConvertV3ToCharInfo(char_info, mii_v3); const auto result = manager.ConvertV3ToCharInfo(char_info, mii_v3);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess); rb.Push(result);
rb.PushRaw<CharInfo>(char_info); rb.PushRaw<CharInfo>(char_info);
} }
void ConvertCoreDataToCharInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto core_data{rp.PopRaw<CoreData>()};
LOG_INFO(Service_Mii, "called");
CharInfo char_info{};
const auto result = manager.ConvertCoreDataToCharInfo(char_info, core_data);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(result);
rb.PushRaw<CharInfo>(char_info);
}
void ConvertCharInfoToCoreData(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto char_info{rp.PopRaw<CharInfo>()};
LOG_INFO(Service_Mii, "called");
CoreData core_data{};
const auto result = manager.ConvertCharInfoToCoreData(core_data, char_info);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CoreData) / sizeof(u32)};
rb.Push(result);
rb.PushRaw<CoreData>(core_data);
}
void Append(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto char_info{rp.PopRaw<CharInfo>()};
LOG_INFO(Service_Mii, "called");
const auto result = manager.Append(metadata, char_info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
MiiManager manager{}; MiiManager manager{};
DatabaseSessionMetadata metadata{}; DatabaseSessionMetadata metadata{};
bool is_system{}; bool is_system{};
@ -267,7 +547,7 @@ private:
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IDatabaseService>(system, is_system); rb.PushIpcInterface<IDatabaseService>(system, is_system);
LOG_DEBUG(Service_Mii, "called"); LOG_CRITICAL(Service_Mii, "called");
} }
bool is_system{}; bool is_system{};
@ -278,9 +558,9 @@ public:
explicit MiiImg(Core::System& system_) : ServiceFramework{system_, "miiimg"} { explicit MiiImg(Core::System& system_) : ServiceFramework{system_, "miiimg"} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, nullptr, "Initialize"}, {0, &MiiImg::Initialize, "Initialize"},
{10, nullptr, "Reload"}, {10, nullptr, "Reload"},
{11, nullptr, "GetCount"}, {11, &MiiImg::GetCount, "GetCount"},
{12, nullptr, "IsEmpty"}, {12, nullptr, "IsEmpty"},
{13, nullptr, "IsFull"}, {13, nullptr, "IsFull"},
{14, nullptr, "GetAttribute"}, {14, nullptr, "GetAttribute"},
@ -297,6 +577,22 @@ public:
RegisterHandlers(functions); RegisterHandlers(functions);
} }
private:
void Initialize(HLERequestContext& ctx) {
LOG_CRITICAL(Service_Mii, "called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void GetCount(HLERequestContext& ctx) {
LOG_CRITICAL(Service_Mii, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(0);
}
}; };
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {

View file

@ -1,59 +1,26 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include <random>
#include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/string_util.h" #include "core/hle/service/mii/mii_database_manager.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/mii/mii_result.h" #include "core/hle/service/mii/mii_result.h"
#include "core/hle/service/mii/mii_util.h" #include "core/hle/service/mii/mii_util.h"
#include "core/hle/service/mii/types/char_info.h"
#include "core/hle/service/mii/types/core_data.h" #include "core/hle/service/mii/types/core_data.h"
#include "core/hle/service/mii/types/raw_data.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 { namespace Service::Mii {
constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
MiiManager::MiiManager() {} MiiManager::MiiManager() {}
bool MiiManager::IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const { Result MiiManager::Initialize(DatabaseSessionMetadata& metadata) {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) { database_manager.MountSaveData();
return false; database_manager.Initialize(metadata, is_broken_with_clear_flag);
} return ResultSuccess;
const auto metadata_update_counter = metadata.update_counter;
metadata.update_counter = update_counter;
return metadata_update_counter != update_counter;
}
bool MiiManager::IsFullDatabase() const {
// TODO(bunnei): We don't implement the Mii database, so it cannot be full
return false;
}
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
}
return mii_count;
}
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;
}
// TODO(bunnei): We don't implement the Mii database, so we can't have an entry
return ResultNotFound;
} }
void MiiManager::BuildDefault(CharInfo& out_char_info, u32 index) const { void MiiManager::BuildDefault(CharInfo& out_char_info, u32 index) const {
@ -74,39 +41,368 @@ void MiiManager::BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Ra
out_char_info.SetFromStoreData(store_data); out_char_info.SetFromStoreData(store_data);
} }
void MiiManager::ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const { bool MiiManager::IsFullDatabase() const {
return database_manager.IsFullDatabase();
}
void MiiManager::SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) const {
metadata.interface_version = version;
}
bool MiiManager::IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return false;
}
const u64 metadata_update_counter = metadata.update_counter;
const u64 database_update_counter = database_manager.GetUpdateCounter();
metadata.update_counter = database_update_counter;
return metadata_update_counter != database_update_counter;
}
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) {
mii_count += database_manager.GetCount(metadata);
}
return mii_count;
}
Result MiiManager::Move(DatabaseSessionMetadata& metadata, u32 index,
const Common::UUID& create_id) {
const auto result = database_manager.Move(metadata, index, create_id);
if (result.IsFailure()) {
return result;
}
if (!database_manager.IsModified()) {
return ResultNotUpdated;
}
return database_manager.SaveDatabase();
}
Result MiiManager::AddOrReplace(DatabaseSessionMetadata& metadata, const StoreData& store_data) {
const auto result = database_manager.AddOrReplace(metadata, store_data);
if (result.IsFailure()) {
return result;
}
if (!database_manager.IsModified()) {
return ResultNotUpdated;
}
return database_manager.SaveDatabase();
}
Result MiiManager::Delete(DatabaseSessionMetadata& metadata, const Common::UUID& create_id) {
const auto result = database_manager.Delete(metadata, create_id);
if (result.IsFailure()) {
return result;
}
if (!database_manager.IsModified()) {
return ResultNotUpdated;
}
return database_manager.SaveDatabase();
}
s32 MiiManager::FindIndex(const Common::UUID& create_id, bool is_special) const {
s32 index{};
const auto result = database_manager.FindIndex(index, create_id, is_special);
if (result.IsError()) {
index = -1;
}
return index;
}
Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
s32& out_index) const {
if (char_info.Verify() != ValidationResult::NoErrors) {
return ResultInvalidCharInfo;
}
s32 index{};
Result result = {};
// FindIndex(index);
if (result.IsError()) {
return ResultNotFound;
}
if (index == -1) {
return ResultNotFound;
}
out_index = index;
return ResultSuccess;
}
Result MiiManager::Append(DatabaseSessionMetadata& metadata, const CharInfo& char_info) {
const auto result = database_manager.Append(metadata, char_info);
if (result.IsError()) {
return ResultNotFound;
}
if (!database_manager.IsModified()) {
return ResultNotUpdated;
}
return database_manager.SaveDatabase();
}
bool MiiManager::IsBrokenWithClearFlag(DatabaseSessionMetadata& metadata) {
const bool is_broken = is_broken_with_clear_flag;
if (is_broken_with_clear_flag) {
is_broken_with_clear_flag = false;
database_manager.Format(metadata);
database_manager.SaveDatabase();
}
return is_broken;
}
Result MiiManager::DestroyFile(DatabaseSessionMetadata& metadata) {
is_broken_with_clear_flag = true;
return database_manager.DestroyFile(metadata);
}
Result MiiManager::DeleteFile() {
return database_manager.DeleteFile();
}
Result MiiManager::Format(DatabaseSessionMetadata& metadata) {
database_manager.Format(metadata);
if (!database_manager.IsModified()) {
return ResultNotUpdated;
}
return database_manager.SaveDatabase();
}
Result MiiManager::ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const {
if (!mii_v3.IsValid()) {
return ResultInvalidCharInfo;
}
StoreData store_data{}; StoreData store_data{};
mii_v3.BuildToStoreData(store_data); mii_v3.BuildToStoreData(store_data);
const auto name = store_data.GetNickname();
if (!MiiUtil::IsFontRegionValid(store_data.GetFontRegion(), name.data)) {
store_data.SetInvalidName();
}
out_char_info.SetFromStoreData(store_data); out_char_info.SetFromStoreData(store_data);
return ResultSuccess;
}
Result MiiManager::ConvertCoreDataToCharInfo(CharInfo& out_char_info,
const CoreData& core_data) const {
if (core_data.IsValid() != ValidationResult::NoErrors) {
return ResultInvalidCharInfo;
}
StoreData store_data{};
store_data.BuildWithCoreData(core_data);
const auto name = store_data.GetNickname();
if (!MiiUtil::IsFontRegionValid(store_data.GetFontRegion(), name.data)) {
store_data.SetInvalidName();
}
out_char_info.SetFromStoreData(store_data);
return ResultSuccess;
}
Result MiiManager::ConvertCharInfoToCoreData(CoreData& out_core_data,
const CharInfo& char_info) const {
if (char_info.Verify() != ValidationResult::NoErrors) {
return ResultInvalidCharInfo;
}
out_core_data.BuildFromCharInfo(char_info);
const auto name = out_core_data.GetNickname();
if (!MiiUtil::IsFontRegionValid(out_core_data.GetFontRegion(), name.data)) {
out_core_data.SetNickname(out_core_data.GetInvalidNickname());
}
return ResultSuccess;
}
Result MiiManager::UpdateLatest(const DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
const CharInfo& char_info, SourceFlag source_flag) const {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return ResultNotFound;
}
if (metadata.IsInterfaceVersionSupported(1)) {
if (char_info.Verify() != ValidationResult::NoErrors) {
return ResultInvalidCharInfo;
}
}
u32 index{};
Result result = database_manager.FindIndex(metadata, index, char_info.GetCreateId());
if (result.IsError()) {
return result;
}
StoreData store_data{};
database_manager.Get(store_data, index, metadata);
if (store_data.GetType() != char_info.GetType()) {
return ResultNotFound;
}
out_char_info.SetFromStoreData(store_data);
if (char_info == out_char_info) {
return ResultNotUpdated;
}
return ResultSuccess;
}
Result MiiManager::UpdateLatest(const DatabaseSessionMetadata& metadata, StoreData& out_store_data,
const StoreData& store_data, SourceFlag source_flag) const {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return ResultNotFound;
}
if (metadata.IsInterfaceVersionSupported(1)) {
if (store_data.IsValid() != ValidationResult::NoErrors) {
return ResultInvalidCharInfo;
}
}
u32 index{};
Result result = database_manager.FindIndex(metadata, index, store_data.GetCreateId());
if (result.IsError()) {
return result;
}
database_manager.Get(out_store_data, index, metadata);
if (out_store_data.GetType() != store_data.GetType()) {
return ResultNotFound;
}
if (store_data == out_store_data) {
return ResultNotUpdated;
}
return ResultSuccess;
} }
Result MiiManager::Get(const DatabaseSessionMetadata& metadata, Result MiiManager::Get(const DatabaseSessionMetadata& metadata,
std::span<CharInfoElement> out_elements, u32& out_count, std::span<CharInfoElement> out_elements, u32& out_count,
SourceFlag source_flag) { SourceFlag source_flag) const {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) { if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return BuildDefault(out_elements, out_count, source_flag); return BuildDefault(out_elements, out_count, source_flag);
} }
// TODO(bunnei): We don't implement the Mii database, so we can't have an entry const auto mii_count = database_manager.GetCount(metadata);
for (std::size_t index = 0; index < mii_count; ++index) {
if (out_elements.size() <= static_cast<std::size_t>(out_count)) {
return ResultInvalidArgumentSize;
}
StoreData store_data{};
database_manager.Get(store_data, index, metadata);
out_elements[out_count].source = Source::Database;
out_elements[out_count].char_info.SetFromStoreData(store_data);
out_count++;
}
// Include default Mii at the end of the list // Include default Mii at the end of the list
return BuildDefault(out_elements, out_count, source_flag); return BuildDefault(out_elements, out_count, source_flag);
} }
Result MiiManager::Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info, Result MiiManager::Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info,
u32& out_count, SourceFlag source_flag) { u32& out_count, SourceFlag source_flag) const {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) { if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return BuildDefault(out_char_info, out_count, source_flag); 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 const auto mii_count = database_manager.GetCount(metadata);
for (std::size_t index = 0; index < mii_count; ++index) {
if (out_char_info.size() <= static_cast<std::size_t>(out_count)) {
return ResultInvalidArgumentSize;
}
StoreData store_data{};
database_manager.Get(store_data, index, metadata);
out_char_info[out_count].SetFromStoreData(store_data);
out_count++;
}
// Include default Mii at the end of the list // Include default Mii at the end of the list
return BuildDefault(out_char_info, out_count, source_flag); return BuildDefault(out_char_info, out_count, source_flag);
} }
Result MiiManager::Get(const DatabaseSessionMetadata& metadata,
std::span<StoreDataElement> out_elements, u32& out_count,
SourceFlag source_flag) const {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return BuildDefault(out_elements, out_count, source_flag);
}
const auto mii_count = database_manager.GetCount(metadata);
for (std::size_t index = 0; index < mii_count; ++index) {
if (out_elements.size() <= static_cast<std::size_t>(out_count)) {
return ResultInvalidArgumentSize;
}
StoreData store_data{};
database_manager.Get(store_data, index, metadata);
out_elements[out_count].store_data = store_data;
out_elements[out_count].source = Source::Database;
out_count++;
}
// 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<StoreData> out_store_data,
u32& out_count, SourceFlag source_flag) const {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return BuildDefault(out_store_data, out_count, source_flag);
}
const auto mii_count = database_manager.GetCount(metadata);
for (std::size_t index = 0; index < mii_count; ++index) {
if (out_store_data.size() <= static_cast<std::size_t>(out_count)) {
return ResultInvalidArgumentSize;
}
StoreData store_data{};
database_manager.Get(store_data, index, metadata);
out_store_data[out_count] = store_data;
out_count++;
}
// Include default Mii at the end of the list
return BuildDefault(out_store_data, out_count, source_flag);
}
Result MiiManager::BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count, Result MiiManager::BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count,
SourceFlag source_flag) { SourceFlag source_flag) const {
if ((source_flag & SourceFlag::Default) == SourceFlag::None) { if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
return ResultSuccess; return ResultSuccess;
} }
@ -129,7 +425,7 @@ Result MiiManager::BuildDefault(std::span<CharInfoElement> out_elements, u32& ou
} }
Result MiiManager::BuildDefault(std::span<CharInfo> out_char_info, u32& out_count, Result MiiManager::BuildDefault(std::span<CharInfo> out_char_info, u32& out_count,
SourceFlag source_flag) { SourceFlag source_flag) const {
if ((source_flag & SourceFlag::Default) == SourceFlag::None) { if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
return ResultSuccess; return ResultSuccess;
} }
@ -150,23 +446,41 @@ Result MiiManager::BuildDefault(std::span<CharInfo> out_char_info, u32& out_coun
return ResultSuccess; return ResultSuccess;
} }
Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info, Result MiiManager::BuildDefault(std::span<StoreDataElement> out_elements, u32& out_count,
s32& out_index) { SourceFlag source_flag) const {
if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
if (char_info.Verify() != ValidationResult::NoErrors) { return ResultSuccess;
return ResultInvalidCharInfo;
} }
constexpr u32 INVALID_INDEX{0xFFFFFFFF}; for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
if (out_elements.size() <= static_cast<std::size_t>(out_count)) {
out_index = INVALID_INDEX; return ResultInvalidArgumentSize;
// 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) { out_elements[out_count].store_data.BuildDefault(static_cast<u32>(index));
metadata.interface_version = version; out_elements[out_count].source = Source::Default;
out_count++;
}
return ResultSuccess;
}
Result MiiManager::BuildDefault(std::span<StoreData> out_char_info, u32& out_count,
SourceFlag source_flag) const {
if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
return ResultSuccess;
}
for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
if (out_char_info.size() <= static_cast<std::size_t>(out_count)) {
return ResultInvalidArgumentSize;
}
out_char_info[out_count].BuildDefault(static_cast<u32>(index));
out_count++;
}
return ResultSuccess;
} }
} // namespace Service::Mii } // namespace Service::Mii

View file

@ -3,47 +3,85 @@
#pragma once #pragma once
#include <vector> #include <span>
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/hle/service/mii/mii_database_manager.h"
#include "core/hle/service/mii/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 { namespace Service::Mii {
class CharInfo;
class CoreData;
class StoreData;
class Ver3StoreData;
// The Mii manager is responsible for loading and storing the Miis to the database in NAND along struct CharInfoElement;
// with providing an easy interface for HLE emulation of the mii service. struct StoreDataElement;
// The Mii manager is responsible for handling mii operations along with providing an easy interface
// for HLE emulation of the mii service.
class MiiManager { class MiiManager {
public: public:
MiiManager(); MiiManager();
Result Initialize(DatabaseSessionMetadata& metadata);
bool IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const; // Auto generated mii
bool IsFullDatabase() 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 BuildDefault(CharInfo& out_char_info, u32 index) const;
void BuildBase(CharInfo& out_char_info, Gender gender) const; void BuildBase(CharInfo& out_char_info, Gender gender) const;
void BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) 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); // Database operations
bool IsFullDatabase() const;
void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) const;
bool IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
u32 GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
Result Move(DatabaseSessionMetadata& metadata, u32 index, const Common::UUID& create_id);
Result AddOrReplace(DatabaseSessionMetadata& metadata, const StoreData& store_data);
Result Delete(DatabaseSessionMetadata& metadata, const Common::UUID& create_id);
s32 FindIndex(const Common::UUID& create_id, bool is_special) const;
Result GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info, Result GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
s32& out_index); s32& out_index) const;
void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version); Result Append(DatabaseSessionMetadata& metadata, const CharInfo& char_info);
// Test database operations
bool IsBrokenWithClearFlag(DatabaseSessionMetadata& metadata);
Result DestroyFile(DatabaseSessionMetadata& metadata);
Result DeleteFile();
Result Format(DatabaseSessionMetadata& metadata);
// Mii conversions
Result ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const;
Result ConvertCoreDataToCharInfo(CharInfo& out_char_info, const CoreData& core_data) const;
Result ConvertCharInfoToCoreData(CoreData& out_core_data, const CharInfo& char_info) const;
Result UpdateLatest(const DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
const CharInfo& char_info, SourceFlag source_flag) const;
Result UpdateLatest(const DatabaseSessionMetadata& metadata, StoreData& out_store_data,
const StoreData& store_data, SourceFlag source_flag) const;
// Overloaded getters
Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfoElement> out_elements,
u32& out_count, SourceFlag source_flag) const;
Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info,
u32& out_count, SourceFlag source_flag) const;
Result Get(const DatabaseSessionMetadata& metadata, std::span<StoreDataElement> out_elements,
u32& out_count, SourceFlag source_flag) const;
Result Get(const DatabaseSessionMetadata& metadata, std::span<StoreData> out_store_data,
u32& out_count, SourceFlag source_flag) const;
private: private:
Result BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count, Result BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count,
SourceFlag source_flag); SourceFlag source_flag) const;
Result BuildDefault(std::span<CharInfo> out_char_info, u32& out_count, SourceFlag source_flag); Result BuildDefault(std::span<CharInfo> out_char_info, u32& out_count,
SourceFlag source_flag) const;
Result BuildDefault(std::span<StoreDataElement> out_char_info, u32& out_count,
SourceFlag source_flag) const;
Result BuildDefault(std::span<StoreData> out_char_info, u32& out_count,
SourceFlag source_flag) const;
u64 update_counter{}; DatabaseManager database_manager{};
// This should be a global value
bool is_broken_with_clear_flag{};
}; };
}; // namespace Service::Mii }; // namespace Service::Mii