mirror of https://git.suyu.dev/suyu/suyu
Merge pull request #11526 from german77/mii_service_v2
service: mii: Update implementation Part2 - Mii database supportmerge-requests/60/head
commit
49cb89e324
@ -0,0 +1,142 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "core/hle/service/mii/mii_database.h"
|
||||||
|
#include "core/hle/service/mii/mii_result.h"
|
||||||
|
#include "core/hle/service/mii/mii_util.h"
|
||||||
|
|
||||||
|
namespace Service::Mii {
|
||||||
|
|
||||||
|
u8 NintendoFigurineDatabase::GetDatabaseLength() const {
|
||||||
|
return database_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NintendoFigurineDatabase::IsFull() const {
|
||||||
|
return database_length >= MaxDatabaseLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
StoreData NintendoFigurineDatabase::Get(std::size_t index) const {
|
||||||
|
StoreData store_data = miis.at(index);
|
||||||
|
|
||||||
|
// This hack is to make external database dumps compatible
|
||||||
|
store_data.SetDeviceChecksum();
|
||||||
|
|
||||||
|
return store_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 NintendoFigurineDatabase::GetCount(const DatabaseSessionMetadata& metadata) const {
|
||||||
|
if (magic == MiiMagic) {
|
||||||
|
return GetDatabaseLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 mii_count{};
|
||||||
|
for (std::size_t index = 0; index < mii_count; ++index) {
|
||||||
|
const auto& store_data = Get(index);
|
||||||
|
if (!store_data.IsSpecial()) {
|
||||||
|
mii_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mii_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NintendoFigurineDatabase::GetIndexByCreatorId(u32& out_index,
|
||||||
|
const Common::UUID& create_id) const {
|
||||||
|
for (std::size_t index = 0; index < database_length; ++index) {
|
||||||
|
if (miis[index].GetCreateId() == create_id) {
|
||||||
|
out_index = static_cast<u32>(index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NintendoFigurineDatabase::Move(u32 current_index, u32 new_index) {
|
||||||
|
if (current_index == new_index) {
|
||||||
|
return ResultNotUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StoreData store_data = miis[current_index];
|
||||||
|
|
||||||
|
if (new_index > current_index) {
|
||||||
|
// Shift left
|
||||||
|
const u32 index_diff = new_index - current_index;
|
||||||
|
for (std::size_t i = 0; i < index_diff; i++) {
|
||||||
|
miis[current_index + i] = miis[current_index + i + 1];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Shift right
|
||||||
|
const u32 index_diff = current_index - new_index;
|
||||||
|
for (std::size_t i = 0; i < index_diff; i++) {
|
||||||
|
miis[current_index - i] = miis[current_index - i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
miis[new_index] = store_data;
|
||||||
|
crc = GenerateDatabaseCrc();
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NintendoFigurineDatabase::Replace(u32 index, const StoreData& store_data) {
|
||||||
|
miis[index] = store_data;
|
||||||
|
crc = GenerateDatabaseCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NintendoFigurineDatabase::Add(const StoreData& store_data) {
|
||||||
|
miis[database_length] = store_data;
|
||||||
|
database_length++;
|
||||||
|
crc = GenerateDatabaseCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NintendoFigurineDatabase::Delete(u32 index) {
|
||||||
|
// Shift left
|
||||||
|
const s32 new_database_size = database_length - 1;
|
||||||
|
if (static_cast<s32>(index) < new_database_size) {
|
||||||
|
for (std::size_t i = index; i < static_cast<std::size_t>(new_database_size); i++) {
|
||||||
|
miis[i] = miis[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
database_length = static_cast<u8>(new_database_size);
|
||||||
|
crc = GenerateDatabaseCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NintendoFigurineDatabase::CleanDatabase() {
|
||||||
|
miis = {};
|
||||||
|
version = 1;
|
||||||
|
magic = DatabaseMagic;
|
||||||
|
database_length = 0;
|
||||||
|
crc = GenerateDatabaseCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NintendoFigurineDatabase::CorruptCrc() {
|
||||||
|
crc = GenerateDatabaseCrc();
|
||||||
|
crc = ~crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NintendoFigurineDatabase::CheckIntegrity() {
|
||||||
|
if (magic != DatabaseMagic) {
|
||||||
|
return ResultInvalidDatabaseSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version != 1) {
|
||||||
|
return ResultInvalidDatabaseVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crc != GenerateDatabaseCrc()) {
|
||||||
|
return ResultInvalidDatabaseChecksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (database_length >= MaxDatabaseLength) {
|
||||||
|
return ResultInvalidDatabaseLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 NintendoFigurineDatabase::GenerateDatabaseCrc() {
|
||||||
|
return MiiUtil::CalculateCrc16(&magic, sizeof(NintendoFigurineDatabase) - sizeof(crc));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::Mii
|
@ -0,0 +1,66 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
#include "core/hle/service/mii/types/store_data.h"
|
||||||
|
|
||||||
|
namespace Service::Mii {
|
||||||
|
|
||||||
|
constexpr std::size_t MaxDatabaseLength{100};
|
||||||
|
constexpr u32 MiiMagic{0xa523b78f};
|
||||||
|
constexpr u32 DatabaseMagic{0x4244464e}; // NFDB
|
||||||
|
|
||||||
|
class NintendoFigurineDatabase {
|
||||||
|
public:
|
||||||
|
/// Returns the total mii count.
|
||||||
|
u8 GetDatabaseLength() const;
|
||||||
|
|
||||||
|
/// Returns true if database is full.
|
||||||
|
bool IsFull() const;
|
||||||
|
|
||||||
|
/// Returns the mii of the specified index.
|
||||||
|
StoreData Get(std::size_t index) const;
|
||||||
|
|
||||||
|
/// Returns the total mii count. Ignoring special mii.
|
||||||
|
u32 GetCount(const DatabaseSessionMetadata& metadata) const;
|
||||||
|
|
||||||
|
/// Returns the index of a mii. If the mii isn't found returns false.
|
||||||
|
bool GetIndexByCreatorId(u32& out_index, const Common::UUID& create_id) const;
|
||||||
|
|
||||||
|
/// Moves the location of a specific mii.
|
||||||
|
Result Move(u32 current_index, u32 new_index);
|
||||||
|
|
||||||
|
/// Replaces mii with new data.
|
||||||
|
void Replace(u32 index, const StoreData& store_data);
|
||||||
|
|
||||||
|
/// Adds a new mii to the end of the database.
|
||||||
|
void Add(const StoreData& store_data);
|
||||||
|
|
||||||
|
/// Removes mii from database and shifts left the remainding data.
|
||||||
|
void Delete(u32 index);
|
||||||
|
|
||||||
|
/// Deletes all contents with a fresh database
|
||||||
|
void CleanDatabase();
|
||||||
|
|
||||||
|
/// Intentionally sets a bad checksum
|
||||||
|
void CorruptCrc();
|
||||||
|
|
||||||
|
/// Returns success if database is valid otherwise returns the corresponding error code.
|
||||||
|
Result CheckIntegrity();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Returns the checksum of the database
|
||||||
|
u16 GenerateDatabaseCrc();
|
||||||
|
|
||||||
|
u32 magic{}; // 'NFDB'
|
||||||
|
std::array<StoreData, MaxDatabaseLength> miis{};
|
||||||
|
u8 version{};
|
||||||
|
u8 database_length{};
|
||||||
|
u16 crc{};
|
||||||
|
};
|
||||||
|
static_assert(sizeof(NintendoFigurineDatabase) == 0x1A98,
|
||||||
|
"NintendoFigurineDatabase has incorrect size.");
|
||||||
|
|
||||||
|
}; // namespace Service::Mii
|
@ -0,0 +1,420 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/fs/file.h"
|
||||||
|
#include "common/fs/fs.h"
|
||||||
|
#include "common/fs/path_util.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
|
||||||
|
#include "core/hle/service/mii/mii_database_manager.h"
|
||||||
|
#include "core/hle/service/mii/mii_result.h"
|
||||||
|
#include "core/hle/service/mii/mii_util.h"
|
||||||
|
#include "core/hle/service/mii/types/char_info.h"
|
||||||
|
#include "core/hle/service/mii/types/store_data.h"
|
||||||
|
|
||||||
|
namespace Service::Mii {
|
||||||
|
const char* DbFileName = "MiiDatabase.dat";
|
||||||
|
|
||||||
|
DatabaseManager::DatabaseManager() {}
|
||||||
|
|
||||||
|
Result DatabaseManager::MountSaveData() {
|
||||||
|
if (!is_save_data_mounted) {
|
||||||
|
system_save_dir =
|
||||||
|
Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000030";
|
||||||
|
if (is_test_db) {
|
||||||
|
system_save_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) /
|
||||||
|
"system/save/8000000000000031";
|
||||||
|
}
|
||||||
|
|
||||||
|
// mount point should be "mii:"
|
||||||
|
|
||||||
|
if (!Common::FS::CreateDirs(system_save_dir)) {
|
||||||
|
return ResultUnknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is_save_data_mounted = true;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DatabaseManager::Initialize(DatabaseSessionMetadata& metadata, bool& is_database_broken) {
|
||||||
|
is_database_broken = false;
|
||||||
|
if (!is_save_data_mounted) {
|
||||||
|
return ResultInvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
database.CleanDatabase();
|
||||||
|
update_counter++;
|
||||||
|
metadata.update_counter = update_counter;
|
||||||
|
|
||||||
|
const Common::FS::IOFile db_file{system_save_dir / DbFileName, Common::FS::FileAccessMode::Read,
|
||||||
|
Common::FS::FileType::BinaryFile};
|
||||||
|
|
||||||
|
if (!db_file.IsOpen()) {
|
||||||
|
return SaveDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Common::FS::GetSize(system_save_dir / DbFileName) != sizeof(NintendoFigurineDatabase)) {
|
||||||
|
is_database_broken = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db_file.Read(database) != 1) {
|
||||||
|
is_database_broken = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_database_broken) {
|
||||||
|
// Dragons happen here for simplicity just clean the database
|
||||||
|
LOG_ERROR(Service_Mii, "Mii database is corrupted");
|
||||||
|
database.CleanDatabase();
|
||||||
|
return ResultUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = database.CheckIntegrity();
|
||||||
|
|
||||||
|
if (result.IsError()) {
|
||||||
|
LOG_ERROR(Service_Mii, "Mii database is corrupted 0x{:0x}", result.raw);
|
||||||
|
database.CleanDatabase();
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO(Service_Mii, "Successfully loaded mii database. size={}",
|
||||||
|
database.GetDatabaseLength());
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseManager::IsFullDatabase() const {
|
||||||
|
return database.GetDatabaseLength() == MaxDatabaseLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseManager::IsModified() const {
|
||||||
|
return is_moddified;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 DatabaseManager::GetUpdateCounter() const {
|
||||||
|
return update_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 DatabaseManager::GetCount(const DatabaseSessionMetadata& metadata) const {
|
||||||
|
const u32 database_size = database.GetDatabaseLength();
|
||||||
|
if (metadata.magic == MiiMagic) {
|
||||||
|
return database_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special mii can't be used. Skip those.
|
||||||
|
|
||||||
|
u32 mii_count{};
|
||||||
|
for (std::size_t index = 0; index < database_size; ++index) {
|
||||||
|
const auto& store_data = database.Get(index);
|
||||||
|
if (store_data.IsSpecial()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
mii_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mii_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseManager::Get(StoreData& out_store_data, std::size_t index,
|
||||||
|
const DatabaseSessionMetadata& metadata) const {
|
||||||
|
if (metadata.magic == MiiMagic) {
|
||||||
|
out_store_data = database.Get(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The index refeers to the mii index without special mii.
|
||||||
|
// Search on the database until we find it
|
||||||
|
|
||||||
|
u32 virtual_index = 0;
|
||||||
|
const u32 database_size = database.GetDatabaseLength();
|
||||||
|
for (std::size_t i = 0; i < database_size; ++i) {
|
||||||
|
const auto& store_data = database.Get(i);
|
||||||
|
if (store_data.IsSpecial()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (virtual_index == index) {
|
||||||
|
out_store_data = store_data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
virtual_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function doesn't fail. It returns the first mii instead
|
||||||
|
out_store_data = database.Get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DatabaseManager::FindIndex(s32& out_index, const Common::UUID& create_id,
|
||||||
|
bool is_special) const {
|
||||||
|
u32 index{};
|
||||||
|
const bool is_found = database.GetIndexByCreatorId(index, create_id);
|
||||||
|
|
||||||
|
if (!is_found) {
|
||||||
|
return ResultNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_special) {
|
||||||
|
out_index = index;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (database.Get(index).IsSpecial()) {
|
||||||
|
return ResultNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_index = 0;
|
||||||
|
|
||||||
|
if (index < 1) {
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i <= index; ++i) {
|
||||||
|
if (database.Get(i).IsSpecial()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out_index++;
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DatabaseManager::FindIndex(const DatabaseSessionMetadata& metadata, u32& out_index,
|
||||||
|
const Common::UUID& create_id) const {
|
||||||
|
u32 index{};
|
||||||
|
const bool is_found = database.GetIndexByCreatorId(index, create_id);
|
||||||
|
|
||||||
|
if (!is_found) {
|
||||||
|
return ResultNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.magic == MiiMagic) {
|
||||||
|
out_index = index;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (database.Get(index).IsSpecial()) {
|
||||||
|
return ResultNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_index = 0;
|
||||||
|
|
||||||
|
if (index < 1) {
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The index refeers to the mii index without special mii.
|
||||||
|
// Search on the database until we find it
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i <= index; ++i) {
|
||||||
|
const auto& store_data = database.Get(i);
|
||||||
|
if (store_data.IsSpecial()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out_index++;
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DatabaseManager::FindMoveIndex(u32& out_index, u32 new_index,
|
||||||
|
const Common::UUID& create_id) const {
|
||||||
|
const auto database_size = database.GetDatabaseLength();
|
||||||
|
|
||||||
|
if (database_size >= 1) {
|
||||||
|
u32 virtual_index{};
|
||||||
|
for (std::size_t i = 0; i < database_size; ++i) {
|
||||||
|
const StoreData& store_data = database.Get(i);
|
||||||
|
if (store_data.IsSpecial()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (virtual_index == new_index) {
|
||||||
|
const bool is_found = database.GetIndexByCreatorId(out_index, create_id);
|
||||||
|
if (!is_found) {
|
||||||
|
return ResultNotFound;
|
||||||
|
}
|
||||||
|
if (store_data.IsSpecial()) {
|
||||||
|
return ResultInvalidOperation;
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
virtual_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool is_found = database.GetIndexByCreatorId(out_index, create_id);
|
||||||
|
if (!is_found) {
|
||||||
|
return ResultNotFound;
|
||||||
|
}
|
||||||
|
const StoreData& store_data = database.Get(out_index);
|
||||||
|
if (store_data.IsSpecial()) {
|
||||||
|
return ResultInvalidOperation;
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DatabaseManager::Move(DatabaseSessionMetadata& metadata, u32 new_index,
|
||||||
|
const Common::UUID& create_id) {
|
||||||
|
u32 current_index{};
|
||||||
|
if (metadata.magic == MiiMagic) {
|
||||||
|
const bool is_found = database.GetIndexByCreatorId(current_index, create_id);
|
||||||
|
if (!is_found) {
|
||||||
|
return ResultNotFound;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const auto result = FindMoveIndex(current_index, new_index, create_id);
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = database.Move(current_index, new_index);
|
||||||
|
if (result.IsFailure()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_moddified = true;
|
||||||
|
update_counter++;
|
||||||
|
metadata.update_counter = update_counter;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DatabaseManager::AddOrReplace(DatabaseSessionMetadata& metadata,
|
||||||
|
const StoreData& store_data) {
|
||||||
|
if (store_data.IsValid() != ValidationResult::NoErrors) {
|
||||||
|
return ResultInvalidStoreData;
|
||||||
|
}
|
||||||
|
if (metadata.magic != MiiMagic && store_data.IsSpecial()) {
|
||||||
|
return ResultInvalidOperation;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 index{};
|
||||||
|
const bool is_found = database.GetIndexByCreatorId(index, store_data.GetCreateId());
|
||||||
|
if (is_found) {
|
||||||
|
const StoreData& old_store_data = database.Get(index);
|
||||||
|
|
||||||
|
if (store_data.IsSpecial() != old_store_data.IsSpecial()) {
|
||||||
|
return ResultInvalidOperation;
|
||||||
|
}
|
||||||
|
|
||||||
|
database.Replace(index, store_data);
|
||||||
|
} else {
|
||||||
|
if (database.IsFull()) {
|
||||||
|
return ResultDatabaseFull;
|
||||||
|
}
|
||||||
|
|
||||||
|
database.Add(store_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
is_moddified = true;
|
||||||
|
update_counter++;
|
||||||
|
metadata.update_counter = update_counter;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DatabaseManager::Delete(DatabaseSessionMetadata& metadata, const Common::UUID& create_id) {
|
||||||
|
u32 index{};
|
||||||
|
const bool is_found = database.GetIndexByCreatorId(index, create_id);
|
||||||
|
if (!is_found) {
|
||||||
|
return ResultNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.magic != MiiMagic) {
|
||||||
|
const auto& store_data = database.Get(index);
|
||||||
|
if (store_data.IsSpecial()) {
|
||||||
|
return ResultInvalidOperation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
database.Delete(index);
|
||||||
|
|
||||||
|
is_moddified = true;
|
||||||
|
update_counter++;
|
||||||
|
metadata.update_counter = update_counter;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DatabaseManager::Append(DatabaseSessionMetadata& metadata, const CharInfo& char_info) {
|
||||||
|
if (char_info.Verify() != ValidationResult::NoErrors) {
|
||||||
|
return ResultInvalidCharInfo2;
|
||||||
|
}
|
||||||
|
if (char_info.GetType() == 1) {
|
||||||
|
return ResultInvalidCharInfoType;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 index{};
|
||||||
|
StoreData store_data{};
|
||||||
|
|
||||||
|
// Loop until the mii we created is not on the database
|
||||||
|
do {
|
||||||
|
store_data.BuildWithCharInfo(char_info);
|
||||||
|
} while (database.GetIndexByCreatorId(index, store_data.GetCreateId()));
|
||||||
|
|
||||||
|
const Result result = store_data.Restore();
|
||||||
|
|
||||||
|
if (result.IsSuccess() || result == ResultNotUpdated) {
|
||||||
|
return AddOrReplace(metadata, store_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DatabaseManager::DestroyFile(DatabaseSessionMetadata& metadata) {
|
||||||
|
database.CorruptCrc();
|
||||||
|
|
||||||
|
is_moddified = true;
|
||||||
|
update_counter++;
|
||||||
|
metadata.update_counter = update_counter;
|
||||||
|
|
||||||
|
const auto result = SaveDatabase();
|
||||||
|
database.CleanDatabase();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DatabaseManager::DeleteFile() {
|
||||||
|
const bool result = Common::FS::RemoveFile(system_save_dir / DbFileName);
|
||||||
|
// TODO: Return proper FS error here
|
||||||
|
return result ? ResultSuccess : ResultUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseManager::Format(DatabaseSessionMetadata& metadata) {
|
||||||
|
database.CleanDatabase();
|
||||||
|
is_moddified = true;
|
||||||
|
update_counter++;
|
||||||
|
metadata.update_counter = update_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DatabaseManager::SaveDatabase() {
|
||||||
|
// TODO: Replace unknown error codes with proper FS error codes when available
|
||||||
|
|
||||||
|
if (!Common::FS::Exists(system_save_dir / DbFileName)) {
|
||||||
|
if (!Common::FS::NewFile(system_save_dir / DbFileName)) {
|
||||||
|
LOG_ERROR(Service_Mii, "Failed to create mii database");
|
||||||
|
return ResultUnknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto file_size = Common::FS::GetSize(system_save_dir / DbFileName);
|
||||||
|
if (file_size != 0 && file_size != sizeof(NintendoFigurineDatabase)) {
|
||||||
|
if (!Common::FS::RemoveFile(system_save_dir / DbFileName)) {
|
||||||
|
LOG_ERROR(Service_Mii, "Failed to delete mii database");
|
||||||
|
return ResultUnknown;
|
||||||
|
}
|
||||||
|
if (!Common::FS::NewFile(system_save_dir / DbFileName)) {
|
||||||
|
LOG_ERROR(Service_Mii, "Failed to create mii database");
|
||||||
|
return ResultUnknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Common::FS::IOFile db_file{system_save_dir / DbFileName,
|
||||||
|
Common::FS::FileAccessMode::ReadWrite,
|
||||||
|
Common::FS::FileType::BinaryFile};
|
||||||
|
|
||||||
|
if (db_file.Write(database) != 1) {
|
||||||
|
LOG_ERROR(Service_Mii, "Failed to save mii database");
|
||||||
|
return ResultUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_moddified = false;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::Mii
|
@ -0,0 +1,58 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/fs/fs.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
#include "core/hle/service/mii/mii_database.h"
|
||||||
|
|
||||||
|
namespace Service::Mii {
|
||||||
|
class CharInfo;
|
||||||
|
class StoreData;
|
||||||
|
|
||||||
|
class DatabaseManager {
|
||||||
|
public:
|
||||||
|
DatabaseManager();
|
||||||
|
Result MountSaveData();
|
||||||
|
Result Initialize(DatabaseSessionMetadata& metadata, bool& is_database_broken);
|
||||||
|
|
||||||
|
bool IsFullDatabase() const;
|
||||||
|
bool IsModified() const;
|
||||||
|
u64 GetUpdateCounter() const;
|
||||||
|
|
||||||
|
void Get(StoreData& out_store_data, std::size_t index,
|
||||||
|
const DatabaseSessionMetadata& metadata) const;
|
||||||
|
u32 GetCount(const DatabaseSessionMetadata& metadata) const;
|
||||||
|
|
||||||
|
Result FindIndex(s32& out_index, const Common::UUID& create_id, bool is_special) const;
|
||||||
|
Result FindIndex(const DatabaseSessionMetadata& metadata, u32& out_index,
|
||||||
|
const Common::UUID& create_id) const;
|
||||||
|
Result FindMoveIndex(u32& out_index, u32 new_index, const Common::UUID& create_id) const;
|
||||||
|
|
||||||
|
Result Move(DatabaseSessionMetadata& metadata, u32 current_index,
|
||||||
|
const Common::UUID& create_id);
|
||||||
|
Result AddOrReplace(DatabaseSessionMetadata& metadata, const StoreData& out_store_data);
|
||||||
|
Result Delete(DatabaseSessionMetadata& metadata, const Common::UUID& create_id);
|
||||||
|
Result Append(DatabaseSessionMetadata& metadata, const CharInfo& char_info);
|
||||||
|
|
||||||
|
Result DestroyFile(DatabaseSessionMetadata& metadata);
|
||||||
|
Result DeleteFile();
|
||||||
|
void Format(DatabaseSessionMetadata& metadata);
|
||||||
|
|
||||||
|
Result SaveDatabase();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// This is the global value of
|
||||||
|
// nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled");
|
||||||
|
bool is_test_db{};
|
||||||
|
|
||||||
|
bool is_moddified{};
|
||||||
|
bool is_save_data_mounted{};
|
||||||
|
u64 update_counter{};
|
||||||
|
NintendoFigurineDatabase database{};
|
||||||
|
|
||||||
|
std::filesystem::path system_save_dir{};
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace Service::Mii
|
Loading…
Reference in New Issue