forked from moonpower/azahar-UWP
Add Artic Base support (#105)
* Add Artic Base support * Add Android support
This commit is contained in:
535
src/core/file_sys/archive_artic.cpp
Normal file
535
src/core/file_sys/archive_artic.cpp
Normal file
@@ -0,0 +1,535 @@
|
||||
// Copyright 2024 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "archive_artic.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
std::vector<u8> ArticArchive::BuildFSPath(const Path& path) {
|
||||
std::vector<u8> ret(sizeof(u32) * 2);
|
||||
u32* raw_data = reinterpret_cast<u32*>(ret.data());
|
||||
auto path_type = path.GetType();
|
||||
auto binary = path.AsBinary();
|
||||
raw_data[0] = static_cast<u32>(path_type);
|
||||
raw_data[1] = static_cast<u32>(binary.size());
|
||||
if (!binary.empty()) {
|
||||
ret.insert(ret.end(), binary.begin(), binary.end());
|
||||
}
|
||||
|
||||
// The insert may have invalidated the pointer
|
||||
raw_data = reinterpret_cast<u32*>(ret.data());
|
||||
if (path_type != LowPathType::Binary && path_type != LowPathType::Invalid) {
|
||||
if (path_type == LowPathType::Wchar) {
|
||||
raw_data[1] += 2;
|
||||
ret.push_back(0);
|
||||
ret.push_back(0);
|
||||
} else {
|
||||
raw_data[1] += 1;
|
||||
ret.push_back(0);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result ArticArchive::RespResult(const std::optional<Network::ArticBase::Client::Response>& resp) {
|
||||
if (!resp.has_value() || !resp->Succeeded()) {
|
||||
return ResultUnknown;
|
||||
}
|
||||
return Result(static_cast<u32>(resp->GetMethodResult()));
|
||||
}
|
||||
|
||||
ArticArchive::~ArticArchive() {
|
||||
if (clear_cache_on_close) {
|
||||
cache_provider->ClearAllCache();
|
||||
}
|
||||
if (archive_handle != -1) {
|
||||
auto req = client->NewRequest("FSUSER_CloseArchive");
|
||||
req.AddParameterS64(archive_handle);
|
||||
client->Send(req);
|
||||
if (report_artic_event != Core::PerfStats::PerfArticEventBits::NONE) {
|
||||
client->ReportArticEvent(static_cast<u64>(report_artic_event));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArticArchive::Open(
|
||||
std::shared_ptr<Network::ArticBase::Client>& client, Service::FS::ArchiveIdCode archive_id,
|
||||
const Path& path, Core::PerfStats::PerfArticEventBits report_artic_event,
|
||||
ArticCacheProvider& cache_provider, bool clear_cache_on_close) {
|
||||
|
||||
auto req = client->NewRequest("FSUSER_OpenArchive");
|
||||
|
||||
req.AddParameterS32(static_cast<s32>(archive_id));
|
||||
auto path_buf = BuildFSPath(path);
|
||||
req.AddParameterBuffer(path_buf.data(), path_buf.size());
|
||||
|
||||
auto resp = client->Send(req);
|
||||
if (!resp.has_value() || !resp->Succeeded()) {
|
||||
return ResultUnknown;
|
||||
}
|
||||
Result res(static_cast<u32>(resp->GetMethodResult()));
|
||||
if (res.IsError())
|
||||
return res;
|
||||
|
||||
auto handle_opt = resp->GetResponseS64(0);
|
||||
if (!handle_opt.has_value()) {
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
return std::make_unique<ArticArchive>(client, *handle_opt, report_artic_event, cache_provider,
|
||||
path, clear_cache_on_close);
|
||||
}
|
||||
|
||||
void ArticArchive::Close() {
|
||||
if (clear_cache_on_close) {
|
||||
cache_provider->ClearAllCache();
|
||||
}
|
||||
|
||||
auto req = client->NewRequest("FSUSER_CloseArchive");
|
||||
req.AddParameterS64(archive_handle);
|
||||
if (RespResult(client->Send(req)).IsSuccess()) {
|
||||
archive_handle = -1;
|
||||
if (report_artic_event != Core::PerfStats::PerfArticEventBits::NONE) {
|
||||
client->ReportArticEvent(static_cast<u64>(report_artic_event));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ArticArchive::GetName() const {
|
||||
return "ArticArchive";
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> ArticArchive::OpenFile(const Path& path, const Mode& mode,
|
||||
u32 attributes) {
|
||||
if (mode.create_flag) {
|
||||
auto cache = cache_provider->ProvideCache(
|
||||
client, cache_provider->PathsToVector(archive_path, path), false);
|
||||
if (cache != nullptr) {
|
||||
cache->Clear();
|
||||
}
|
||||
}
|
||||
auto req = client->NewRequest("FSUSER_OpenFile");
|
||||
|
||||
req.AddParameterS64(archive_handle);
|
||||
auto path_buf = BuildFSPath(path);
|
||||
req.AddParameterBuffer(path_buf.data(), path_buf.size());
|
||||
req.AddParameterU32(mode.hex);
|
||||
req.AddParameterU32(attributes);
|
||||
|
||||
auto resp = client->Send(req);
|
||||
auto res = RespResult(resp);
|
||||
if (res.IsError())
|
||||
return res;
|
||||
|
||||
auto handle_opt = resp->GetResponseS32(0);
|
||||
if (!handle_opt.has_value())
|
||||
return ResultUnknown;
|
||||
|
||||
auto size_opt = resp->GetResponseU64(1);
|
||||
if (size_opt.has_value()) {
|
||||
auto cache = cache_provider->ProvideCache(
|
||||
client, cache_provider->PathsToVector(archive_path, path), true);
|
||||
if (cache != nullptr) {
|
||||
cache->ForceSetSize(static_cast<size_t>(*size_opt));
|
||||
}
|
||||
}
|
||||
|
||||
if (open_reporter->open_files++ == 0 &&
|
||||
report_artic_event != Core::PerfStats::PerfArticEventBits::NONE) {
|
||||
client->ReportArticEvent(static_cast<u64>(report_artic_event) | (1ULL << 32));
|
||||
}
|
||||
|
||||
return std::make_unique<ArticFileBackend>(client, *handle_opt, open_reporter, archive_path,
|
||||
*cache_provider, path);
|
||||
}
|
||||
|
||||
Result ArticArchive::DeleteFile(const Path& path) const {
|
||||
auto cache = cache_provider->ProvideCache(
|
||||
client, cache_provider->PathsToVector(archive_path, path), false);
|
||||
if (cache != nullptr) {
|
||||
cache->Clear();
|
||||
}
|
||||
|
||||
auto req = client->NewRequest("FSUSER_DeleteFile");
|
||||
|
||||
req.AddParameterS64(archive_handle);
|
||||
auto path_buf = BuildFSPath(path);
|
||||
req.AddParameterBuffer(path_buf.data(), path_buf.size());
|
||||
|
||||
return RespResult(client->Send(req));
|
||||
}
|
||||
|
||||
Result ArticArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
|
||||
auto cache = cache_provider->ProvideCache(
|
||||
client, cache_provider->PathsToVector(archive_path, src_path), false);
|
||||
if (cache != nullptr) {
|
||||
cache->Clear();
|
||||
}
|
||||
cache = cache_provider->ProvideCache(
|
||||
client, cache_provider->PathsToVector(archive_path, dest_path), false);
|
||||
if (cache != nullptr) {
|
||||
cache->Clear();
|
||||
}
|
||||
|
||||
auto req = client->NewRequest("FSUSER_RenameFile");
|
||||
|
||||
req.AddParameterS64(archive_handle);
|
||||
auto src_path_buf = BuildFSPath(src_path);
|
||||
req.AddParameterBuffer(src_path_buf.data(), src_path_buf.size());
|
||||
req.AddParameterS64(archive_handle);
|
||||
auto dest_path_buf = BuildFSPath(dest_path);
|
||||
req.AddParameterBuffer(dest_path_buf.data(), dest_path_buf.size());
|
||||
|
||||
return RespResult(client->Send(req));
|
||||
}
|
||||
|
||||
Result ArticArchive::DeleteDirectory(const Path& path) const {
|
||||
cache_provider->ClearAllCache();
|
||||
|
||||
auto req = client->NewRequest("FSUSER_DeleteDirectory");
|
||||
|
||||
req.AddParameterS64(archive_handle);
|
||||
auto path_buf = BuildFSPath(path);
|
||||
req.AddParameterBuffer(path_buf.data(), path_buf.size());
|
||||
|
||||
return RespResult(client->Send(req));
|
||||
}
|
||||
|
||||
Result ArticArchive::DeleteDirectoryRecursively(const Path& path) const {
|
||||
cache_provider->ClearAllCache();
|
||||
|
||||
auto req = client->NewRequest("FSUSER_DeleteDirectoryRec");
|
||||
|
||||
req.AddParameterS64(archive_handle);
|
||||
auto path_buf = BuildFSPath(path);
|
||||
req.AddParameterBuffer(path_buf.data(), path_buf.size());
|
||||
|
||||
return RespResult(client->Send(req));
|
||||
}
|
||||
|
||||
Result ArticArchive::CreateFile(const Path& path, u64 size, u32 attributes) const {
|
||||
auto cache = cache_provider->ProvideCache(
|
||||
client, cache_provider->PathsToVector(archive_path, path), false);
|
||||
if (cache != nullptr) {
|
||||
cache->Clear();
|
||||
}
|
||||
|
||||
auto req = client->NewRequest("FSUSER_CreateFile");
|
||||
|
||||
req.AddParameterS64(archive_handle);
|
||||
auto path_buf = BuildFSPath(path);
|
||||
req.AddParameterBuffer(path_buf.data(), path_buf.size());
|
||||
req.AddParameterU32(attributes);
|
||||
req.AddParameterU64(size);
|
||||
|
||||
return RespResult(client->Send(req));
|
||||
}
|
||||
|
||||
Result ArticArchive::CreateDirectory(const Path& path, u32 attributes) const {
|
||||
auto req = client->NewRequest("FSUSER_CreateDirectory");
|
||||
|
||||
req.AddParameterS64(archive_handle);
|
||||
auto path_buf = BuildFSPath(path);
|
||||
req.AddParameterBuffer(path_buf.data(), path_buf.size());
|
||||
req.AddParameterU32(attributes);
|
||||
|
||||
return RespResult(client->Send(req));
|
||||
}
|
||||
|
||||
Result ArticArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
|
||||
cache_provider->ClearAllCache();
|
||||
|
||||
auto req = client->NewRequest("FSUSER_RenameDirectory");
|
||||
|
||||
req.AddParameterS64(archive_handle);
|
||||
auto src_path_buf = BuildFSPath(src_path);
|
||||
req.AddParameterBuffer(src_path_buf.data(), src_path_buf.size());
|
||||
req.AddParameterS64(archive_handle);
|
||||
auto dest_path_buf = BuildFSPath(dest_path);
|
||||
req.AddParameterBuffer(dest_path_buf.data(), dest_path_buf.size());
|
||||
|
||||
return RespResult(client->Send(req));
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> ArticArchive::OpenDirectory(const Path& path) {
|
||||
auto req = client->NewRequest("FSUSER_OpenDirectory");
|
||||
|
||||
req.AddParameterS64(archive_handle);
|
||||
auto path_buf = BuildFSPath(path);
|
||||
req.AddParameterBuffer(path_buf.data(), path_buf.size());
|
||||
|
||||
auto resp = client->Send(req);
|
||||
auto res = RespResult(resp);
|
||||
if (res.IsError())
|
||||
return res;
|
||||
|
||||
auto handle_opt = resp->GetResponseS32(0);
|
||||
if (!handle_opt.has_value())
|
||||
return ResultUnknown;
|
||||
|
||||
if (open_reporter->open_files++ == 0 &&
|
||||
report_artic_event != Core::PerfStats::PerfArticEventBits::NONE) {
|
||||
client->ReportArticEvent(static_cast<u64>(report_artic_event) | (1ULL << 32));
|
||||
}
|
||||
|
||||
return std::make_unique<ArticDirectoryBackend>(client, *handle_opt, archive_path,
|
||||
open_reporter);
|
||||
}
|
||||
|
||||
u64 ArticArchive::GetFreeBytes() const {
|
||||
auto req = client->NewRequest("FSUSER_GetFreeBytes");
|
||||
|
||||
req.AddParameterS64(archive_handle);
|
||||
|
||||
auto resp = client->Send(req);
|
||||
auto res = RespResult(resp);
|
||||
if (res.IsError()) // TODO(PabloMK7): Return error code and not u64
|
||||
return 0;
|
||||
|
||||
auto free_bytes_opt = resp->GetResponseS64(0);
|
||||
return free_bytes_opt.has_value() ? static_cast<u64>(*free_bytes_opt) : 0;
|
||||
}
|
||||
|
||||
Result ArticArchive::Control(u32 action, u8* input, size_t input_size, u8* output,
|
||||
size_t output_size) {
|
||||
auto req = client->NewRequest("FSUSER_ControlArchive");
|
||||
|
||||
req.AddParameterS64(archive_handle);
|
||||
req.AddParameterU32(action);
|
||||
req.AddParameterBuffer(input, input_size);
|
||||
req.AddParameterU32(static_cast<u32>(output_size));
|
||||
|
||||
auto resp = client->Send(req);
|
||||
auto res = RespResult(resp);
|
||||
if (res.IsError())
|
||||
return res;
|
||||
|
||||
auto output_buf = resp->GetResponseBuffer(0);
|
||||
if (!output_buf.has_value())
|
||||
return res;
|
||||
|
||||
if (output_buf->second != output_size)
|
||||
return ResultUnknown;
|
||||
|
||||
memcpy(output, output_buf->first, output_buf->second);
|
||||
return res;
|
||||
}
|
||||
|
||||
Result ArticArchive::SetSaveDataSecureValue(u32 secure_value_slot, u64 secure_value, bool flush) {
|
||||
auto req = client->NewRequest("FSUSER_SetSaveDataSecureValue");
|
||||
|
||||
req.AddParameterS64(archive_handle);
|
||||
req.AddParameterU32(secure_value_slot);
|
||||
req.AddParameterU64(secure_value);
|
||||
req.AddParameterS8(flush != 0);
|
||||
|
||||
return RespResult(client->Send(req));
|
||||
}
|
||||
|
||||
ResultVal<std::tuple<bool, bool, u64>> ArticArchive::GetSaveDataSecureValue(u32 secure_value_slot) {
|
||||
auto req = client->NewRequest("FSUSER_GetSaveDataSecureValue");
|
||||
|
||||
req.AddParameterS64(archive_handle);
|
||||
req.AddParameterU32(secure_value_slot);
|
||||
|
||||
auto resp = client->Send(req);
|
||||
auto res = RespResult(resp);
|
||||
if (res.IsError())
|
||||
return res;
|
||||
|
||||
struct {
|
||||
bool exists;
|
||||
bool isGamecard;
|
||||
u64 secure_value;
|
||||
} secure_value_result;
|
||||
static_assert(sizeof(secure_value_result) == 0x10);
|
||||
|
||||
auto output_buf = resp->GetResponseBuffer(0);
|
||||
if (!output_buf.has_value())
|
||||
return res;
|
||||
|
||||
if (output_buf->second != sizeof(secure_value_result))
|
||||
return ResultUnknown;
|
||||
|
||||
memcpy(&secure_value_result, output_buf->first, output_buf->second);
|
||||
return std::make_tuple(secure_value_result.exists, secure_value_result.isGamecard,
|
||||
secure_value_result.secure_value);
|
||||
}
|
||||
|
||||
void ArticArchive::OpenFileReporter::OnFileClosed() {
|
||||
if (--open_files == 0 && report_artic_event != Core::PerfStats::PerfArticEventBits::NONE) {
|
||||
client->ReportArticEvent(static_cast<u64>(report_artic_event));
|
||||
}
|
||||
}
|
||||
|
||||
void ArticArchive::OpenFileReporter::OnDirectoryClosed() {
|
||||
if (--open_files == 0 && report_artic_event != Core::PerfStats::PerfArticEventBits::NONE) {
|
||||
client->ReportArticEvent(static_cast<u64>(report_artic_event));
|
||||
}
|
||||
}
|
||||
|
||||
ArticFileBackend::~ArticFileBackend() {
|
||||
if (file_handle != -1) {
|
||||
auto req = client->NewRequest("FSFILE_Close");
|
||||
req.AddParameterS32(file_handle);
|
||||
client->Send(req);
|
||||
open_reporter->OnFileClosed();
|
||||
}
|
||||
}
|
||||
|
||||
ResultVal<std::size_t> ArticFileBackend::Read(u64 offset, std::size_t length, u8* buffer) const {
|
||||
auto cache = cache_provider->ProvideCache(
|
||||
client, cache_provider->PathsToVector(archive_path, file_path), true);
|
||||
|
||||
if (cache != nullptr) {
|
||||
return cache->Read(file_handle, offset, length, buffer);
|
||||
}
|
||||
|
||||
auto req = client->NewRequest("FSFILE_Read");
|
||||
|
||||
req.AddParameterS32(file_handle);
|
||||
req.AddParameterU64(offset);
|
||||
req.AddParameterU32(static_cast<u32>(length));
|
||||
|
||||
auto resp = client->Send(req);
|
||||
auto res = ArticArchive::RespResult(resp);
|
||||
if (res.IsError())
|
||||
return res;
|
||||
|
||||
auto read_buf = resp->GetResponseBuffer(0);
|
||||
if (!read_buf || read_buf->second > length) {
|
||||
return std::size_t(0);
|
||||
}
|
||||
|
||||
memcpy(buffer, read_buf->first, read_buf->second);
|
||||
return read_buf->second;
|
||||
}
|
||||
|
||||
ResultVal<std::size_t> ArticFileBackend::Write(u64 offset, std::size_t length, bool flush,
|
||||
bool update_timestamp, const u8* buffer) {
|
||||
u32 flags = (flush ? 1 : 0) | (update_timestamp ? (1 << 8) : 0);
|
||||
auto cache = cache_provider->ProvideCache(
|
||||
client, cache_provider->PathsToVector(archive_path, file_path), true);
|
||||
if (cache != nullptr) {
|
||||
return cache->Write(file_handle, offset, length, buffer, flags);
|
||||
} else {
|
||||
auto req = client->NewRequest("FSFILE_Write");
|
||||
|
||||
req.AddParameterS32(file_handle);
|
||||
req.AddParameterU64(offset);
|
||||
req.AddParameterU32(static_cast<u32>(length));
|
||||
req.AddParameterU32(flags);
|
||||
req.AddParameterBuffer(buffer, length);
|
||||
|
||||
auto resp = client->Send(req);
|
||||
auto res = ArticArchive::RespResult(resp);
|
||||
if (res.IsError())
|
||||
return res;
|
||||
|
||||
auto writen_buf = resp->GetResponseS32(0);
|
||||
if (!writen_buf) {
|
||||
return std::size_t(0);
|
||||
}
|
||||
|
||||
return std::size_t(*writen_buf);
|
||||
}
|
||||
}
|
||||
|
||||
u64 ArticFileBackend::GetSize() const {
|
||||
auto cache = cache_provider->ProvideCache(
|
||||
client, cache_provider->PathsToVector(archive_path, file_path), true);
|
||||
if (cache != nullptr) {
|
||||
auto res = cache->GetSize(file_handle);
|
||||
if (res.Failed())
|
||||
return 0;
|
||||
return res.Unwrap();
|
||||
} else {
|
||||
|
||||
auto req = client->NewRequest("FSFILE_GetSize");
|
||||
|
||||
req.AddParameterS32(file_handle);
|
||||
|
||||
auto resp = client->Send(req);
|
||||
auto res = ArticArchive::RespResult(resp);
|
||||
if (res.IsError())
|
||||
return 0;
|
||||
|
||||
auto size_buf = resp->GetResponseS64(0);
|
||||
if (!size_buf) {
|
||||
return 0;
|
||||
}
|
||||
return *size_buf;
|
||||
}
|
||||
}
|
||||
|
||||
bool ArticFileBackend::SetSize(u64 size) const {
|
||||
auto req = client->NewRequest("FSFILE_SetSize");
|
||||
|
||||
req.AddParameterS32(file_handle);
|
||||
req.AddParameterU64(size);
|
||||
|
||||
return ArticArchive::RespResult(client->Send(req)).IsSuccess();
|
||||
}
|
||||
|
||||
bool ArticFileBackend::Close() {
|
||||
auto req = client->NewRequest("FSFILE_Close");
|
||||
req.AddParameterS32(file_handle);
|
||||
bool ret = ArticArchive::RespResult(client->Send(req)).IsSuccess();
|
||||
if (ret) {
|
||||
file_handle = -1;
|
||||
open_reporter->OnFileClosed();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ArticFileBackend::Flush() const {
|
||||
auto req = client->NewRequest("FSFILE_Flush");
|
||||
|
||||
req.AddParameterS32(file_handle);
|
||||
|
||||
client->Send(req);
|
||||
}
|
||||
|
||||
ArticDirectoryBackend::~ArticDirectoryBackend() {
|
||||
if (dir_handle != -1) {
|
||||
auto req = client->NewRequest("FSDIR_Close");
|
||||
req.AddParameterS32(dir_handle);
|
||||
client->Send(req);
|
||||
open_reporter->OnDirectoryClosed();
|
||||
}
|
||||
}
|
||||
|
||||
u32 ArticDirectoryBackend::Read(const u32 count, Entry* entries) {
|
||||
auto req = client->NewRequest("FSDIR_Read");
|
||||
|
||||
req.AddParameterS32(dir_handle);
|
||||
req.AddParameterU32(count);
|
||||
|
||||
auto resp = client->Send(req);
|
||||
auto res = ArticArchive::RespResult(resp);
|
||||
if (res.IsError())
|
||||
return 0;
|
||||
|
||||
auto entry_buf = resp->GetResponseBuffer(0);
|
||||
if (!entry_buf) {
|
||||
return 0;
|
||||
}
|
||||
u32 ret_count = static_cast<u32>(entry_buf->second / sizeof(Entry));
|
||||
|
||||
memcpy(entries, entry_buf->first, ret_count * sizeof(Entry));
|
||||
return ret_count;
|
||||
}
|
||||
|
||||
bool ArticDirectoryBackend::Close() {
|
||||
auto req = client->NewRequest("FSDIR_Close");
|
||||
req.AddParameterS32(dir_handle);
|
||||
bool ret = ArticArchive::RespResult(client->Send(req)).IsSuccess();
|
||||
if (ret) {
|
||||
dir_handle = -1;
|
||||
open_reporter->OnDirectoryClosed();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
} // namespace FileSys
|
||||
268
src/core/file_sys/archive_artic.h
Normal file
268
src/core/file_sys/archive_artic.h
Normal file
@@ -0,0 +1,268 @@
|
||||
// Copyright 2024 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "atomic"
|
||||
|
||||
#include <boost/serialization/unique_ptr.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/file_sys/artic_cache.h"
|
||||
#include "core/file_sys/directory_backend.h"
|
||||
#include "core/file_sys/file_backend.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "network/artic_base/artic_base_client.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class ArticArchive : public ArchiveBackend {
|
||||
public:
|
||||
static std::vector<u8> BuildFSPath(const Path& path);
|
||||
static Result RespResult(const std::optional<Network::ArticBase::Client::Response>& resp);
|
||||
|
||||
explicit ArticArchive(std::shared_ptr<Network::ArticBase::Client>& _client, s64 _archive_handle,
|
||||
Core::PerfStats::PerfArticEventBits _report_artic_event,
|
||||
ArticCacheProvider& _cache_provider, const Path& _archive_path,
|
||||
bool _clear_cache_on_close)
|
||||
: client(_client), archive_handle(_archive_handle), report_artic_event(_report_artic_event),
|
||||
cache_provider(&_cache_provider), archive_path(_archive_path),
|
||||
clear_cache_on_close(_clear_cache_on_close) {
|
||||
open_reporter = std::make_shared<OpenFileReporter>(_client, _report_artic_event);
|
||||
}
|
||||
~ArticArchive() override;
|
||||
|
||||
static ResultVal<std::unique_ptr<ArchiveBackend>> Open(
|
||||
std::shared_ptr<Network::ArticBase::Client>& client, Service::FS::ArchiveIdCode archive_id,
|
||||
const Path& path, Core::PerfStats::PerfArticEventBits report_artic_event,
|
||||
ArticCacheProvider& cache_provider, bool clear_cache_on_close);
|
||||
|
||||
void Close() override;
|
||||
|
||||
/**
|
||||
* Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
|
||||
*/
|
||||
std::string GetName() const override;
|
||||
|
||||
/**
|
||||
* Open a file specified by its path, using the specified mode
|
||||
* @param path Path relative to the archive
|
||||
* @param mode Mode to open the file with
|
||||
* @return Opened file, or error code
|
||||
*/
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode& mode,
|
||||
u32 attributes) override;
|
||||
|
||||
/**
|
||||
* Delete a file specified by its path
|
||||
* @param path Path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
Result DeleteFile(const Path& path) const override;
|
||||
|
||||
/**
|
||||
* Rename a File specified by its path
|
||||
* @param src_path Source path relative to the archive
|
||||
* @param dest_path Destination path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
Result RenameFile(const Path& src_path, const Path& dest_path) const override;
|
||||
|
||||
/**
|
||||
* Delete a directory specified by its path
|
||||
* @param path Path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
Result DeleteDirectory(const Path& path) const override;
|
||||
|
||||
/**
|
||||
* Delete a directory specified by its path and anything under it
|
||||
* @param path Path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
Result DeleteDirectoryRecursively(const Path& path) const override;
|
||||
|
||||
/**
|
||||
* Create a file specified by its path
|
||||
* @param path Path relative to the Archive
|
||||
* @param size The size of the new file, filled with zeroes
|
||||
* @return Result of the operation
|
||||
*/
|
||||
Result CreateFile(const Path& path, u64 size, u32 attributes) const override;
|
||||
|
||||
/**
|
||||
* Create a directory specified by its path
|
||||
* @param path Path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
Result CreateDirectory(const Path& path, u32 attributes) const override;
|
||||
|
||||
/**
|
||||
* Rename a Directory specified by its path
|
||||
* @param src_path Source path relative to the archive
|
||||
* @param dest_path Destination path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
Result RenameDirectory(const Path& src_path, const Path& dest_path) const override;
|
||||
|
||||
/**
|
||||
* Open a directory specified by its path
|
||||
* @param path Path relative to the archive
|
||||
* @return Opened directory, or error code
|
||||
*/
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) override;
|
||||
|
||||
/**
|
||||
* Get the free space
|
||||
* @return The number of free bytes in the archive
|
||||
*/
|
||||
u64 GetFreeBytes() const override;
|
||||
|
||||
Result Control(u32 action, u8* input, size_t input_size, u8* output,
|
||||
size_t output_size) override;
|
||||
|
||||
Result SetSaveDataSecureValue(u32 secure_value_slot, u64 secure_value, bool flush) override;
|
||||
|
||||
ResultVal<std::tuple<bool, bool, u64>> GetSaveDataSecureValue(u32 secure_value_slot) override;
|
||||
|
||||
bool IsSlow() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
const Path& GetArchivePath() {
|
||||
return archive_path;
|
||||
}
|
||||
|
||||
protected:
|
||||
ArticArchive() = default;
|
||||
|
||||
private:
|
||||
friend class ArticFileBackend;
|
||||
friend class ArticDirectoryBackend;
|
||||
class OpenFileReporter {
|
||||
public:
|
||||
OpenFileReporter(const std::shared_ptr<Network::ArticBase::Client>& cli,
|
||||
Core::PerfStats::PerfArticEventBits _report_artic_event)
|
||||
: client(cli), report_artic_event(_report_artic_event) {}
|
||||
|
||||
void OnFileClosed();
|
||||
|
||||
void OnDirectoryClosed();
|
||||
|
||||
std::shared_ptr<Network::ArticBase::Client> client;
|
||||
Core::PerfStats::PerfArticEventBits report_artic_event =
|
||||
Core::PerfStats::PerfArticEventBits::NONE;
|
||||
std::atomic<u32> open_files = 0;
|
||||
};
|
||||
|
||||
std::shared_ptr<Network::ArticBase::Client> client;
|
||||
s64 archive_handle;
|
||||
std::shared_ptr<OpenFileReporter> open_reporter;
|
||||
Core::PerfStats::PerfArticEventBits report_artic_event =
|
||||
Core::PerfStats::PerfArticEventBits::NONE;
|
||||
ArticCacheProvider* cache_provider = nullptr;
|
||||
Path archive_path;
|
||||
bool clear_cache_on_close;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveBackend>(*this);
|
||||
ar& archive_handle;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class ArticFileBackend : public FileBackend {
|
||||
public:
|
||||
explicit ArticFileBackend(std::shared_ptr<Network::ArticBase::Client>& _client,
|
||||
s32 _file_handle,
|
||||
const std::shared_ptr<ArticArchive::OpenFileReporter>& _open_reporter,
|
||||
const Path& _archive_path, ArticCacheProvider& _cache_provider,
|
||||
const Path& _file_path)
|
||||
: client(_client), file_handle(_file_handle), open_reporter(_open_reporter),
|
||||
archive_path(_archive_path), cache_provider(&_cache_provider), file_path(_file_path) {}
|
||||
~ArticFileBackend() override;
|
||||
|
||||
ResultVal<std::size_t> Read(u64 offset, std::size_t length, u8* buffer) const override;
|
||||
|
||||
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush, bool update_timestamp,
|
||||
const u8* buffer) override;
|
||||
|
||||
u64 GetSize() const override;
|
||||
|
||||
bool SetSize(u64 size) const override;
|
||||
|
||||
bool Close() override;
|
||||
|
||||
void Flush() const override;
|
||||
|
||||
bool AllowsCachedReads() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CacheReady(std::size_t file_offset, std::size_t length) override {
|
||||
auto cache = cache_provider->ProvideCache(
|
||||
client, cache_provider->PathsToVector(archive_path, file_path), true);
|
||||
if (cache == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return cache->CacheReady(file_offset, length);
|
||||
}
|
||||
|
||||
protected:
|
||||
ArticFileBackend() = default;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Network::ArticBase::Client> client;
|
||||
s32 file_handle;
|
||||
std::shared_ptr<ArticArchive::OpenFileReporter> open_reporter;
|
||||
Path archive_path;
|
||||
ArticCacheProvider* cache_provider = nullptr;
|
||||
Path file_path;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<FileBackend>(*this);
|
||||
ar& file_handle;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class ArticDirectoryBackend : public DirectoryBackend {
|
||||
public:
|
||||
explicit ArticDirectoryBackend(
|
||||
std::shared_ptr<Network::ArticBase::Client>& _client, s32 _dir_handle,
|
||||
const Path& _archive_path,
|
||||
const std::shared_ptr<ArticArchive::OpenFileReporter>& _open_reporter)
|
||||
: client(_client), dir_handle(_dir_handle), archive_path(_archive_path),
|
||||
open_reporter(_open_reporter) {}
|
||||
~ArticDirectoryBackend() override;
|
||||
|
||||
u32 Read(const u32 count, Entry* entries) override;
|
||||
bool Close() override;
|
||||
|
||||
bool IsSlow() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
ArticDirectoryBackend() = default;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Network::ArticBase::Client> client;
|
||||
s32 dir_handle;
|
||||
Path archive_path;
|
||||
std::shared_ptr<ArticArchive::OpenFileReporter> open_reporter;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<DirectoryBackend>(*this);
|
||||
ar& dir_handle;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArticArchive)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArticFileBackend)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArticDirectoryBackend)
|
||||
@@ -105,8 +105,7 @@ std::vector<u8> Path::AsBinary() const {
|
||||
std::vector<u8> to_return(u16str.size() * 2);
|
||||
for (std::size_t i = 0; i < u16str.size(); ++i) {
|
||||
u16 tmp_char = u16str.at(i);
|
||||
to_return[i * 2] = (tmp_char & 0xFF00) >> 8;
|
||||
to_return[i * 2 + 1] = (tmp_char & 0x00FF);
|
||||
*reinterpret_cast<u16*>(to_return.data() + i * 2) = tmp_char;
|
||||
}
|
||||
return to_return;
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@ struct ArchiveFormatInfo {
|
||||
u8 duplicate_data; ///< Whether the archive should duplicate the data.
|
||||
};
|
||||
static_assert(std::is_trivial_v<ArchiveFormatInfo>, "ArchiveFormatInfo is not POD");
|
||||
static_assert(sizeof(ArchiveFormatInfo) == 16, "Invalid ArchiveFormatInfo size");
|
||||
|
||||
class ArchiveBackend : NonCopyable {
|
||||
public:
|
||||
@@ -119,8 +120,8 @@ public:
|
||||
* @param mode Mode to open the file with
|
||||
* @return Opened file, or error code
|
||||
*/
|
||||
virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
|
||||
const Mode& mode) const = 0;
|
||||
virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode& mode,
|
||||
u32 attributes = 0) = 0;
|
||||
|
||||
/**
|
||||
* Delete a file specified by its path
|
||||
@@ -157,14 +158,14 @@ public:
|
||||
* @param size The size of the new file, filled with zeroes
|
||||
* @return Result of the operation
|
||||
*/
|
||||
virtual Result CreateFile(const Path& path, u64 size) const = 0;
|
||||
virtual Result CreateFile(const Path& path, u64 size, u32 attributes = 0) const = 0;
|
||||
|
||||
/**
|
||||
* Create a directory specified by its path
|
||||
* @param path Path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
virtual Result CreateDirectory(const Path& path) const = 0;
|
||||
virtual Result CreateDirectory(const Path& path, u32 attributes = 0) const = 0;
|
||||
|
||||
/**
|
||||
* Rename a Directory specified by its path
|
||||
@@ -179,7 +180,7 @@ public:
|
||||
* @param path Path relative to the archive
|
||||
* @return Opened directory, or error code
|
||||
*/
|
||||
virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const = 0;
|
||||
virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) = 0;
|
||||
|
||||
/**
|
||||
* Get the free space
|
||||
@@ -187,6 +188,20 @@ public:
|
||||
*/
|
||||
virtual u64 GetFreeBytes() const = 0;
|
||||
|
||||
/**
|
||||
* Close the archive
|
||||
*/
|
||||
virtual void Close() {}
|
||||
|
||||
virtual Result Control(u32 action, u8* input, size_t input_size, u8* output,
|
||||
size_t output_size) {
|
||||
LOG_WARNING(Service_FS,
|
||||
"(STUBBED) called, archive={}, action={:08X}, input_size={:08X}, "
|
||||
"output_size={:08X}",
|
||||
GetName(), action, input_size, output_size);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
u64 GetOpenDelayNs() {
|
||||
if (delay_generator != nullptr) {
|
||||
return delay_generator->GetOpenDelayNs();
|
||||
@@ -196,6 +211,31 @@ public:
|
||||
return delay_generator->GetOpenDelayNs();
|
||||
}
|
||||
|
||||
virtual Result SetSaveDataSecureValue(u32 secure_value_slot, u64 secure_value, bool flush) {
|
||||
|
||||
// TODO: Generate and Save the Secure Value
|
||||
|
||||
LOG_WARNING(Service_FS,
|
||||
"(STUBBED) called, value=0x{:016x} secure_value_slot=0x{:04X} "
|
||||
"flush={}",
|
||||
secure_value, secure_value_slot, flush);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
virtual ResultVal<std::tuple<bool, bool, u64>> GetSaveDataSecureValue(u32 secure_value_slot) {
|
||||
|
||||
// TODO: Implement Secure Value Lookup & Generation
|
||||
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called secure_value_slot=0x{:08X}", secure_value_slot);
|
||||
|
||||
return std::make_tuple<bool, bool, u64>(false, true, 0);
|
||||
}
|
||||
|
||||
virtual bool IsSlow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<DelayGenerator> delay_generator;
|
||||
|
||||
@@ -232,7 +272,7 @@ public:
|
||||
* @return Result of the operation, 0 on success
|
||||
*/
|
||||
virtual Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) = 0;
|
||||
u64 program_id, u32 directory_buckets, u32 file_buckets) = 0;
|
||||
|
||||
/**
|
||||
* Retrieves the format info about the archive with the specified path
|
||||
@@ -242,6 +282,10 @@ public:
|
||||
*/
|
||||
virtual ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const = 0;
|
||||
|
||||
virtual bool IsSlow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {}
|
||||
friend class boost::serialization::access;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/archive_artic.h"
|
||||
#include "core/file_sys/archive_extsavedata.h"
|
||||
#include "core/file_sys/disk_archive.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
@@ -37,7 +38,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush,
|
||||
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush, bool update_timestamp,
|
||||
const u8* buffer) override {
|
||||
if (offset > size) {
|
||||
return ResultWriteBeyondEnd;
|
||||
@@ -49,7 +50,7 @@ public:
|
||||
length = size - offset;
|
||||
}
|
||||
|
||||
return DiskFile::Write(offset, length, flush, buffer);
|
||||
return DiskFile::Write(offset, length, flush, update_timestamp, buffer);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -100,8 +101,8 @@ public:
|
||||
return "ExtSaveDataArchive: " + mount_point;
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
|
||||
const Mode& mode) const override {
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode& mode,
|
||||
u32 attributes) override {
|
||||
LOG_DEBUG(Service_FS, "called path={} mode={:01X}", path.DebugStr(), mode.hex);
|
||||
|
||||
const PathParser path_parser(path);
|
||||
@@ -234,69 +235,187 @@ Path ArchiveFactory_ExtSaveData::GetCorrectedPath(const Path& path) {
|
||||
return {binary_data};
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(const Path& path,
|
||||
u64 program_id) {
|
||||
const auto directory = type == ExtSaveDataType::Boss ? "boss/" : "user/";
|
||||
const auto fullpath = GetExtSaveDataPath(mount_point, GetCorrectedPath(path)) + directory;
|
||||
if (!FileUtil::Exists(fullpath)) {
|
||||
// TODO(Subv): Verify the archive behavior of SharedExtSaveData compared to ExtSaveData.
|
||||
// ExtSaveData seems to return FS_NotFound (120) when the archive doesn't exist.
|
||||
if (type != ExtSaveDataType::Shared) {
|
||||
return ResultNotFoundInvalidState;
|
||||
} else {
|
||||
return ResultNotFormatted;
|
||||
}
|
||||
static Service::FS::ArchiveIdCode ExtSaveDataTypeToArchiveID(ExtSaveDataType type) {
|
||||
switch (type) {
|
||||
case FileSys::ExtSaveDataType::Normal:
|
||||
return Service::FS::ArchiveIdCode::ExtSaveData;
|
||||
case FileSys::ExtSaveDataType::Shared:
|
||||
return Service::FS::ArchiveIdCode::SharedExtSaveData;
|
||||
case FileSys::ExtSaveDataType::Boss:
|
||||
return Service::FS::ArchiveIdCode::BossExtSaveData;
|
||||
default:
|
||||
return Service::FS::ArchiveIdCode::ExtSaveData;
|
||||
}
|
||||
std::unique_ptr<DelayGenerator> delay_generator = std::make_unique<ExtSaveDataDelayGenerator>();
|
||||
return std::make_unique<ExtSaveDataArchive>(fullpath, std::move(delay_generator));
|
||||
}
|
||||
|
||||
Result ArchiveFactory_ExtSaveData::Format(const Path& path,
|
||||
const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) {
|
||||
auto corrected_path = GetCorrectedPath(path);
|
||||
|
||||
// These folders are always created with the ExtSaveData
|
||||
std::string user_path = GetExtSaveDataPath(mount_point, corrected_path) + "user/";
|
||||
std::string boss_path = GetExtSaveDataPath(mount_point, corrected_path) + "boss/";
|
||||
FileUtil::CreateFullPath(user_path);
|
||||
FileUtil::CreateFullPath(boss_path);
|
||||
|
||||
// Write the format metadata
|
||||
std::string metadata_path = GetExtSaveDataPath(mount_point, corrected_path) + "metadata";
|
||||
FileUtil::IOFile file(metadata_path, "wb");
|
||||
|
||||
if (!file.IsOpen()) {
|
||||
// TODO(Subv): Find the correct error code
|
||||
return ResultUnknown;
|
||||
static Core::PerfStats::PerfArticEventBits ExtSaveDataTypeToPerfArtic(ExtSaveDataType type) {
|
||||
switch (type) {
|
||||
case FileSys::ExtSaveDataType::Normal:
|
||||
return Core::PerfStats::PerfArticEventBits::ARTIC_EXT_DATA;
|
||||
case FileSys::ExtSaveDataType::Shared:
|
||||
return Core::PerfStats::PerfArticEventBits::ARTIC_SHARED_EXT_DATA;
|
||||
case FileSys::ExtSaveDataType::Boss:
|
||||
return Core::PerfStats::PerfArticEventBits::ARTIC_BOSS_EXT_DATA;
|
||||
default:
|
||||
return Core::PerfStats::PerfArticEventBits::ARTIC_EXT_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
file.WriteBytes(&format_info, sizeof(format_info));
|
||||
return ResultSuccess;
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(const Path& path,
|
||||
u64 program_id) {
|
||||
if (IsUsingArtic()) {
|
||||
EnsureCacheCreated();
|
||||
return ArticArchive::Open(artic_client, ExtSaveDataTypeToArchiveID(type), path,
|
||||
ExtSaveDataTypeToPerfArtic(type), *this,
|
||||
type != FileSys::ExtSaveDataType::Normal);
|
||||
} else {
|
||||
const auto directory = type == ExtSaveDataType::Boss ? "boss/" : "user/";
|
||||
const auto fullpath = GetExtSaveDataPath(mount_point, GetCorrectedPath(path)) + directory;
|
||||
if (!FileUtil::Exists(fullpath)) {
|
||||
// TODO(Subv): Verify the archive behavior of SharedExtSaveData compared to ExtSaveData.
|
||||
// ExtSaveData seems to return FS_NotFound (120) when the archive doesn't exist.
|
||||
if (type != ExtSaveDataType::Shared) {
|
||||
return ResultNotFoundInvalidState;
|
||||
} else {
|
||||
return ResultNotFormatted;
|
||||
}
|
||||
}
|
||||
std::unique_ptr<DelayGenerator> delay_generator =
|
||||
std::make_unique<ExtSaveDataDelayGenerator>();
|
||||
return std::make_unique<ExtSaveDataArchive>(fullpath, std::move(delay_generator));
|
||||
}
|
||||
}
|
||||
|
||||
Result ArchiveFactory_ExtSaveData::FormatAsExtData(const Path& path,
|
||||
const FileSys::ArchiveFormatInfo& format_info,
|
||||
u8 unknown, u64 program_id, u64 total_size,
|
||||
std::span<const u8> icon) {
|
||||
if (IsUsingArtic()) {
|
||||
ExtSaveDataArchivePath path_data;
|
||||
std::memcpy(&path_data, path.AsBinary().data(), sizeof(path_data));
|
||||
|
||||
Service::FS::ExtSaveDataInfo artic_extdata_path;
|
||||
|
||||
artic_extdata_path.media_type = static_cast<u8>(path_data.media_type);
|
||||
artic_extdata_path.unknown = unknown;
|
||||
artic_extdata_path.save_id_low = path_data.save_low;
|
||||
artic_extdata_path.save_id_high = path_data.save_high;
|
||||
|
||||
auto req = artic_client->NewRequest("FSUSER_CreateExtSaveData");
|
||||
|
||||
req.AddParameterBuffer(&artic_extdata_path, sizeof(artic_extdata_path));
|
||||
req.AddParameterU32(format_info.number_directories);
|
||||
req.AddParameterU32(format_info.number_files);
|
||||
req.AddParameterU64(total_size);
|
||||
req.AddParameterBuffer(icon.data(), icon.size());
|
||||
|
||||
return ArticArchive::RespResult(artic_client->Send(req));
|
||||
} else {
|
||||
auto corrected_path = GetCorrectedPath(path);
|
||||
|
||||
// These folders are always created with the ExtSaveData
|
||||
std::string user_path = GetExtSaveDataPath(mount_point, corrected_path) + "user/";
|
||||
std::string boss_path = GetExtSaveDataPath(mount_point, corrected_path) + "boss/";
|
||||
FileUtil::CreateFullPath(user_path);
|
||||
FileUtil::CreateFullPath(boss_path);
|
||||
|
||||
// Write the format metadata
|
||||
std::string metadata_path = GetExtSaveDataPath(mount_point, corrected_path) + "metadata";
|
||||
FileUtil::IOFile file(metadata_path, "wb");
|
||||
|
||||
if (!file.IsOpen()) {
|
||||
// TODO(Subv): Find the correct error code
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
file.WriteBytes(&format_info, sizeof(format_info));
|
||||
|
||||
FileUtil::IOFile icon_file(FileSys::GetExtSaveDataPath(GetMountPoint(), path) + "icon",
|
||||
"wb");
|
||||
icon_file.WriteBytes(icon.data(), icon.size());
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
Result ArchiveFactory_ExtSaveData::DeleteExtData(Service::FS::MediaType media_type, u8 unknown,
|
||||
u32 high, u32 low) {
|
||||
if (IsUsingArtic()) {
|
||||
Service::FS::ExtSaveDataInfo artic_extdata_path;
|
||||
|
||||
artic_extdata_path.media_type = static_cast<u8>(media_type);
|
||||
artic_extdata_path.unknown = unknown;
|
||||
artic_extdata_path.save_id_low = low;
|
||||
artic_extdata_path.save_id_high = high;
|
||||
|
||||
auto req = artic_client->NewRequest("FSUSER_DeleteExtSaveData");
|
||||
|
||||
req.AddParameterBuffer(&artic_extdata_path, sizeof(artic_extdata_path));
|
||||
|
||||
return ArticArchive::RespResult(artic_client->Send(req));
|
||||
} else {
|
||||
// Construct the binary path to the archive first
|
||||
FileSys::Path path =
|
||||
FileSys::ConstructExtDataBinaryPath(static_cast<u32>(media_type), high, low);
|
||||
|
||||
std::string media_type_directory;
|
||||
if (media_type == Service::FS::MediaType::NAND) {
|
||||
media_type_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||
} else if (media_type == Service::FS::MediaType::SDMC) {
|
||||
media_type_directory = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
|
||||
} else {
|
||||
LOG_ERROR(Service_FS, "Unsupported media type {}", media_type);
|
||||
return ResultUnknown; // TODO(Subv): Find the right error code
|
||||
}
|
||||
|
||||
// Delete all directories (/user, /boss) and the icon file.
|
||||
std::string base_path = FileSys::GetExtDataContainerPath(
|
||||
media_type_directory, media_type == Service::FS::MediaType::NAND);
|
||||
std::string extsavedata_path = FileSys::GetExtSaveDataPath(base_path, path);
|
||||
if (FileUtil::Exists(extsavedata_path) && !FileUtil::DeleteDirRecursively(extsavedata_path))
|
||||
return ResultUnknown; // TODO(Subv): Find the right error code
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
ResultVal<ArchiveFormatInfo> ArchiveFactory_ExtSaveData::GetFormatInfo(const Path& path,
|
||||
u64 program_id) const {
|
||||
std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata";
|
||||
FileUtil::IOFile file(metadata_path, "rb");
|
||||
if (IsUsingArtic()) {
|
||||
auto req = artic_client->NewRequest("FSUSER_GetFormatInfo");
|
||||
|
||||
if (!file.IsOpen()) {
|
||||
LOG_ERROR(Service_FS, "Could not open metadata information for archive");
|
||||
// TODO(Subv): Verify error code
|
||||
return ResultNotFormatted;
|
||||
req.AddParameterS32(static_cast<u32>(ExtSaveDataTypeToArchiveID(type)));
|
||||
auto path_artic = ArticArchive::BuildFSPath(path);
|
||||
req.AddParameterBuffer(path_artic.data(), path_artic.size());
|
||||
|
||||
auto resp = artic_client->Send(req);
|
||||
Result res = ArticArchive::RespResult(resp);
|
||||
if (R_FAILED(res)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
auto info_buf = resp->GetResponseBuffer(0);
|
||||
if (!info_buf.has_value() || info_buf->second != sizeof(ArchiveFormatInfo)) {
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
ArchiveFormatInfo info;
|
||||
memcpy(&info, info_buf->first, sizeof(info));
|
||||
return info;
|
||||
} else {
|
||||
std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata";
|
||||
FileUtil::IOFile file(metadata_path, "rb");
|
||||
|
||||
if (!file.IsOpen()) {
|
||||
LOG_ERROR(Service_FS, "Could not open metadata information for archive");
|
||||
// TODO(Subv): Verify error code
|
||||
return ResultNotFormatted;
|
||||
}
|
||||
|
||||
ArchiveFormatInfo info = {};
|
||||
file.ReadBytes(&info, sizeof(info));
|
||||
return info;
|
||||
}
|
||||
|
||||
ArchiveFormatInfo info = {};
|
||||
file.ReadBytes(&info, sizeof(info));
|
||||
return info;
|
||||
}
|
||||
|
||||
void ArchiveFactory_ExtSaveData::WriteIcon(const Path& path, std::span<const u8> icon) {
|
||||
std::string game_path = FileSys::GetExtSaveDataPath(GetMountPoint(), path);
|
||||
FileUtil::IOFile icon_file(game_path + "icon", "wb");
|
||||
icon_file.WriteBytes(icon.data(), icon.size());
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ExtSaveDataDelayGenerator)
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/file_sys/artic_cache.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "network/artic_base/artic_base_client.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -22,7 +25,7 @@ enum class ExtSaveDataType {
|
||||
};
|
||||
|
||||
/// File system interface to the ExtSaveData archive
|
||||
class ArchiveFactory_ExtSaveData final : public ArchiveFactory {
|
||||
class ArchiveFactory_ExtSaveData final : public ArchiveFactory, public ArticCacheProvider {
|
||||
public:
|
||||
ArchiveFactory_ExtSaveData(const std::string& mount_point, ExtSaveDataType type_);
|
||||
|
||||
@@ -31,21 +34,34 @@ public:
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path, u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) override;
|
||||
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
||||
|
||||
bool IsSlow() override {
|
||||
return IsUsingArtic();
|
||||
}
|
||||
|
||||
const std::string& GetMountPoint() const {
|
||||
return mount_point;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the SMDH icon of the ExtSaveData to file
|
||||
* @param path Path of this ExtSaveData
|
||||
* @param icon_data Binary data of the icon
|
||||
* @param icon_size Size of the icon data
|
||||
*/
|
||||
void WriteIcon(const Path& path, std::span<const u8> icon);
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info, u64 program_id,
|
||||
u32 directory_buckets, u32 file_buckets) override {
|
||||
return UnimplementedFunction(ErrorModule::FS);
|
||||
};
|
||||
|
||||
Result FormatAsExtData(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
|
||||
u8 unknown, u64 program_id, u64 total_size, std::span<const u8> icon);
|
||||
|
||||
Result DeleteExtData(Service::FS::MediaType media_type, u8 unknown, u32 high, u32 low);
|
||||
|
||||
void RegisterArtic(std::shared_ptr<Network::ArticBase::Client>& client) {
|
||||
artic_client = client;
|
||||
}
|
||||
|
||||
bool IsUsingArtic() const {
|
||||
return artic_client.get() != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Type of ext save data archive being accessed.
|
||||
@@ -61,10 +77,13 @@ private:
|
||||
/// Returns a path with the correct SaveIdHigh value for Shared extdata paths.
|
||||
Path GetCorrectedPath(const Path& path);
|
||||
|
||||
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
|
||||
|
||||
ArchiveFactory_ExtSaveData() = default;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveFactory>(*this);
|
||||
ar& boost::serialization::base_object<ArticCacheProvider>(*this);
|
||||
ar& type;
|
||||
ar& mount_point;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "common/string_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/archive_artic.h"
|
||||
#include "core/file_sys/archive_ncch.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/ivfc_archive.h"
|
||||
@@ -69,8 +70,9 @@ Path MakeNCCHFilePath(NCCHFileOpenType open_type, u32 content_index, NCCHFilePat
|
||||
return FileSys::Path(std::move(file));
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> NCCHArchive::OpenFile(const Path& path,
|
||||
const Mode& mode) const {
|
||||
ResultVal<std::unique_ptr<FileBackend>> NCCHArchive::OpenFile(const Path& path, const Mode& mode,
|
||||
u32 attributes) {
|
||||
|
||||
if (path.GetType() != LowPathType::Binary) {
|
||||
LOG_ERROR(Service_FS, "Path need to be Binary");
|
||||
return ResultInvalidPath;
|
||||
@@ -207,14 +209,14 @@ Result NCCHArchive::DeleteDirectoryRecursively(const Path& path) const {
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
Result NCCHArchive::CreateFile(const Path& path, u64 size) const {
|
||||
Result NCCHArchive::CreateFile(const Path& path, u64 size, u32 attributes) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to create a file in an NCCH archive ({}).", GetName());
|
||||
// TODO: Verify error code
|
||||
return Result(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported,
|
||||
ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
Result NCCHArchive::CreateDirectory(const Path& path) const {
|
||||
Result NCCHArchive::CreateDirectory(const Path& path, u32 attributes) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an NCCH archive ({}).", GetName());
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultUnknown;
|
||||
@@ -226,7 +228,7 @@ Result NCCHArchive::RenameDirectory(const Path& src_path, const Path& dest_path)
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> NCCHArchive::OpenDirectory(const Path& path) const {
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> NCCHArchive::OpenDirectory(const Path& path) {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to open a directory within an NCCH archive ({}).",
|
||||
GetName().c_str());
|
||||
// TODO(shinyquagsire23): Use correct error code
|
||||
@@ -255,7 +257,7 @@ ResultVal<std::size_t> NCCHFile::Read(const u64 offset, const std::size_t length
|
||||
}
|
||||
|
||||
ResultVal<std::size_t> NCCHFile::Write(const u64 offset, const std::size_t length, const bool flush,
|
||||
const u8* buffer) {
|
||||
const bool update_timestamp, const u8* buffer) {
|
||||
LOG_ERROR(Service_FS, "Attempted to write to NCCH file");
|
||||
// TODO(shinyquagsire23): Find error code
|
||||
return 0ULL;
|
||||
@@ -274,6 +276,13 @@ ArchiveFactory_NCCH::ArchiveFactory_NCCH() {}
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path& path,
|
||||
u64 program_id) {
|
||||
|
||||
if (IsUsingArtic()) {
|
||||
EnsureCacheCreated();
|
||||
return ArticArchive::Open(artic_client, Service::FS::ArchiveIdCode::NCCH, path,
|
||||
Core::PerfStats::PerfArticEventBits::NONE, *this, false);
|
||||
}
|
||||
|
||||
if (path.GetType() != LowPathType::Binary) {
|
||||
LOG_ERROR(Service_FS, "Path need to be Binary");
|
||||
return ResultInvalidPath;
|
||||
@@ -293,7 +302,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path&
|
||||
}
|
||||
|
||||
Result ArchiveFactory_NCCH::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) {
|
||||
u64 program_id, u32 directory_buckets, u32 file_buckets) {
|
||||
LOG_ERROR(Service_FS, "Attempted to format a NCCH archive.");
|
||||
// TODO: Verify error code
|
||||
return Result(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported,
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/file_sys/artic_cache.h"
|
||||
#include "core/file_sys/file_backend.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "network/artic_base/artic_base_client.h"
|
||||
|
||||
namespace Service::FS {
|
||||
enum class MediaType : u32;
|
||||
@@ -48,16 +50,16 @@ public:
|
||||
return "NCCHArchive";
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
|
||||
const Mode& mode) const override;
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode& mode,
|
||||
u32 attributes) override;
|
||||
Result DeleteFile(const Path& path) const override;
|
||||
Result RenameFile(const Path& src_path, const Path& dest_path) const override;
|
||||
Result DeleteDirectory(const Path& path) const override;
|
||||
Result DeleteDirectoryRecursively(const Path& path) const override;
|
||||
Result CreateFile(const Path& path, u64 size) const override;
|
||||
Result CreateDirectory(const Path& path) const override;
|
||||
Result CreateFile(const Path& path, u64 size, u32 attributes) const override;
|
||||
Result CreateDirectory(const Path& path, u32 attributes) const override;
|
||||
Result RenameDirectory(const Path& src_path, const Path& dest_path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) override;
|
||||
u64 GetFreeBytes() const override;
|
||||
|
||||
protected:
|
||||
@@ -82,11 +84,11 @@ public:
|
||||
explicit NCCHFile(std::vector<u8> buffer, std::unique_ptr<DelayGenerator> delay_generator_);
|
||||
|
||||
ResultVal<std::size_t> Read(u64 offset, std::size_t length, u8* buffer) const override;
|
||||
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush,
|
||||
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush, bool update_timestamp,
|
||||
const u8* buffer) override;
|
||||
u64 GetSize() const override;
|
||||
bool SetSize(u64 size) const override;
|
||||
bool Close() const override {
|
||||
bool Close() override {
|
||||
return false;
|
||||
}
|
||||
void Flush() const override {}
|
||||
@@ -105,7 +107,7 @@ private:
|
||||
};
|
||||
|
||||
/// File system interface to the NCCH archive
|
||||
class ArchiveFactory_NCCH final : public ArchiveFactory {
|
||||
class ArchiveFactory_NCCH final : public ArchiveFactory, public ArticCacheProvider {
|
||||
public:
|
||||
explicit ArchiveFactory_NCCH();
|
||||
|
||||
@@ -114,14 +116,29 @@ public:
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path, u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info, u64 program_id,
|
||||
u32 directory_buckets, u32 file_buckets) override;
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
||||
|
||||
bool IsSlow() override {
|
||||
return IsUsingArtic();
|
||||
}
|
||||
|
||||
void RegisterArtic(std::shared_ptr<Network::ArticBase::Client>& client) {
|
||||
artic_client = client;
|
||||
}
|
||||
|
||||
bool IsUsingArtic() const {
|
||||
return artic_client.get() != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveFactory>(*this);
|
||||
ar& boost::serialization::base_object<ArticCacheProvider>(*this);
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
@@ -75,12 +75,14 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_OtherSaveDataPermitted
|
||||
return ResultGamecardNotInserted;
|
||||
}
|
||||
|
||||
return sd_savedata_source->Open(program_id);
|
||||
return sd_savedata_source->Open(Service::FS::ArchiveIdCode::OtherSaveDataPermitted, path,
|
||||
program_id);
|
||||
}
|
||||
|
||||
Result ArchiveFactory_OtherSaveDataPermitted::Format(const Path& path,
|
||||
const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) {
|
||||
u64 program_id, u32 directory_buckets,
|
||||
u32 file_buckets) {
|
||||
LOG_ERROR(Service_FS, "Attempted to format a OtherSaveDataPermitted archive.");
|
||||
return ResultInvalidPath;
|
||||
}
|
||||
@@ -96,7 +98,8 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_OtherSaveDataPermitted::GetFormatInf
|
||||
return ResultGamecardNotInserted;
|
||||
}
|
||||
|
||||
return sd_savedata_source->GetFormatInfo(program_id);
|
||||
return sd_savedata_source->GetFormatInfo(
|
||||
program_id, Service::FS::ArchiveIdCode::OtherSaveDataPermitted, path);
|
||||
}
|
||||
|
||||
ArchiveFactory_OtherSaveDataGeneral::ArchiveFactory_OtherSaveDataGeneral(
|
||||
@@ -114,12 +117,14 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_OtherSaveDataGeneral::
|
||||
return ResultGamecardNotInserted;
|
||||
}
|
||||
|
||||
return sd_savedata_source->Open(program_id);
|
||||
return sd_savedata_source->Open(Service::FS::ArchiveIdCode::OtherSaveDataGeneral, path,
|
||||
program_id);
|
||||
}
|
||||
|
||||
Result ArchiveFactory_OtherSaveDataGeneral::Format(const Path& path,
|
||||
const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 /*client_program_id*/) {
|
||||
u64 /*client_program_id*/, u32 directory_buckets,
|
||||
u32 file_buckets) {
|
||||
MediaType media_type;
|
||||
u64 program_id;
|
||||
CASCADE_RESULT(std::tie(media_type, program_id), ParsePathGeneral(path));
|
||||
@@ -129,7 +134,9 @@ Result ArchiveFactory_OtherSaveDataGeneral::Format(const Path& path,
|
||||
return ResultGamecardNotInserted;
|
||||
}
|
||||
|
||||
return sd_savedata_source->Format(program_id, format_info);
|
||||
return sd_savedata_source->Format(program_id, format_info,
|
||||
Service::FS::ArchiveIdCode::OtherSaveDataPermitted, path,
|
||||
directory_buckets, file_buckets);
|
||||
}
|
||||
|
||||
ResultVal<ArchiveFormatInfo> ArchiveFactory_OtherSaveDataGeneral::GetFormatInfo(
|
||||
@@ -143,7 +150,8 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_OtherSaveDataGeneral::GetFormatInfo(
|
||||
return ResultGamecardNotInserted;
|
||||
}
|
||||
|
||||
return sd_savedata_source->GetFormatInfo(program_id);
|
||||
return sd_savedata_source->GetFormatInfo(
|
||||
program_id, Service::FS::ArchiveIdCode::OtherSaveDataPermitted, path);
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -22,10 +22,14 @@ public:
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path, u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info, u64 program_id,
|
||||
u32 directory_buckets, u32 file_buckets) override;
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
||||
|
||||
bool IsSlow() override {
|
||||
return sd_savedata_source->IsUsingArtic();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
|
||||
|
||||
@@ -49,8 +53,8 @@ public:
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path, u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info, u64 program_id,
|
||||
u32 directory_buckets, u32 file_buckets) override;
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -18,18 +18,20 @@ ArchiveFactory_SaveData::ArchiveFactory_SaveData(
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path,
|
||||
u64 program_id) {
|
||||
return sd_savedata_source->Open(program_id);
|
||||
return sd_savedata_source->Open(Service::FS::ArchiveIdCode::SaveData, path, program_id);
|
||||
}
|
||||
|
||||
Result ArchiveFactory_SaveData::Format(const Path& path,
|
||||
const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) {
|
||||
return sd_savedata_source->Format(program_id, format_info);
|
||||
u64 program_id, u32 directory_buckets, u32 file_buckets) {
|
||||
return sd_savedata_source->Format(program_id, format_info, Service::FS::ArchiveIdCode::SaveData,
|
||||
path, directory_buckets, file_buckets);
|
||||
}
|
||||
|
||||
ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveData::GetFormatInfo(const Path& path,
|
||||
u64 program_id) const {
|
||||
return sd_savedata_source->GetFormatInfo(program_id);
|
||||
return sd_savedata_source->GetFormatInfo(program_id, Service::FS::ArchiveIdCode::SaveData,
|
||||
path);
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -20,11 +20,15 @@ public:
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path, u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info, u64 program_id,
|
||||
u32 directory_buckets, u32 file_buckets) override;
|
||||
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
||||
|
||||
bool IsSlow() override {
|
||||
return sd_savedata_source->IsUsingArtic();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ public:
|
||||
SERIALIZE_DELAY_GENERATOR
|
||||
};
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFile(const Path& path,
|
||||
const Mode& mode) const {
|
||||
ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFile(const Path& path, const Mode& mode,
|
||||
u32 attributes) {
|
||||
Mode modified_mode;
|
||||
modified_mode.hex = mode.hex;
|
||||
|
||||
@@ -222,7 +222,7 @@ Result SDMCArchive::DeleteDirectoryRecursively(const Path& path) const {
|
||||
path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
|
||||
}
|
||||
|
||||
Result SDMCArchive::CreateFile(const FileSys::Path& path, u64 size) const {
|
||||
Result SDMCArchive::CreateFile(const FileSys::Path& path, u64 size, u32 attributes) const {
|
||||
const PathParser path_parser(path);
|
||||
|
||||
if (!path_parser.IsValid()) {
|
||||
@@ -267,7 +267,7 @@ Result SDMCArchive::CreateFile(const FileSys::Path& path, u64 size) const {
|
||||
ErrorLevel::Info);
|
||||
}
|
||||
|
||||
Result SDMCArchive::CreateDirectory(const Path& path) const {
|
||||
Result SDMCArchive::CreateDirectory(const Path& path, u32 attributes) const {
|
||||
const PathParser path_parser(path);
|
||||
|
||||
if (!path_parser.IsValid()) {
|
||||
@@ -331,7 +331,7 @@ Result SDMCArchive::RenameDirectory(const Path& src_path, const Path& dest_path)
|
||||
ErrorSummary::NothingHappened, ErrorLevel::Status);
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> SDMCArchive::OpenDirectory(const Path& path) const {
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> SDMCArchive::OpenDirectory(const Path& path) {
|
||||
const PathParser path_parser(path);
|
||||
|
||||
if (!path_parser.IsValid()) {
|
||||
@@ -392,7 +392,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path&
|
||||
}
|
||||
|
||||
Result ArchiveFactory_SDMC::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) {
|
||||
u64 program_id, u32 directory_buckets, u32 file_buckets) {
|
||||
// This is kind of an undesirable operation, so let's just ignore it. :)
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@@ -27,16 +27,16 @@ public:
|
||||
return "SDMCArchive: " + mount_point;
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
|
||||
const Mode& mode) const override;
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode& mode,
|
||||
u32 attributes) override;
|
||||
Result DeleteFile(const Path& path) const override;
|
||||
Result RenameFile(const Path& src_path, const Path& dest_path) const override;
|
||||
Result DeleteDirectory(const Path& path) const override;
|
||||
Result DeleteDirectoryRecursively(const Path& path) const override;
|
||||
Result CreateFile(const Path& path, u64 size) const override;
|
||||
Result CreateDirectory(const Path& path) const override;
|
||||
Result CreateFile(const Path& path, u64 size, u32 attributes) const override;
|
||||
Result CreateDirectory(const Path& path, u32 attributes) const override;
|
||||
Result RenameDirectory(const Path& src_path, const Path& dest_path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) override;
|
||||
u64 GetFreeBytes() const override;
|
||||
|
||||
protected:
|
||||
@@ -68,8 +68,8 @@ public:
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path, u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info, u64 program_id,
|
||||
u32 directory_buckets, u32 file_buckets) override;
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -41,7 +41,8 @@ public:
|
||||
};
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> SDMCWriteOnlyArchive::OpenFile(const Path& path,
|
||||
const Mode& mode) const {
|
||||
const Mode& mode,
|
||||
u32 attributes) {
|
||||
if (mode.read_flag) {
|
||||
LOG_ERROR(Service_FS, "Read flag is not supported");
|
||||
return ResultInvalidReadFlag;
|
||||
@@ -49,8 +50,7 @@ ResultVal<std::unique_ptr<FileBackend>> SDMCWriteOnlyArchive::OpenFile(const Pat
|
||||
return SDMCArchive::OpenFileBase(path, mode);
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> SDMCWriteOnlyArchive::OpenDirectory(
|
||||
const Path& path) const {
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> SDMCWriteOnlyArchive::OpenDirectory(const Path& path) {
|
||||
LOG_ERROR(Service_FS, "Not supported");
|
||||
return ResultUnsupportedOpenFlags;
|
||||
}
|
||||
@@ -83,7 +83,8 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMCWriteOnly::Open(co
|
||||
|
||||
Result ArchiveFactory_SDMCWriteOnly::Format(const Path& path,
|
||||
const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) {
|
||||
u64 program_id, u32 directory_buckets,
|
||||
u32 file_buckets) {
|
||||
// TODO(wwylele): hwtest this
|
||||
LOG_ERROR(Service_FS, "Attempted to format a SDMC write-only archive.");
|
||||
return ResultUnknown;
|
||||
|
||||
@@ -24,10 +24,10 @@ public:
|
||||
return "SDMCWriteOnlyArchive: " + mount_point;
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
|
||||
const Mode& mode) const override;
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode& mode,
|
||||
u32 attributes) override;
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) override;
|
||||
|
||||
private:
|
||||
SDMCWriteOnlyArchive() = default;
|
||||
@@ -54,8 +54,8 @@ public:
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path, u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info, u64 program_id,
|
||||
u32 directory_buckets, u32 file_buckets) override;
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
return data->size();
|
||||
}
|
||||
|
||||
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush,
|
||||
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush, bool update_timestamp,
|
||||
const u8* buffer) override {
|
||||
LOG_ERROR(Service_FS, "The file is read-only!");
|
||||
return ResultUnsupportedOpenFlags;
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Close() const override {
|
||||
bool Close() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,8 @@ public:
|
||||
return "SelfNCCHArchive";
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode&) const override {
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode&,
|
||||
u32 attributes) override {
|
||||
// Note: SelfNCCHArchive doesn't check the open mode.
|
||||
|
||||
if (path.GetType() != LowPathType::Binary) {
|
||||
@@ -154,12 +155,12 @@ public:
|
||||
return ResultUnsupportedOpenFlags;
|
||||
}
|
||||
|
||||
Result CreateFile(const Path& path, u64 size) const override {
|
||||
Result CreateFile(const Path& path, u64 size, u32 attributes) const override {
|
||||
LOG_ERROR(Service_FS, "Unsupported");
|
||||
return ResultUnsupportedOpenFlags;
|
||||
}
|
||||
|
||||
Result CreateDirectory(const Path& path) const override {
|
||||
Result CreateDirectory(const Path& path, u32 attributes) const override {
|
||||
LOG_ERROR(Service_FS, "Unsupported");
|
||||
return ResultUnsupportedOpenFlags;
|
||||
}
|
||||
@@ -169,7 +170,7 @@ public:
|
||||
return ResultUnsupportedOpenFlags;
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override {
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) override {
|
||||
LOG_ERROR(Service_FS, "Unsupported");
|
||||
return ResultUnsupportedOpenFlags;
|
||||
}
|
||||
@@ -297,7 +298,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const P
|
||||
}
|
||||
|
||||
Result ArchiveFactory_SelfNCCH::Format(const Path&, const FileSys::ArchiveFormatInfo&,
|
||||
u64 program_id) {
|
||||
u64 program_id, u32 directory_buckets, u32 file_buckets) {
|
||||
LOG_ERROR(Service_FS, "Attempted to format a SelfNCCH archive.");
|
||||
return ResultInvalidPath;
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ public:
|
||||
return "SelfNCCH";
|
||||
}
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path, u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info, u64 program_id,
|
||||
u32 directory_buckets, u32 file_buckets) override;
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/archives.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/archive_artic.h"
|
||||
#include "core/file_sys/archive_source_sd_savedata.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/savedata_archive.h"
|
||||
@@ -40,49 +41,101 @@ ArchiveSource_SDSaveData::ArchiveSource_SDSaveData(const std::string& sdmc_direc
|
||||
LOG_DEBUG(Service_FS, "Directory {} set as SaveData.", mount_point);
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveSource_SDSaveData::Open(u64 program_id) {
|
||||
std::string concrete_mount_point = GetSaveDataPath(mount_point, program_id);
|
||||
if (!FileUtil::Exists(concrete_mount_point)) {
|
||||
// When a SaveData archive is created for the first time, it is not yet formatted and the
|
||||
// save file/directory structure expected by the game has not yet been initialized.
|
||||
// Returning the NotFormatted error code will signal the game to provision the SaveData
|
||||
// archive with the files and folders that it expects.
|
||||
return ResultNotFormatted;
|
||||
}
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveSource_SDSaveData::Open(
|
||||
Service::FS::ArchiveIdCode archive_id, const Path& path, u64 program_id) {
|
||||
if (IsUsingArtic()) {
|
||||
EnsureCacheCreated();
|
||||
return ArticArchive::Open(artic_client, archive_id, path,
|
||||
Core::PerfStats::PerfArticEventBits::ARTIC_SAVE_DATA, *this,
|
||||
archive_id != Service::FS::ArchiveIdCode::SaveData);
|
||||
} else {
|
||||
std::string concrete_mount_point = GetSaveDataPath(mount_point, program_id);
|
||||
if (!FileUtil::Exists(concrete_mount_point)) {
|
||||
// When a SaveData archive is created for the first time, it is not yet formatted and
|
||||
// the save file/directory structure expected by the game has not yet been initialized.
|
||||
// Returning the NotFormatted error code will signal the game to provision the SaveData
|
||||
// archive with the files and folders that it expects.
|
||||
return ResultNotFormatted;
|
||||
}
|
||||
|
||||
return std::make_unique<SaveDataArchive>(std::move(concrete_mount_point));
|
||||
return std::make_unique<SaveDataArchive>(std::move(concrete_mount_point));
|
||||
}
|
||||
}
|
||||
|
||||
Result ArchiveSource_SDSaveData::Format(u64 program_id,
|
||||
const FileSys::ArchiveFormatInfo& format_info) {
|
||||
std::string concrete_mount_point = GetSaveDataPath(mount_point, program_id);
|
||||
FileUtil::DeleteDirRecursively(concrete_mount_point);
|
||||
FileUtil::CreateFullPath(concrete_mount_point);
|
||||
const FileSys::ArchiveFormatInfo& format_info,
|
||||
Service::FS::ArchiveIdCode archive_id, const Path& path,
|
||||
u32 directory_buckets, u32 file_buckets) {
|
||||
if (IsUsingArtic()) {
|
||||
ClearAllCache();
|
||||
auto req = artic_client->NewRequest("FSUSER_FormatSaveData");
|
||||
|
||||
// Write the format metadata
|
||||
std::string metadata_path = GetSaveDataMetadataPath(mount_point, program_id);
|
||||
FileUtil::IOFile file(metadata_path, "wb");
|
||||
req.AddParameterS32(static_cast<u32>(archive_id));
|
||||
auto artic_path = ArticArchive::BuildFSPath(path);
|
||||
req.AddParameterBuffer(artic_path.data(), artic_path.size());
|
||||
req.AddParameterU32(format_info.total_size / 512);
|
||||
req.AddParameterU32(format_info.number_directories);
|
||||
req.AddParameterU32(format_info.number_files);
|
||||
req.AddParameterU32(directory_buckets);
|
||||
req.AddParameterU32(file_buckets);
|
||||
req.AddParameterU8(format_info.duplicate_data);
|
||||
|
||||
if (file.IsOpen()) {
|
||||
file.WriteBytes(&format_info, sizeof(format_info));
|
||||
auto resp = artic_client->Send(req);
|
||||
return ArticArchive::RespResult(resp);
|
||||
} else {
|
||||
std::string concrete_mount_point = GetSaveDataPath(mount_point, program_id);
|
||||
FileUtil::DeleteDirRecursively(concrete_mount_point);
|
||||
FileUtil::CreateFullPath(concrete_mount_point);
|
||||
|
||||
// Write the format metadata
|
||||
std::string metadata_path = GetSaveDataMetadataPath(mount_point, program_id);
|
||||
FileUtil::IOFile file(metadata_path, "wb");
|
||||
|
||||
if (file.IsOpen()) {
|
||||
file.WriteBytes(&format_info, sizeof(format_info));
|
||||
return ResultSuccess;
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultVal<ArchiveFormatInfo> ArchiveSource_SDSaveData::GetFormatInfo(u64 program_id) const {
|
||||
std::string metadata_path = GetSaveDataMetadataPath(mount_point, program_id);
|
||||
FileUtil::IOFile file(metadata_path, "rb");
|
||||
ResultVal<ArchiveFormatInfo> ArchiveSource_SDSaveData::GetFormatInfo(
|
||||
u64 program_id, Service::FS::ArchiveIdCode archive_id, const Path& path) const {
|
||||
if (IsUsingArtic()) {
|
||||
auto req = artic_client->NewRequest("FSUSER_GetFormatInfo");
|
||||
|
||||
if (!file.IsOpen()) {
|
||||
LOG_ERROR(Service_FS, "Could not open metadata information for archive");
|
||||
// TODO(Subv): Verify error code
|
||||
return ResultNotFormatted;
|
||||
req.AddParameterS32(static_cast<u32>(archive_id));
|
||||
auto path_artic = ArticArchive::BuildFSPath(path);
|
||||
req.AddParameterBuffer(path_artic.data(), path_artic.size());
|
||||
|
||||
auto resp = artic_client->Send(req);
|
||||
Result res = ArticArchive::RespResult(resp);
|
||||
if (R_FAILED(res)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
auto info_buf = resp->GetResponseBuffer(0);
|
||||
if (!info_buf.has_value() || info_buf->second != sizeof(ArchiveFormatInfo)) {
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
ArchiveFormatInfo info;
|
||||
memcpy(&info, info_buf->first, sizeof(info));
|
||||
return info;
|
||||
} else {
|
||||
std::string metadata_path = GetSaveDataMetadataPath(mount_point, program_id);
|
||||
FileUtil::IOFile file(metadata_path, "rb");
|
||||
|
||||
if (!file.IsOpen()) {
|
||||
LOG_ERROR(Service_FS, "Could not open metadata information for archive");
|
||||
// TODO(Subv): Verify error code
|
||||
return ResultNotFormatted;
|
||||
}
|
||||
|
||||
ArchiveFormatInfo info = {};
|
||||
file.ReadBytes(&info, sizeof(info));
|
||||
return info;
|
||||
}
|
||||
|
||||
ArchiveFormatInfo info = {};
|
||||
file.ReadBytes(&info, sizeof(info));
|
||||
return info;
|
||||
}
|
||||
|
||||
std::string ArchiveSource_SDSaveData::GetSaveDataPathFor(const std::string& mount_point,
|
||||
|
||||
@@ -9,27 +9,48 @@
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/file_sys/artic_cache.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "network/artic_base/artic_base_client.h"
|
||||
|
||||
namespace Service::FS {
|
||||
enum class ArchiveIdCode : u32;
|
||||
} // namespace Service::FS
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
/// A common source of SD save data archive
|
||||
class ArchiveSource_SDSaveData {
|
||||
class ArchiveSource_SDSaveData : public ArticCacheProvider {
|
||||
public:
|
||||
explicit ArchiveSource_SDSaveData(const std::string& mount_point);
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> Open(u64 program_id);
|
||||
Result Format(u64 program_id, const FileSys::ArchiveFormatInfo& format_info);
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(u64 program_id) const;
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> Open(Service::FS::ArchiveIdCode archive_id,
|
||||
const Path& path, u64 program_id);
|
||||
Result Format(u64 program_id, const FileSys::ArchiveFormatInfo& format_info,
|
||||
Service::FS::ArchiveIdCode archive_id, const Path& path, u32 directory_buckets,
|
||||
u32 file_buckets);
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(u64 program_id,
|
||||
Service::FS::ArchiveIdCode archive_id,
|
||||
const Path& path) const;
|
||||
|
||||
static std::string GetSaveDataPathFor(const std::string& mount_point, u64 program_id);
|
||||
|
||||
void RegisterArtic(std::shared_ptr<Network::ArticBase::Client>& client) {
|
||||
artic_client = client;
|
||||
}
|
||||
|
||||
bool IsUsingArtic() const {
|
||||
return artic_client.get() != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string mount_point;
|
||||
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
|
||||
|
||||
ArchiveSource_SDSaveData() = default;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArticCacheProvider>(*this);
|
||||
ar& mount_point;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
|
||||
@@ -64,7 +64,8 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c
|
||||
|
||||
Result ArchiveFactory_SystemSaveData::Format(const Path& path,
|
||||
const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) {
|
||||
u64 program_id, u32 directory_buckets,
|
||||
u32 file_buckets) {
|
||||
std::string fullpath = GetSystemSaveDataPath(base_path, path);
|
||||
FileUtil::DeleteDirRecursively(fullpath);
|
||||
FileUtil::CreateFullPath(fullpath);
|
||||
|
||||
@@ -20,8 +20,8 @@ public:
|
||||
explicit ArchiveFactory_SystemSaveData(const std::string& mount_point);
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path, u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) override;
|
||||
Result Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info, u64 program_id,
|
||||
u32 directory_buckets, u32 file_buckets) override;
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
||||
|
||||
std::string GetName() const override {
|
||||
|
||||
235
src/core/file_sys/artic_cache.cpp
Normal file
235
src/core/file_sys/artic_cache.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
// Copyright 2024 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "artic_cache.h"
|
||||
|
||||
namespace FileSys {
|
||||
ResultVal<std::size_t> ArticCache::Read(s32 file_handle, std::size_t offset, std::size_t length,
|
||||
u8* buffer) {
|
||||
if (length == 0)
|
||||
return size_t();
|
||||
|
||||
const auto segments = BreakupRead(offset, length);
|
||||
std::size_t read_progress = 0;
|
||||
|
||||
// Skip cache if the read is too big
|
||||
if (segments.size() == 1 && segments[0].second > cache_line_size) {
|
||||
if (segments[0].second < big_cache_skip) {
|
||||
std::unique_lock big_read_guard(big_cache_mutex);
|
||||
auto big_cache_entry = big_cache.request(std::make_pair(offset, length));
|
||||
if (!big_cache_entry.first) {
|
||||
LOG_TRACE(Service_FS, "ArticCache BMISS: offset={}, length={}", offset, length);
|
||||
big_cache_entry.second.clear();
|
||||
big_cache_entry.second.resize(length);
|
||||
auto res =
|
||||
ReadFromArtic(file_handle, reinterpret_cast<u8*>(big_cache_entry.second.data()),
|
||||
length, offset);
|
||||
if (res.Failed())
|
||||
return res;
|
||||
length = res.Unwrap();
|
||||
} else {
|
||||
LOG_TRACE(Service_FS, "ArticCache BHIT: offset={}, length={}", offset, length);
|
||||
}
|
||||
memcpy(buffer, big_cache_entry.second.data(), length);
|
||||
} else {
|
||||
if (segments[0].second < very_big_cache_skip) {
|
||||
std::unique_lock very_big_read_guard(very_big_cache_mutex);
|
||||
auto very_big_cache_entry = very_big_cache.request(std::make_pair(offset, length));
|
||||
if (!very_big_cache_entry.first) {
|
||||
LOG_TRACE(Service_FS, "ArticCache VBMISS: offset={}, length={}", offset,
|
||||
length);
|
||||
very_big_cache_entry.second.clear();
|
||||
very_big_cache_entry.second.resize(length);
|
||||
auto res = ReadFromArtic(
|
||||
file_handle, reinterpret_cast<u8*>(very_big_cache_entry.second.data()),
|
||||
length, offset);
|
||||
if (res.Failed())
|
||||
return res;
|
||||
length = res.Unwrap();
|
||||
} else {
|
||||
LOG_TRACE(Service_FS, "ArticCache VBHIT: offset={}, length={}", offset, length);
|
||||
}
|
||||
memcpy(buffer, very_big_cache_entry.second.data(), length);
|
||||
} else {
|
||||
LOG_TRACE(Service_FS, "ArticCache SKIP: offset={}, length={}", offset, length);
|
||||
|
||||
auto res = ReadFromArtic(file_handle, buffer, length, offset);
|
||||
if (res.Failed())
|
||||
return res;
|
||||
length = res.Unwrap();
|
||||
}
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
// TODO(PabloMK7): Make cache thread safe, read the comment in CacheReady function.
|
||||
std::unique_lock read_guard(cache_mutex);
|
||||
for (const auto& seg : segments) {
|
||||
std::size_t read_size = cache_line_size;
|
||||
std::size_t page = OffsetToPage(seg.first);
|
||||
// Check if segment is in cache
|
||||
auto cache_entry = cache.request(page);
|
||||
if (!cache_entry.first) {
|
||||
// If not found, read from artic and cache the data
|
||||
auto res = ReadFromArtic(file_handle, cache_entry.second.data(), read_size, page);
|
||||
if (res.Failed())
|
||||
return res;
|
||||
read_size = res.Unwrap();
|
||||
LOG_TRACE(Service_FS, "ArticCache MISS: page={}, length={}, into={}", page, seg.second,
|
||||
(seg.first - page));
|
||||
} else {
|
||||
LOG_TRACE(Service_FS, "ArticCache HIT: page={}, length={}, into={}", page, seg.second,
|
||||
(seg.first - page));
|
||||
}
|
||||
std::size_t copy_amount =
|
||||
(read_size > (seg.first - page))
|
||||
? std::min((seg.first - page) + seg.second, read_size) - (seg.first - page)
|
||||
: 0;
|
||||
std::memcpy(buffer + read_progress, cache_entry.second.data() + (seg.first - page),
|
||||
copy_amount);
|
||||
read_progress += copy_amount;
|
||||
}
|
||||
return read_progress;
|
||||
}
|
||||
|
||||
bool ArticCache::CacheReady(std::size_t file_offset, std::size_t length) {
|
||||
auto segments = BreakupRead(file_offset, length);
|
||||
if (segments.size() == 1 && segments[0].second > cache_line_size) {
|
||||
return false;
|
||||
} else {
|
||||
std::shared_lock read_guard(cache_mutex);
|
||||
for (auto it = segments.begin(); it != segments.end(); it++) {
|
||||
if (!cache.contains(OffsetToPage(it->first)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void ArticCache::Clear() {
|
||||
std::unique_lock l1(cache_mutex), l2(big_cache_mutex), l3(very_big_cache_mutex);
|
||||
cache.clear();
|
||||
big_cache.clear();
|
||||
very_big_cache.clear();
|
||||
data_size = std::nullopt;
|
||||
}
|
||||
|
||||
ResultVal<size_t> ArticCache::Write(s32 file_handle, std::size_t offset, std::size_t length,
|
||||
const u8* buffer, u32 flags) {
|
||||
// Can probably do better, but write operations are usually done at the end, so it doesn't
|
||||
// matter much
|
||||
Clear();
|
||||
|
||||
size_t written_amount = 0;
|
||||
while (written_amount != length) {
|
||||
size_t to_write =
|
||||
std::min<size_t>(client->GetServerRequestMaxSize() - 0x100, length - written_amount);
|
||||
|
||||
auto req = client->NewRequest("FSFILE_Write");
|
||||
req.AddParameterS32(file_handle);
|
||||
req.AddParameterS64(static_cast<s64>(offset + written_amount));
|
||||
req.AddParameterS32(static_cast<s32>(to_write));
|
||||
req.AddParameterS32(static_cast<s32>(flags));
|
||||
req.AddParameterBuffer(buffer + written_amount, to_write);
|
||||
auto resp = client->Send(req);
|
||||
if (!resp.has_value() || !resp->Succeeded())
|
||||
return Result(-1);
|
||||
|
||||
auto res = Result(static_cast<u32>(resp->GetMethodResult()));
|
||||
if (res.IsError())
|
||||
return res;
|
||||
|
||||
auto actually_written_opt = resp->GetResponseS32(0);
|
||||
if (!actually_written_opt.has_value())
|
||||
return Result(-1);
|
||||
|
||||
size_t actually_written = static_cast<size_t>(actually_written_opt.value());
|
||||
|
||||
written_amount += actually_written;
|
||||
if (actually_written != to_write)
|
||||
break;
|
||||
}
|
||||
return written_amount;
|
||||
}
|
||||
|
||||
ResultVal<size_t> ArticCache::GetSize(s32 file_handle) {
|
||||
std::unique_lock l1(cache_mutex);
|
||||
|
||||
if (data_size.has_value())
|
||||
return data_size.value();
|
||||
|
||||
auto req = client->NewRequest("FSFILE_GetSize");
|
||||
|
||||
req.AddParameterS32(file_handle);
|
||||
|
||||
auto resp = client->Send(req);
|
||||
if (!resp.has_value() || !resp->Succeeded())
|
||||
return Result(-1);
|
||||
|
||||
auto res = Result(static_cast<u32>(resp->GetMethodResult()));
|
||||
if (res.IsError())
|
||||
return res;
|
||||
|
||||
auto size_buf = resp->GetResponseS64(0);
|
||||
if (!size_buf) {
|
||||
return Result(-1);
|
||||
}
|
||||
|
||||
data_size = static_cast<size_t>(*size_buf);
|
||||
return data_size.value();
|
||||
}
|
||||
|
||||
ResultVal<size_t> ArticCache::ReadFromArtic(s32 file_handle, u8* buffer, size_t len,
|
||||
size_t offset) {
|
||||
size_t read_amount = 0;
|
||||
while (read_amount != len) {
|
||||
size_t to_read =
|
||||
std::min<size_t>(client->GetServerRequestMaxSize() - 0x100, len - read_amount);
|
||||
|
||||
auto req = client->NewRequest("FSFILE_Read");
|
||||
req.AddParameterS32(file_handle);
|
||||
req.AddParameterS64(static_cast<s64>(offset + read_amount));
|
||||
req.AddParameterS32(static_cast<s32>(to_read));
|
||||
auto resp = client->Send(req);
|
||||
if (!resp.has_value() || !resp->Succeeded())
|
||||
return Result(-1);
|
||||
|
||||
auto res = Result(static_cast<u32>(resp->GetMethodResult()));
|
||||
if (res.IsError())
|
||||
return res;
|
||||
|
||||
auto read_buff = resp->GetResponseBuffer(0);
|
||||
if (!read_buff.has_value())
|
||||
return Result(-1);
|
||||
size_t actually_read = read_buff->second;
|
||||
|
||||
memcpy(buffer + read_amount, read_buff->first, actually_read);
|
||||
read_amount += actually_read;
|
||||
if (actually_read != to_read)
|
||||
break;
|
||||
}
|
||||
return read_amount;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::size_t, std::size_t>> ArticCache::BreakupRead(std::size_t offset,
|
||||
std::size_t length) {
|
||||
std::vector<std::pair<std::size_t, std::size_t>> ret;
|
||||
|
||||
// Reads bigger than the cache line size will probably never hit again
|
||||
if (length > max_breakup_size) {
|
||||
ret.push_back(std::make_pair(offset, length));
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::size_t curr_offset = offset;
|
||||
while (length) {
|
||||
std::size_t next_page = OffsetToPage(curr_offset + cache_line_size);
|
||||
std::size_t curr_page_len = std::min(length, next_page - curr_offset);
|
||||
ret.push_back(std::make_pair(curr_offset, curr_page_len));
|
||||
curr_offset = next_page;
|
||||
length -= curr_page_len;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
} // namespace FileSys
|
||||
154
src/core/file_sys/artic_cache.h
Normal file
154
src/core/file_sys/artic_cache.h
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2024 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <shared_mutex>
|
||||
#include "vector"
|
||||
|
||||
#include <boost/serialization/array.hpp>
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/static_lru_cache.h"
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "network/artic_base/artic_base_client.h"
|
||||
|
||||
namespace FileSys {
|
||||
class ArticCache {
|
||||
public:
|
||||
ArticCache() = default;
|
||||
|
||||
ArticCache(const std::shared_ptr<Network::ArticBase::Client>& cli) : client(cli) {}
|
||||
|
||||
ResultVal<std::size_t> Read(s32 file_handle, std::size_t offset, std::size_t length,
|
||||
u8* buffer);
|
||||
|
||||
bool CacheReady(std::size_t file_offset, std::size_t length);
|
||||
|
||||
void Clear();
|
||||
|
||||
ResultVal<std::size_t> Write(s32 file_handle, std::size_t offset, std::size_t length,
|
||||
const u8* buffer, u32 flags);
|
||||
|
||||
ResultVal<size_t> GetSize(s32 file_handle);
|
||||
|
||||
void ForceSetSize(const std::optional<size_t>& size) {
|
||||
data_size = size;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Network::ArticBase::Client> client;
|
||||
std::optional<size_t> data_size;
|
||||
|
||||
// Total cache size: 32MB small, 512MB big (worst case), 160MB very big (worst case).
|
||||
// The worst case values are unrealistic, they will never happen in any real game.
|
||||
static constexpr std::size_t cache_line_size = 4 * 1024;
|
||||
static constexpr std::size_t cache_line_count = 256;
|
||||
static constexpr std::size_t max_breakup_size = 8 * 1024;
|
||||
|
||||
static constexpr std::size_t big_cache_skip = 1 * 1024 * 1024;
|
||||
static constexpr std::size_t big_cache_lines = 1024;
|
||||
|
||||
static constexpr std::size_t very_big_cache_skip = 10 * 1024 * 1024;
|
||||
static constexpr std::size_t very_big_cache_lines = 24;
|
||||
|
||||
Common::StaticLRUCache<std::size_t, std::array<u8, cache_line_size>, cache_line_count> cache;
|
||||
std::shared_mutex cache_mutex;
|
||||
|
||||
struct NoInitChar {
|
||||
u8 value;
|
||||
NoInitChar() noexcept {
|
||||
// do nothing
|
||||
static_assert(sizeof *this == sizeof value, "invalid size");
|
||||
}
|
||||
};
|
||||
Common::StaticLRUCache<std::pair<std::size_t, std::size_t>, std::vector<NoInitChar>,
|
||||
big_cache_lines>
|
||||
big_cache;
|
||||
std::shared_mutex big_cache_mutex;
|
||||
Common::StaticLRUCache<std::pair<std::size_t, std::size_t>, std::vector<NoInitChar>,
|
||||
very_big_cache_lines>
|
||||
very_big_cache;
|
||||
std::shared_mutex very_big_cache_mutex;
|
||||
|
||||
ResultVal<std::size_t> ReadFromArtic(s32 file_handle, u8* buffer, size_t len, size_t offset);
|
||||
|
||||
std::size_t OffsetToPage(std::size_t offset) {
|
||||
return Common::AlignDown<std::size_t>(offset, cache_line_size);
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::size_t, std::size_t>> BreakupRead(std::size_t offset,
|
||||
std::size_t length);
|
||||
|
||||
protected:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class ArticCacheProvider {
|
||||
public:
|
||||
virtual ~ArticCacheProvider() {}
|
||||
|
||||
std::vector<u8> PathsToVector(const Path& archive_path, const Path& file_path) {
|
||||
auto archive_path_binary = archive_path.AsBinary();
|
||||
auto file_path_binary = file_path.AsBinary();
|
||||
|
||||
std::vector<u8> ret;
|
||||
ret.push_back(static_cast<u8>(file_path.GetType()));
|
||||
ret.insert(ret.end(), archive_path_binary.begin(), archive_path_binary.end());
|
||||
ret.push_back(static_cast<u8>(archive_path.GetType()));
|
||||
ret.insert(ret.end(), file_path_binary.begin(), file_path_binary.end());
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ArticCache> ProvideCache(
|
||||
const std::shared_ptr<Network::ArticBase::Client>& cli, const std::vector<u8>& path,
|
||||
bool create) {
|
||||
if (file_caches == nullptr)
|
||||
return nullptr;
|
||||
|
||||
auto it = file_caches->find(path);
|
||||
if (it == file_caches->end()) {
|
||||
if (!create) {
|
||||
return nullptr;
|
||||
}
|
||||
auto res = std::make_shared<ArticCache>(cli);
|
||||
file_caches->insert({path, res});
|
||||
return res;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
virtual void ClearAllCache() {
|
||||
if (file_caches != nullptr) {
|
||||
file_caches->clear();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void EnsureCacheCreated() {
|
||||
if (file_caches == nullptr) {
|
||||
file_caches =
|
||||
std::make_unique<std::map<std::vector<u8>, std::shared_ptr<ArticCache>>>();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {}
|
||||
friend class boost::serialization::access;
|
||||
|
||||
private:
|
||||
std::unique_ptr<std::map<std::vector<u8>, std::shared_ptr<ArticCache>>> file_caches = nullptr;
|
||||
std::shared_ptr<Network::ArticBase::Client> client;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArticCache)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArticCacheProvider)
|
||||
@@ -49,7 +49,11 @@ public:
|
||||
* Close the directory
|
||||
* @return true if the directory closed correctly
|
||||
*/
|
||||
virtual bool Close() const = 0;
|
||||
virtual bool Close() = 0;
|
||||
|
||||
virtual bool IsSlow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
|
||||
@@ -26,7 +26,7 @@ ResultVal<std::size_t> DiskFile::Read(const u64 offset, const std::size_t length
|
||||
}
|
||||
|
||||
ResultVal<std::size_t> DiskFile::Write(const u64 offset, const std::size_t length, const bool flush,
|
||||
const u8* buffer) {
|
||||
const bool update_timestamp, const u8* buffer) {
|
||||
if (!mode.write_flag)
|
||||
return ResultInvalidOpenFlags;
|
||||
|
||||
@@ -47,7 +47,7 @@ bool DiskFile::SetSize(const u64 size) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskFile::Close() const {
|
||||
bool DiskFile::Close() {
|
||||
return file->Close();
|
||||
}
|
||||
|
||||
|
||||
@@ -30,11 +30,11 @@ public:
|
||||
}
|
||||
|
||||
ResultVal<std::size_t> Read(u64 offset, std::size_t length, u8* buffer) const override;
|
||||
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush,
|
||||
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush, bool update_timestamp,
|
||||
const u8* buffer) override;
|
||||
u64 GetSize() const override;
|
||||
bool SetSize(u64 size) const override;
|
||||
bool Close() const override;
|
||||
bool Close() override;
|
||||
|
||||
void Flush() const override {
|
||||
file->Flush();
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
|
||||
u32 Read(u32 count, Entry* entries) override;
|
||||
|
||||
bool Close() const override {
|
||||
bool Close() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
* @return Number of bytes written, or error code
|
||||
*/
|
||||
virtual ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush,
|
||||
const u8* buffer) = 0;
|
||||
bool update_timestamp, const u8* buffer) = 0;
|
||||
|
||||
/**
|
||||
* Get the amount of time a 3ds needs to read those data
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
* Close the file
|
||||
* @return true if the file closed correctly
|
||||
*/
|
||||
virtual bool Close() const = 0;
|
||||
virtual bool Close() = 0;
|
||||
|
||||
/**
|
||||
* Flushes the file
|
||||
|
||||
@@ -28,8 +28,8 @@ std::string IVFCArchive::GetName() const {
|
||||
return "IVFC";
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path,
|
||||
const Mode& mode) const {
|
||||
ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, const Mode& mode,
|
||||
u32 attributes) {
|
||||
std::unique_ptr<DelayGenerator> delay_generator = std::make_unique<IVFCDelayGenerator>();
|
||||
return std::make_unique<IVFCFile>(romfs_file, std::move(delay_generator));
|
||||
}
|
||||
@@ -61,14 +61,14 @@ Result IVFCArchive::DeleteDirectoryRecursively(const Path& path) const {
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
Result IVFCArchive::CreateFile(const Path& path, u64 size) const {
|
||||
Result IVFCArchive::CreateFile(const Path& path, u64 size, u32 attributes) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to create a file in an IVFC archive ({}).", GetName());
|
||||
// TODO: Verify error code
|
||||
return Result(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported,
|
||||
ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
Result IVFCArchive::CreateDirectory(const Path& path) const {
|
||||
Result IVFCArchive::CreateDirectory(const Path& path, u32 attributes) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an IVFC archive ({}).", GetName());
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultUnknown;
|
||||
@@ -80,7 +80,7 @@ Result IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path)
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) const {
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) {
|
||||
return std::make_unique<IVFCDirectory>();
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ ResultVal<std::size_t> IVFCFile::Read(const u64 offset, const std::size_t length
|
||||
}
|
||||
|
||||
ResultVal<std::size_t> IVFCFile::Write(const u64 offset, const std::size_t length, const bool flush,
|
||||
const u8* buffer) {
|
||||
const bool update_timestamp, const u8* buffer) {
|
||||
LOG_ERROR(Service_FS, "Attempted to write to IVFC file");
|
||||
// TODO(Subv): Find error code
|
||||
return 0ULL;
|
||||
@@ -133,7 +133,8 @@ ResultVal<std::size_t> IVFCFileInMemory::Read(const u64 offset, const std::size_
|
||||
}
|
||||
|
||||
ResultVal<std::size_t> IVFCFileInMemory::Write(const u64 offset, const std::size_t length,
|
||||
const bool flush, const u8* buffer) {
|
||||
const bool flush, const bool update_timestamp,
|
||||
const u8* buffer) {
|
||||
LOG_ERROR(Service_FS, "Attempted to write to IVFC file");
|
||||
// TODO(Subv): Find error code
|
||||
return 0ULL;
|
||||
|
||||
@@ -101,16 +101,16 @@ public:
|
||||
|
||||
std::string GetName() const override;
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
|
||||
const Mode& mode) const override;
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode& mode,
|
||||
u32 attributes) override;
|
||||
Result DeleteFile(const Path& path) const override;
|
||||
Result RenameFile(const Path& src_path, const Path& dest_path) const override;
|
||||
Result DeleteDirectory(const Path& path) const override;
|
||||
Result DeleteDirectoryRecursively(const Path& path) const override;
|
||||
Result CreateFile(const Path& path, u64 size) const override;
|
||||
Result CreateDirectory(const Path& path) const override;
|
||||
Result CreateFile(const Path& path, u64 size, u32 attributes) const override;
|
||||
Result CreateDirectory(const Path& path, u32 attributes) const override;
|
||||
Result RenameDirectory(const Path& src_path, const Path& dest_path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) override;
|
||||
u64 GetFreeBytes() const override;
|
||||
|
||||
protected:
|
||||
@@ -122,11 +122,11 @@ public:
|
||||
IVFCFile(std::shared_ptr<RomFSReader> file, std::unique_ptr<DelayGenerator> delay_generator_);
|
||||
|
||||
ResultVal<std::size_t> Read(u64 offset, std::size_t length, u8* buffer) const override;
|
||||
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush,
|
||||
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush, bool update_timestamp,
|
||||
const u8* buffer) override;
|
||||
u64 GetSize() const override;
|
||||
bool SetSize(u64 size) const override;
|
||||
bool Close() const override {
|
||||
bool Close() override {
|
||||
return false;
|
||||
}
|
||||
void Flush() const override {}
|
||||
@@ -157,7 +157,7 @@ public:
|
||||
u32 Read(const u32 count, Entry* entries) override {
|
||||
return 0;
|
||||
}
|
||||
bool Close() const override {
|
||||
bool Close() override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -168,11 +168,11 @@ public:
|
||||
std::unique_ptr<DelayGenerator> delay_generator_);
|
||||
|
||||
ResultVal<std::size_t> Read(u64 offset, std::size_t length, u8* buffer) const override;
|
||||
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush,
|
||||
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush, bool update_timestamp,
|
||||
const u8* buffer) override;
|
||||
u64 GetSize() const override;
|
||||
bool SetSize(u64 size) const override;
|
||||
bool Close() const override {
|
||||
bool Close() override {
|
||||
return false;
|
||||
}
|
||||
void Flush() const override {}
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
#include <cryptopp/modes.h>
|
||||
#include "common/archives.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/archive_artic.h"
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/file_sys/romfs_reader.h"
|
||||
#include "core/hle/service/fs/fs_user.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::DirectRomFSReader)
|
||||
|
||||
@@ -109,4 +113,102 @@ std::vector<std::pair<std::size_t, std::size_t>> DirectRomFSReader::BreakupRead(
|
||||
return ret;
|
||||
}
|
||||
|
||||
ArticRomFSReader::ArticRomFSReader(std::shared_ptr<Network::ArticBase::Client>& cli,
|
||||
bool is_update_romfs)
|
||||
: client(cli), cache(cli) {
|
||||
auto req = client->NewRequest("FSUSER_OpenFileDirectly");
|
||||
|
||||
FileSys::Path archive(FileSys::LowPathType::Empty, {});
|
||||
std::vector<u8> fileVec(0xC);
|
||||
fileVec[0] = static_cast<u8>(is_update_romfs ? 5 : 0);
|
||||
FileSys::Path file(FileSys::LowPathType::Binary, fileVec);
|
||||
|
||||
req.AddParameterS32(static_cast<s32>(Service::FS::ArchiveIdCode::SelfNCCH));
|
||||
|
||||
auto archive_buf = ArticArchive::BuildFSPath(archive);
|
||||
req.AddParameterBuffer(archive_buf.data(), archive_buf.size());
|
||||
auto file_buf = ArticArchive::BuildFSPath(file);
|
||||
req.AddParameterBuffer(file_buf.data(), file_buf.size());
|
||||
|
||||
req.AddParameterS32(1);
|
||||
req.AddParameterS32(0);
|
||||
|
||||
auto resp = client->Send(req);
|
||||
|
||||
if (!resp.has_value() || !resp->Succeeded()) {
|
||||
load_status = Loader::ResultStatus::Error;
|
||||
return;
|
||||
}
|
||||
if (resp->GetMethodResult() != 0) {
|
||||
load_status = Loader::ResultStatus::ErrorNotUsed;
|
||||
return;
|
||||
}
|
||||
|
||||
auto handle_buf = resp->GetResponseBuffer(0);
|
||||
if (!handle_buf.has_value() || handle_buf->second != sizeof(s32)) {
|
||||
load_status = Loader::ResultStatus::Error;
|
||||
return;
|
||||
}
|
||||
|
||||
romfs_handle = *reinterpret_cast<s32*>(handle_buf->first);
|
||||
|
||||
req = client->NewRequest("FSFILE_GetSize");
|
||||
|
||||
req.AddParameterS32(romfs_handle);
|
||||
|
||||
resp = client->Send(req);
|
||||
|
||||
if (!resp.has_value() || !resp->Succeeded()) {
|
||||
load_status = Loader::ResultStatus::Error;
|
||||
return;
|
||||
}
|
||||
if (resp->GetMethodResult() != 0) {
|
||||
load_status = Loader::ResultStatus::ErrorNotUsed;
|
||||
return;
|
||||
}
|
||||
|
||||
auto size_buf = resp->GetResponseBuffer(0);
|
||||
if (!size_buf.has_value() || size_buf->second != sizeof(u64)) {
|
||||
load_status = Loader::ResultStatus::Error;
|
||||
return;
|
||||
}
|
||||
|
||||
data_size = static_cast<size_t>(*reinterpret_cast<u64*>(size_buf->first));
|
||||
load_status = Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
ArticRomFSReader::~ArticRomFSReader() {
|
||||
if (romfs_handle != -1) {
|
||||
auto req = client->NewRequest("FSFILE_Close");
|
||||
req.AddParameterS32(romfs_handle);
|
||||
client->Send(req);
|
||||
romfs_handle = -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t ArticRomFSReader::ReadFile(std::size_t offset, std::size_t length, u8* buffer) {
|
||||
length = std::min(length, static_cast<std::size_t>(data_size) - offset);
|
||||
auto res = cache.Read(romfs_handle, offset, length, buffer);
|
||||
if (res.Failed())
|
||||
return 0;
|
||||
return res.Unwrap();
|
||||
}
|
||||
|
||||
bool ArticRomFSReader::AllowsCachedReads() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArticRomFSReader::CacheReady(std::size_t file_offset, std::size_t length) {
|
||||
return cache.CacheReady(file_offset, length);
|
||||
}
|
||||
|
||||
void ArticRomFSReader::CloseFile() {
|
||||
if (romfs_handle != -1) {
|
||||
auto req = client->NewRequest("FSFILE_Close");
|
||||
req.AddParameterS32(romfs_handle);
|
||||
client->Send(req);
|
||||
romfs_handle = -1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/static_lru_cache.h"
|
||||
#include "core/file_sys/artic_cache.h"
|
||||
#include "network/artic_base/artic_base_client.h"
|
||||
|
||||
namespace Loader {
|
||||
enum class ResultStatus;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -97,6 +103,53 @@ private:
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
/**
|
||||
* A RomFS reader that reads from an artic base server.
|
||||
*/
|
||||
class ArticRomFSReader : public RomFSReader {
|
||||
public:
|
||||
ArticRomFSReader() = default;
|
||||
ArticRomFSReader(std::shared_ptr<Network::ArticBase::Client>& cli, bool is_update_romfs);
|
||||
|
||||
~ArticRomFSReader() override;
|
||||
|
||||
std::size_t GetSize() const override {
|
||||
return data_size;
|
||||
}
|
||||
|
||||
std::size_t ReadFile(std::size_t offset, std::size_t length, u8* buffer) override;
|
||||
|
||||
bool AllowsCachedReads() const override;
|
||||
|
||||
bool CacheReady(std::size_t file_offset, std::size_t length) override;
|
||||
|
||||
Loader::ResultStatus OpenStatus() {
|
||||
return load_status;
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
cache.Clear();
|
||||
}
|
||||
|
||||
void CloseFile();
|
||||
|
||||
private:
|
||||
std::shared_ptr<Network::ArticBase::Client> client;
|
||||
size_t data_size = 0;
|
||||
s32 romfs_handle = -1;
|
||||
Loader::ResultStatus load_status;
|
||||
|
||||
ArticCache cache;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<RomFSReader>(*this);
|
||||
ar& data_size;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::DirectRomFSReader)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArticRomFSReader)
|
||||
|
||||
@@ -36,7 +36,8 @@ public:
|
||||
};
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& path,
|
||||
const Mode& mode) const {
|
||||
const Mode& mode,
|
||||
u32 attributes) {
|
||||
LOG_DEBUG(Service_FS, "called path={} mode={:01X}", path.DebugStr(), mode.hex);
|
||||
|
||||
const PathParser path_parser(path);
|
||||
@@ -203,7 +204,7 @@ Result SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const {
|
||||
path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
|
||||
}
|
||||
|
||||
Result SaveDataArchive::CreateFile(const FileSys::Path& path, u64 size) const {
|
||||
Result SaveDataArchive::CreateFile(const FileSys::Path& path, u64 size, u32 attributes) const {
|
||||
const PathParser path_parser(path);
|
||||
|
||||
if (!path_parser.IsValid()) {
|
||||
@@ -253,7 +254,7 @@ Result SaveDataArchive::CreateFile(const FileSys::Path& path, u64 size) const {
|
||||
ErrorLevel::Info);
|
||||
}
|
||||
|
||||
Result SaveDataArchive::CreateDirectory(const Path& path) const {
|
||||
Result SaveDataArchive::CreateDirectory(const Path& path, u32 attributes) const {
|
||||
const PathParser path_parser(path);
|
||||
|
||||
if (!path_parser.IsValid()) {
|
||||
@@ -319,8 +320,7 @@ Result SaveDataArchive::RenameDirectory(const Path& src_path, const Path& dest_p
|
||||
ErrorSummary::NothingHappened, ErrorLevel::Status);
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> SaveDataArchive::OpenDirectory(
|
||||
const Path& path) const {
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> SaveDataArchive::OpenDirectory(const Path& path) {
|
||||
const PathParser path_parser(path);
|
||||
|
||||
if (!path_parser.IsValid()) {
|
||||
|
||||
@@ -22,16 +22,16 @@ public:
|
||||
return "SaveDataArchive: " + mount_point;
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
|
||||
const Mode& mode) const override;
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode& mode,
|
||||
u32 attributes) override;
|
||||
Result DeleteFile(const Path& path) const override;
|
||||
Result RenameFile(const Path& src_path, const Path& dest_path) const override;
|
||||
Result DeleteDirectory(const Path& path) const override;
|
||||
Result DeleteDirectoryRecursively(const Path& path) const override;
|
||||
Result CreateFile(const Path& path, u64 size) const override;
|
||||
Result CreateDirectory(const Path& path) const override;
|
||||
Result CreateFile(const Path& path, u64 size, u32 attributes) const override;
|
||||
Result CreateDirectory(const Path& path, u32 attributes) const override;
|
||||
Result RenameDirectory(const Path& src_path, const Path& dest_path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) override;
|
||||
u64 GetFreeBytes() const override;
|
||||
|
||||
protected:
|
||||
|
||||
74
src/core/file_sys/secure_value_backend.cpp
Normal file
74
src/core/file_sys/secure_value_backend.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2024 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/archives.h"
|
||||
#include "secure_value_backend.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::DefaultSecureValueBackend)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
Result DefaultSecureValueBackend::ObsoletedSetSaveDataSecureValue(u32 unique_id, u8 title_variation,
|
||||
u32 secure_value_slot,
|
||||
u64 secure_value) {
|
||||
|
||||
// TODO: Generate and Save the Secure Value
|
||||
|
||||
LOG_WARNING(Service_FS,
|
||||
"(STUBBED) called, value=0x{:016x} secure_value_slot=0x{:08X} "
|
||||
"unqiue_id=0x{:08X} title_variation=0x{:02X}",
|
||||
secure_value, secure_value_slot, unique_id, title_variation);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultVal<std::tuple<bool, u64>> DefaultSecureValueBackend::ObsoletedGetSaveDataSecureValue(
|
||||
u32 unique_id, u8 title_variation, u32 secure_value_slot) {
|
||||
|
||||
// TODO: Implement Secure Value Lookup & Generation
|
||||
|
||||
LOG_WARNING(Service_FS,
|
||||
"(STUBBED) called, secure_value_slot=0x{:08X} "
|
||||
"unqiue_id=0x{:08X} title_variation=0x{:02X}",
|
||||
secure_value_slot, unique_id, title_variation);
|
||||
|
||||
return std::make_tuple<bool, u64>(false, 0);
|
||||
}
|
||||
|
||||
Result DefaultSecureValueBackend::ControlSecureSave(u32 action, u8* input, size_t input_size,
|
||||
u8* output, size_t output_size) {
|
||||
|
||||
LOG_WARNING(Service_FS,
|
||||
"(STUBBED) called, action=0x{:08X} "
|
||||
"input_size=0x{:016X} output_size=0x{:016X}",
|
||||
action, input_size, output_size);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result DefaultSecureValueBackend::SetThisSaveDataSecureValue(u32 secure_value_slot,
|
||||
u64 secure_value) {
|
||||
// TODO: Generate and Save the Secure Value
|
||||
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called, secure_value=0x{:016x} secure_value_slot=0x{:08X}",
|
||||
secure_value, secure_value_slot);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultVal<std::tuple<bool, bool, u64>> DefaultSecureValueBackend::GetThisSaveDataSecureValue(
|
||||
u32 secure_value_slot) {
|
||||
|
||||
// TODO: Implement Secure Value Lookup & Generation
|
||||
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called secure_value_slot=0x{:08X}", secure_value_slot);
|
||||
|
||||
return std::make_tuple<bool, bool, u64>(false, true, 0);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void FileSys::DefaultSecureValueBackend::serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<SecureValueBackend>(*this);
|
||||
}
|
||||
} // namespace FileSys
|
||||
65
src/core/file_sys/secure_value_backend.h
Normal file
65
src/core/file_sys/secure_value_backend.h
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2024 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tuple"
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
|
||||
namespace FileSys {
|
||||
class SecureValueBackend : NonCopyable {
|
||||
public:
|
||||
virtual ~SecureValueBackend(){};
|
||||
|
||||
virtual Result ObsoletedSetSaveDataSecureValue(u32 unique_id, u8 title_variation,
|
||||
u32 secure_value_slot, u64 secure_value) = 0;
|
||||
|
||||
virtual ResultVal<std::tuple<bool, u64>> ObsoletedGetSaveDataSecureValue(
|
||||
u32 unique_id, u8 title_variation, u32 secure_value_slot) = 0;
|
||||
|
||||
virtual Result ControlSecureSave(u32 action, u8* input, size_t input_size, u8* output,
|
||||
size_t output_size) = 0;
|
||||
|
||||
virtual Result SetThisSaveDataSecureValue(u32 secure_value_slot, u64 secure_value) = 0;
|
||||
|
||||
virtual ResultVal<std::tuple<bool, bool, u64>> GetThisSaveDataSecureValue(
|
||||
u32 secure_value_slot) = 0;
|
||||
|
||||
virtual bool BackendIsSlow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class DefaultSecureValueBackend : public SecureValueBackend {
|
||||
public:
|
||||
Result ObsoletedSetSaveDataSecureValue(u32 unique_id, u8 title_variation, u32 secure_value_slot,
|
||||
u64 secure_value) override;
|
||||
|
||||
ResultVal<std::tuple<bool, u64>> ObsoletedGetSaveDataSecureValue(
|
||||
u32 unique_id, u8 title_variation, u32 secure_value_slot) override;
|
||||
|
||||
Result ControlSecureSave(u32 action, u8* input, size_t input_size, u8* output,
|
||||
size_t output_size) override;
|
||||
|
||||
Result SetThisSaveDataSecureValue(u32 secure_value_slot, u64 secure_value) override;
|
||||
|
||||
ResultVal<std::tuple<bool, bool, u64>> GetThisSaveDataSecureValue(
|
||||
u32 secure_value_slot) override;
|
||||
|
||||
protected:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int);
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::DefaultSecureValueBackend)
|
||||
119
src/core/file_sys/secure_value_backend_artic.cpp
Normal file
119
src/core/file_sys/secure_value_backend_artic.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
// Copyright 2024 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/archives.h"
|
||||
#include "core/file_sys/archive_artic.h"
|
||||
#include "core/file_sys/secure_value_backend_artic.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ArticSecureValueBackend)
|
||||
|
||||
namespace FileSys {
|
||||
Result ArticSecureValueBackend::ObsoletedSetSaveDataSecureValue(u32 unique_id, u8 title_variation,
|
||||
u32 secure_value_slot,
|
||||
u64 secure_value) {
|
||||
auto req = client->NewRequest("FSUSER_ObsSetSaveDataSecureVal");
|
||||
|
||||
req.AddParameterU64(secure_value);
|
||||
req.AddParameterU32(secure_value_slot);
|
||||
req.AddParameterU32(unique_id);
|
||||
req.AddParameterU8(title_variation);
|
||||
|
||||
return ArticArchive::RespResult(client->Send(req));
|
||||
}
|
||||
|
||||
ResultVal<std::tuple<bool, u64>> ArticSecureValueBackend::ObsoletedGetSaveDataSecureValue(
|
||||
u32 unique_id, u8 title_variation, u32 secure_value_slot) {
|
||||
|
||||
auto req = client->NewRequest("FSUSER_ObsGetSaveDataSecureVal");
|
||||
|
||||
req.AddParameterU32(secure_value_slot);
|
||||
req.AddParameterU32(unique_id);
|
||||
req.AddParameterU8(title_variation);
|
||||
|
||||
auto resp = client->Send(req);
|
||||
auto res = ArticArchive::RespResult(resp);
|
||||
if (res.IsError())
|
||||
return res;
|
||||
|
||||
struct {
|
||||
bool exists;
|
||||
u64 secure_value;
|
||||
} secure_value_result;
|
||||
static_assert(sizeof(secure_value_result) == 0x10);
|
||||
|
||||
auto output_buf = resp->GetResponseBuffer(0);
|
||||
if (!output_buf.has_value())
|
||||
return res;
|
||||
|
||||
if (output_buf->second != sizeof(secure_value_result))
|
||||
return ResultUnknown;
|
||||
|
||||
memcpy(&secure_value_result, output_buf->first, output_buf->second);
|
||||
return std::make_tuple(secure_value_result.exists, secure_value_result.secure_value);
|
||||
}
|
||||
|
||||
Result ArticSecureValueBackend::ControlSecureSave(u32 action, u8* input, size_t input_size,
|
||||
u8* output, size_t output_size) {
|
||||
auto req = client->NewRequest("FSUSER_ControlSecureSave");
|
||||
|
||||
req.AddParameterU32(action);
|
||||
req.AddParameterBuffer(input, input_size);
|
||||
req.AddParameterU32(static_cast<u32>(output_size));
|
||||
|
||||
auto resp = client->Send(req);
|
||||
auto res = ArticArchive::RespResult(resp);
|
||||
if (res.IsError())
|
||||
return res;
|
||||
|
||||
auto output_buf = resp->GetResponseBuffer(0);
|
||||
if (!output_buf.has_value())
|
||||
return res;
|
||||
|
||||
if (output_buf->second != output_size)
|
||||
return ResultUnknown;
|
||||
|
||||
memcpy(output, output_buf->first, output_buf->second);
|
||||
return res;
|
||||
}
|
||||
|
||||
Result ArticSecureValueBackend::SetThisSaveDataSecureValue(u32 secure_value_slot,
|
||||
u64 secure_value) {
|
||||
auto req = client->NewRequest("FSUSER_SetThisSaveDataSecVal");
|
||||
|
||||
req.AddParameterU32(secure_value_slot);
|
||||
req.AddParameterU64(secure_value);
|
||||
|
||||
return ArticArchive::RespResult(client->Send(req));
|
||||
}
|
||||
|
||||
ResultVal<std::tuple<bool, bool, u64>> ArticSecureValueBackend::GetThisSaveDataSecureValue(
|
||||
u32 secure_value_slot) {
|
||||
auto req = client->NewRequest("FSUSER_GetThisSaveDataSecVal");
|
||||
|
||||
req.AddParameterU32(secure_value_slot);
|
||||
|
||||
auto resp = client->Send(req);
|
||||
auto res = ArticArchive::RespResult(resp);
|
||||
if (res.IsError())
|
||||
return res;
|
||||
|
||||
struct {
|
||||
bool exists;
|
||||
bool isGamecard;
|
||||
u64 secure_value;
|
||||
} secure_value_result;
|
||||
static_assert(sizeof(secure_value_result) == 0x10);
|
||||
|
||||
auto output_buf = resp->GetResponseBuffer(0);
|
||||
if (!output_buf.has_value())
|
||||
return res;
|
||||
|
||||
if (output_buf->second != sizeof(secure_value_result))
|
||||
return ResultUnknown;
|
||||
|
||||
memcpy(&secure_value_result, output_buf->first, output_buf->second);
|
||||
return std::make_tuple(secure_value_result.exists, secure_value_result.isGamecard,
|
||||
secure_value_result.secure_value);
|
||||
}
|
||||
} // namespace FileSys
|
||||
53
src/core/file_sys/secure_value_backend_artic.h
Normal file
53
src/core/file_sys/secure_value_backend_artic.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2024 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tuple"
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/secure_value_backend.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "network/artic_base/artic_base_client.h"
|
||||
|
||||
namespace FileSys {
|
||||
class ArticSecureValueBackend : public SecureValueBackend {
|
||||
public:
|
||||
ArticSecureValueBackend(const std::shared_ptr<Network::ArticBase::Client>& _client)
|
||||
: client(_client) {}
|
||||
|
||||
Result ObsoletedSetSaveDataSecureValue(u32 unique_id, u8 title_variation, u32 secure_value_slot,
|
||||
u64 secure_value) override;
|
||||
|
||||
ResultVal<std::tuple<bool, u64>> ObsoletedGetSaveDataSecureValue(
|
||||
u32 unique_id, u8 title_variation, u32 secure_value_slot) override;
|
||||
|
||||
Result ControlSecureSave(u32 action, u8* input, size_t input_size, u8* output,
|
||||
size_t output_size) override;
|
||||
|
||||
Result SetThisSaveDataSecureValue(u32 secure_value_slot, u64 secure_value) override;
|
||||
|
||||
ResultVal<std::tuple<bool, bool, u64>> GetThisSaveDataSecureValue(
|
||||
u32 secure_value_slot) override;
|
||||
|
||||
bool BackendIsSlow() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
ArticSecureValueBackend() = default;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<SecureValueBackend>(*this);
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Network::ArticBase::Client> client;
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArticSecureValueBackend)
|
||||
Reference in New Issue
Block a user