mii: Implement Delete and Destroy file
This commit is contained in:
parent
c40cff454d
commit
1aa2b99a98
4 changed files with 122 additions and 13 deletions
|
@ -19,15 +19,16 @@ struct UUID {
|
||||||
constexpr explicit UUID(const u128& id) : uuid{id} {}
|
constexpr explicit UUID(const u128& id) : uuid{id} {}
|
||||||
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
|
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
|
||||||
|
|
||||||
explicit operator bool() const {
|
constexpr explicit operator bool() const {
|
||||||
return uuid != INVALID_UUID;
|
return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const UUID& rhs) const {
|
constexpr bool operator==(const UUID& rhs) const {
|
||||||
return uuid == rhs.uuid;
|
// TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20
|
||||||
|
return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator!=(const UUID& rhs) const {
|
constexpr bool operator!=(const UUID& rhs) const {
|
||||||
return !operator==(rhs);
|
return !operator==(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <fmt/ostream.h>
|
||||||
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
|
@ -15,6 +17,10 @@
|
||||||
|
|
||||||
namespace Service::Mii {
|
namespace Service::Mii {
|
||||||
|
|
||||||
|
constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
|
||||||
|
constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
|
||||||
|
constexpr ResultCode ERROR_NOT_IN_TEST_MODE{ErrorModule::Mii, 99};
|
||||||
|
|
||||||
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
|
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
|
||||||
public:
|
public:
|
||||||
explicit IDatabaseService() : ServiceFramework{"IDatabaseService"} {
|
explicit IDatabaseService() : ServiceFramework{"IDatabaseService"} {
|
||||||
|
@ -35,8 +41,8 @@ public:
|
||||||
{12, &IDatabaseService::Move, "Move"},
|
{12, &IDatabaseService::Move, "Move"},
|
||||||
{13, &IDatabaseService::AddOrReplace, "AddOrReplace"},
|
{13, &IDatabaseService::AddOrReplace, "AddOrReplace"},
|
||||||
{14, &IDatabaseService::Delete, "Delete"},
|
{14, &IDatabaseService::Delete, "Delete"},
|
||||||
{15, nullptr, "DestroyFile"},
|
{15, &IDatabaseService::DestroyFile, "DestroyFile"},
|
||||||
{16, nullptr, "DeleteFile"},
|
{16, &IDatabaseService::DeleteFile, "DeleteFile"},
|
||||||
{17, &IDatabaseService::Format, "Format"},
|
{17, &IDatabaseService::Format, "Format"},
|
||||||
{18, nullptr, "Import"},
|
{18, nullptr, "Import"},
|
||||||
{19, nullptr, "Export"},
|
{19, nullptr, "Export"},
|
||||||
|
@ -135,12 +141,33 @@ private:
|
||||||
|
|
||||||
void BuildRandom(Kernel::HLERequestContext& ctx) {
|
void BuildRandom(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto random_params{rp.PopRaw<RandomParameters>()};
|
const auto [unknown1, unknown2, unknown3] = rp.PopRaw<RandomParameters>();
|
||||||
|
|
||||||
|
if (unknown1 > 3) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||||
|
LOG_ERROR(Service_Mii, "Invalid unknown1 value: {}", unknown1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unknown2 > 2) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||||
|
LOG_ERROR(Service_Mii, "Invalid unknown2 value: {}", unknown2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unknown3 > 3) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||||
|
LOG_ERROR(Service_Mii, "Invalid unknown3 value: {}", unknown3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
LOG_DEBUG(Service_Mii, "called with param_1={:08X}, param_2={:08X}, param_3={:08X}",
|
LOG_DEBUG(Service_Mii, "called with param_1={:08X}, param_2={:08X}, param_3={:08X}",
|
||||||
random_params.unknown_1, random_params.unknown_2, random_params.unknown_3);
|
unknown1, unknown2, unknown3);
|
||||||
|
|
||||||
const auto info = db.CreateRandom(random_params);
|
const auto info = db.CreateRandom({unknown1, unknown2, unknown3});
|
||||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
|
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushRaw<MiiInfo>(info);
|
rb.PushRaw<MiiInfo>(info);
|
||||||
|
@ -150,6 +177,14 @@ private:
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto index{rp.PopRaw<u32>()};
|
const auto index{rp.PopRaw<u32>()};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
LOG_DEBUG(Service_Mii, "called with index={:08X}", index);
|
LOG_DEBUG(Service_Mii, "called with index={:08X}", index);
|
||||||
|
|
||||||
const auto info = db.CreateDefault(index);
|
const auto info = db.CreateDefault(index);
|
||||||
|
@ -218,7 +253,14 @@ private:
|
||||||
void Move(Kernel::HLERequestContext& ctx) {
|
void Move(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto uuid{rp.PopRaw<Common::UUID>()};
|
const auto uuid{rp.PopRaw<Common::UUID>()};
|
||||||
const auto index{rp.PopRaw<u32>()};
|
const auto index{rp.PopRaw<s32>()};
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
LOG_ERROR(Service_Mii, "Index cannot be negative but is {:08X}!", index);
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
LOG_DEBUG(Service_Mii, "called with uuid={}, index={:08X}", uuid.FormatSwitch(), index);
|
LOG_DEBUG(Service_Mii, "called with uuid={}, index={:08X}", uuid.FormatSwitch(), index);
|
||||||
|
|
||||||
|
@ -252,8 +294,37 @@ private:
|
||||||
const auto success = db.Remove(uuid);
|
const auto success = db.Remove(uuid);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
// TODO(DarkLordZach): Find a better error code
|
rb.Push(success ? RESULT_SUCCESS : ERROR_CANNOT_FIND_ENTRY);
|
||||||
rb.Push(success ? RESULT_SUCCESS : ResultCode(-1));
|
}
|
||||||
|
|
||||||
|
void DestroyFile(Kernel::HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_Mii, "called");
|
||||||
|
|
||||||
|
if (!db.IsTestModeEnabled()) {
|
||||||
|
LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot destory database file.");
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ERROR_NOT_IN_TEST_MODE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.Push(db.DestroyFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteFile(Kernel::HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_Mii, "called");
|
||||||
|
|
||||||
|
if (!db.IsTestModeEnabled()) {
|
||||||
|
LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot delete database file.");
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ERROR_NOT_IN_TEST_MODE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.Push(db.DeleteFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Format(Kernel::HLERequestContext& ctx) {
|
void Format(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
|
@ -32,6 +32,13 @@ constexpr MiiStoreData DEFAULT_MII = {
|
||||||
// Default values taken from multiple real databases
|
// Default values taken from multiple real databases
|
||||||
const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0};
|
const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0};
|
||||||
|
|
||||||
|
constexpr std::array<const char*, 4> SOURCE_NAMES{
|
||||||
|
"Database",
|
||||||
|
"Default",
|
||||||
|
"Account",
|
||||||
|
"Friend",
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
|
template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
|
||||||
std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
|
std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
|
||||||
std::array<T, DestArraySize> out{};
|
std::array<T, DestArraySize> out{};
|
||||||
|
@ -167,6 +174,11 @@ MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os,Source source) {
|
||||||
|
os << SOURCE_NAMES.at(static_cast<std::size_t>(source));
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
std::u16string MiiInfo::Name() const {
|
std::u16string MiiInfo::Name() const {
|
||||||
return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
|
return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
|
||||||
}
|
}
|
||||||
|
@ -212,6 +224,10 @@ void MiiManager::ResetUpdatedFlag() {
|
||||||
updated_flag = false;
|
updated_flag = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MiiManager::IsTestModeEnabled() const {
|
||||||
|
return is_test_mode_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
bool MiiManager::Empty() const {
|
bool MiiManager::Empty() const {
|
||||||
return Size() == 0;
|
return Size() == 0;
|
||||||
}
|
}
|
||||||
|
@ -318,6 +334,17 @@ bool MiiManager::AddOrReplace(const MiiStoreData& data) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MiiManager::DestroyFile() {
|
||||||
|
database = DEFAULT_MII_DATABASE;
|
||||||
|
updated_flag = false;
|
||||||
|
return DeleteFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MiiManager::DeleteFile() {
|
||||||
|
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
|
||||||
|
return FileUtil::Exists(path) && FileUtil::Delete(path);
|
||||||
|
}
|
||||||
|
|
||||||
void MiiManager::WriteToFile() {
|
void MiiManager::WriteToFile() {
|
||||||
const auto raw_path =
|
const auto raw_path =
|
||||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030";
|
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030";
|
||||||
|
|
|
@ -27,6 +27,8 @@ enum class Source : u32 {
|
||||||
Friend = 3,
|
Friend = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, Source source);
|
||||||
|
|
||||||
struct MiiInfo {
|
struct MiiInfo {
|
||||||
Common::UUID uuid;
|
Common::UUID uuid;
|
||||||
std::array<char16_t, 11> name;
|
std::array<char16_t, 11> name;
|
||||||
|
@ -183,6 +185,8 @@ struct MiiStoreBitFields {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size.");
|
static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size.");
|
||||||
|
static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
|
||||||
|
"MiiStoreBitFields is not trivially copyable.");
|
||||||
|
|
||||||
struct MiiStoreData {
|
struct MiiStoreData {
|
||||||
// This corresponds to the above structure MiiStoreBitFields. I did it like this because the
|
// This corresponds to the above structure MiiStoreBitFields. I did it like this because the
|
||||||
|
@ -229,6 +233,8 @@ public:
|
||||||
bool CheckUpdatedFlag() const;
|
bool CheckUpdatedFlag() const;
|
||||||
void ResetUpdatedFlag();
|
void ResetUpdatedFlag();
|
||||||
|
|
||||||
|
bool IsTestModeEnabled() const;
|
||||||
|
|
||||||
bool Empty() const;
|
bool Empty() const;
|
||||||
bool Full() const;
|
bool Full() const;
|
||||||
|
|
||||||
|
@ -248,6 +254,9 @@ public:
|
||||||
bool Move(Common::UUID uuid, u32 new_index);
|
bool Move(Common::UUID uuid, u32 new_index);
|
||||||
bool AddOrReplace(const MiiStoreData& data);
|
bool AddOrReplace(const MiiStoreData& data);
|
||||||
|
|
||||||
|
bool DestroyFile();
|
||||||
|
bool DeleteFile();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void WriteToFile();
|
void WriteToFile();
|
||||||
void ReadFromFile();
|
void ReadFromFile();
|
||||||
|
@ -258,6 +267,7 @@ private:
|
||||||
|
|
||||||
MiiDatabase database;
|
MiiDatabase database;
|
||||||
bool updated_flag = false;
|
bool updated_flag = false;
|
||||||
|
bool is_test_mode_enabled = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
}; // namespace Service::Mii
|
}; // namespace Service::Mii
|
||||||
|
|
Loading…
Reference in a new issue