mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-27 22:06:27 -05:00
9a07ed53eb
Now that we have most of core free of shadowing, we can enable the warning as an error to catch anything that may be remaining and also eliminate this class of logic bug entirely.
310 lines
10 KiB
C++
310 lines
10 KiB
C++
// Copyright 2018 yuzu emulator team
|
|
// Licensed under GPLv2 or any later version
|
|
// Refer to the license.txt file included.
|
|
|
|
#include <memory>
|
|
|
|
#include "common/logging/log.h"
|
|
#include "core/hle/ipc_helpers.h"
|
|
#include "core/hle/kernel/hle_ipc.h"
|
|
#include "core/hle/service/mii/manager.h"
|
|
#include "core/hle/service/mii/mii.h"
|
|
#include "core/hle/service/service.h"
|
|
#include "core/hle/service/sm/sm.h"
|
|
|
|
namespace Service::Mii {
|
|
|
|
constexpr ResultCode 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, nullptr, "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(RESULT_SUCCESS);
|
|
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(RESULT_SUCCESS);
|
|
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(RESULT_SUCCESS);
|
|
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(RESULT_SUCCESS);
|
|
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<MiiInfo> values;
|
|
for (const auto& element : *result) {
|
|
values.emplace_back(element.info);
|
|
}
|
|
|
|
ctx.WriteBuffer(SerializeArray(values));
|
|
|
|
IPC::ResponseBuilder rb{ctx, 3};
|
|
rb.Push(RESULT_SUCCESS);
|
|
rb.Push<u32>(static_cast<u32>(result->size()));
|
|
}
|
|
|
|
void UpdateLatest(Kernel::HLERequestContext& ctx) {
|
|
IPC::RequestParser rp{ctx};
|
|
const auto info{rp.PopRaw<MiiInfo>()};
|
|
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(MiiInfo) / sizeof(u32)};
|
|
rb.Push(RESULT_SUCCESS);
|
|
rb.PushRaw<MiiInfo>(*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(MiiInfo) / sizeof(u32)};
|
|
rb.Push(RESULT_SUCCESS);
|
|
rb.PushRaw<MiiInfo>(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(MiiInfo) / sizeof(u32)};
|
|
rb.Push(RESULT_SUCCESS);
|
|
rb.PushRaw<MiiInfo>(manager.BuildDefault(index));
|
|
}
|
|
|
|
void GetIndex(Kernel::HLERequestContext& ctx) {
|
|
IPC::RequestParser rp{ctx};
|
|
const auto info{rp.PopRaw<MiiInfo>()};
|
|
|
|
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(RESULT_SUCCESS);
|
|
}
|
|
|
|
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(RESULT_SUCCESS);
|
|
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
|