mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2024-11-27 03:54:17 +01:00
artic base: Implement DLC support and other fixes
- Artic Base: Implement DLC support and other fixes - Fix per game settings not working with artic loader - Fix compilation error
This commit is contained in:
parent
10d93555c3
commit
550e06e1c9
@ -10,6 +10,7 @@
|
|||||||
#include "common/archives.h"
|
#include "common/archives.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
|
#include "core/file_sys/archive_artic.h"
|
||||||
#include "core/file_sys/archive_systemsavedata.h"
|
#include "core/file_sys/archive_systemsavedata.h"
|
||||||
#include "core/file_sys/errors.h"
|
#include "core/file_sys/errors.h"
|
||||||
#include "core/file_sys/savedata_archive.h"
|
#include "core/file_sys/savedata_archive.h"
|
||||||
@ -52,24 +53,45 @@ Path ConstructSystemSaveDataBinaryPath(u32 high, u32 low) {
|
|||||||
ArchiveFactory_SystemSaveData::ArchiveFactory_SystemSaveData(const std::string& nand_path)
|
ArchiveFactory_SystemSaveData::ArchiveFactory_SystemSaveData(const std::string& nand_path)
|
||||||
: base_path(GetSystemSaveDataContainerPath(nand_path)) {}
|
: base_path(GetSystemSaveDataContainerPath(nand_path)) {}
|
||||||
|
|
||||||
|
static bool AllowArticSystemSaveData(const Path& path) {
|
||||||
|
constexpr u32 APP_SYSTEM_SAVE_DATA_MASK = 0x00020000;
|
||||||
|
if (path.GetType() == FileSys::LowPathType::Binary) {
|
||||||
|
std::vector<u8> path_data = path.AsBinary();
|
||||||
|
return path_data.size() == 8 &&
|
||||||
|
(*reinterpret_cast<u32*>(path_data.data() + 4) & APP_SYSTEM_SAVE_DATA_MASK) != 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(const Path& path,
|
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(const Path& path,
|
||||||
u64 program_id) {
|
u64 program_id) {
|
||||||
|
if (IsUsingArtic() && AllowArticSystemSaveData(path)) {
|
||||||
|
EnsureCacheCreated();
|
||||||
|
return ArticArchive::Open(artic_client, Service::FS::ArchiveIdCode::SystemSaveData, path,
|
||||||
|
Core::PerfStats::PerfArticEventBits::ARTIC_SYSTEM_SAVE_DATA,
|
||||||
|
*this, false);
|
||||||
|
} else {
|
||||||
std::string fullpath = GetSystemSaveDataPath(base_path, path);
|
std::string fullpath = GetSystemSaveDataPath(base_path, path);
|
||||||
if (!FileUtil::Exists(fullpath)) {
|
if (!FileUtil::Exists(fullpath)) {
|
||||||
// TODO(Subv): Check error code, this one is probably wrong
|
// TODO(Subv): Check error code, this one is probably wrong
|
||||||
return ResultNotFound;
|
return ResultNotFound;
|
||||||
}
|
}
|
||||||
return std::make_unique<SaveDataArchive>(fullpath);
|
return std::make_unique<SaveDataArchive>(fullpath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ArchiveFactory_SystemSaveData::Format(const Path& path,
|
Result ArchiveFactory_SystemSaveData::Format(const Path& path,
|
||||||
const FileSys::ArchiveFormatInfo& format_info,
|
const FileSys::ArchiveFormatInfo& format_info,
|
||||||
u64 program_id, u32 directory_buckets,
|
u64 program_id, u32 directory_buckets,
|
||||||
u32 file_buckets) {
|
u32 file_buckets) {
|
||||||
std::string fullpath = GetSystemSaveDataPath(base_path, path);
|
const std::vector<u8> vec_data = path.AsBinary();
|
||||||
FileUtil::DeleteDirRecursively(fullpath);
|
u32 save_low;
|
||||||
FileUtil::CreateFullPath(fullpath);
|
u32 save_high;
|
||||||
return ResultSuccess;
|
std::memcpy(&save_low, &vec_data[4], sizeof(u32));
|
||||||
|
std::memcpy(&save_high, &vec_data[0], sizeof(u32));
|
||||||
|
return FormatAsSysData(save_high, save_low, format_info.total_size, 0x1000,
|
||||||
|
format_info.number_directories, format_info.number_files,
|
||||||
|
directory_buckets, file_buckets, format_info.duplicate_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultVal<ArchiveFormatInfo> ArchiveFactory_SystemSaveData::GetFormatInfo(const Path& path,
|
ResultVal<ArchiveFormatInfo> ArchiveFactory_SystemSaveData::GetFormatInfo(const Path& path,
|
||||||
@ -79,4 +101,45 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_SystemSaveData::GetFormatInfo(const
|
|||||||
return ResultUnknown;
|
return ResultUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ArchiveFactory_SystemSaveData::FormatAsSysData(u32 high, u32 low, u32 total_size,
|
||||||
|
u32 block_size, u32 number_directories,
|
||||||
|
u32 number_files,
|
||||||
|
u32 number_directory_buckets,
|
||||||
|
u32 number_file_buckets, u8 duplicate_data) {
|
||||||
|
if (IsUsingArtic() &&
|
||||||
|
AllowArticSystemSaveData(FileSys::ConstructSystemSaveDataBinaryPath(high, low))) {
|
||||||
|
auto req = artic_client->NewRequest("FSUSER_CreateSysSaveData");
|
||||||
|
|
||||||
|
req.AddParameterU32(high);
|
||||||
|
req.AddParameterU32(low);
|
||||||
|
req.AddParameterU32(total_size);
|
||||||
|
req.AddParameterU32(block_size);
|
||||||
|
req.AddParameterU32(number_directories);
|
||||||
|
req.AddParameterU32(number_files);
|
||||||
|
req.AddParameterU32(number_directory_buckets);
|
||||||
|
req.AddParameterU32(number_file_buckets);
|
||||||
|
req.AddParameterU8(duplicate_data);
|
||||||
|
|
||||||
|
auto resp = artic_client->Send(req);
|
||||||
|
if (!resp.has_value() || !resp->Succeeded()) {
|
||||||
|
return ResultUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result res(static_cast<u32>(resp->GetMethodResult()));
|
||||||
|
return res;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Construct the binary path to the archive first
|
||||||
|
const FileSys::Path path = FileSys::ConstructSystemSaveDataBinaryPath(high, low);
|
||||||
|
|
||||||
|
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||||
|
const std::string base_path = FileSys::GetSystemSaveDataContainerPath(nand_directory);
|
||||||
|
const std::string systemsavedata_path = FileSys::GetSystemSaveDataPath(base_path, path);
|
||||||
|
if (!FileUtil::CreateFullPath(systemsavedata_path)) {
|
||||||
|
return ResultUnknown; // TODO(Subv): Find the right error code
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
@ -10,12 +10,15 @@
|
|||||||
#include <boost/serialization/string.hpp>
|
#include <boost/serialization/string.hpp>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/archive_backend.h"
|
#include "core/file_sys/archive_backend.h"
|
||||||
|
#include "core/file_sys/artic_cache.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
#include "core/hle/service/fs/archive.h"
|
||||||
|
#include "network/artic_base/artic_base_client.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
/// File system interface to the SystemSaveData archive
|
/// File system interface to the SystemSaveData archive
|
||||||
class ArchiveFactory_SystemSaveData final : public ArchiveFactory {
|
class ArchiveFactory_SystemSaveData final : public ArchiveFactory, public ArticCacheProvider {
|
||||||
public:
|
public:
|
||||||
explicit ArchiveFactory_SystemSaveData(const std::string& mount_point);
|
explicit ArchiveFactory_SystemSaveData(const std::string& mount_point);
|
||||||
|
|
||||||
@ -24,13 +27,31 @@ public:
|
|||||||
u32 directory_buckets, u32 file_buckets) override;
|
u32 directory_buckets, u32 file_buckets) override;
|
||||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
||||||
|
|
||||||
|
Result FormatAsSysData(u32 high, u32 low, u32 total_size, u32 block_size,
|
||||||
|
u32 number_directories, u32 number_files, u32 number_directory_buckets,
|
||||||
|
u32 number_file_buckets, u8 duplicate_data);
|
||||||
|
|
||||||
std::string GetName() const override {
|
std::string GetName() const override {
|
||||||
return "SystemSaveData";
|
return "SystemSaveData";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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:
|
private:
|
||||||
std::string base_path;
|
std::string base_path;
|
||||||
|
|
||||||
|
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
|
||||||
|
|
||||||
ArchiveFactory_SystemSaveData() = default;
|
ArchiveFactory_SystemSaveData() = default;
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive& ar, const unsigned int) {
|
void serialize(Archive& ar, const unsigned int) {
|
||||||
|
@ -775,30 +775,137 @@ Module::Interface::~Interface() = default;
|
|||||||
|
|
||||||
void Module::Interface::GetNumPrograms(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::GetNumPrograms(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
u32 media_type = rp.Pop<u8>();
|
u8 media_type = rp.Pop<u8>();
|
||||||
|
|
||||||
|
if (artic_client.get()) {
|
||||||
|
struct AsyncData {
|
||||||
|
u8 media_type;
|
||||||
|
|
||||||
|
ResultVal<s32> res;
|
||||||
|
};
|
||||||
|
auto async_data = std::make_shared<AsyncData>();
|
||||||
|
async_data->media_type = media_type;
|
||||||
|
|
||||||
|
ctx.RunAsync(
|
||||||
|
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
auto req = artic_client->NewRequest("AM_GetTitleCount");
|
||||||
|
|
||||||
|
req.AddParameterU8(async_data->media_type);
|
||||||
|
|
||||||
|
auto resp = artic_client->Send(req);
|
||||||
|
|
||||||
|
if (!resp.has_value() || !resp->Succeeded()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = Result(static_cast<u32>(resp->GetMethodResult()));
|
||||||
|
if (res.IsError()) {
|
||||||
|
async_data->res = res;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto count = resp->GetResponseS32(0);
|
||||||
|
if (!count.has_value()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async_data->res = *count;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
[async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestBuilder rb(ctx, 2, 0);
|
||||||
|
|
||||||
|
rb.Push(async_data->res.Code());
|
||||||
|
rb.Push<u32>(
|
||||||
|
static_cast<u32>(async_data->res.Succeeded() ? async_data->res.Unwrap() : 0));
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
} else {
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<u32>(static_cast<u32>(am->am_title_list[media_type].size()));
|
rb.Push<u32>(static_cast<u32>(am->am_title_list[media_type].size()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::FindDLCContentInfos(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::FindDLCContentInfos(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
|
||||||
|
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
||||||
u64 title_id = rp.Pop<u64>();
|
u64 title_id = rp.Pop<u64>();
|
||||||
u32 content_count = rp.Pop<u32>();
|
u32 content_count = rp.Pop<u32>();
|
||||||
auto& content_requested_in = rp.PopMappedBuffer();
|
auto& content_requested_in = rp.PopMappedBuffer();
|
||||||
|
if (artic_client.get()) {
|
||||||
|
struct AsyncData {
|
||||||
|
u8 media_type;
|
||||||
|
u64 title_id;
|
||||||
|
std::vector<u16> content_requested;
|
||||||
|
|
||||||
|
Result res{0};
|
||||||
|
std::vector<u8> out;
|
||||||
|
Kernel::MappedBuffer* content_info_out;
|
||||||
|
};
|
||||||
|
auto async_data = std::make_shared<AsyncData>();
|
||||||
|
async_data->media_type = static_cast<u8>(media_type);
|
||||||
|
async_data->title_id = title_id;
|
||||||
|
async_data->content_requested.resize(content_count);
|
||||||
|
content_requested_in.Read(async_data->content_requested.data(), 0,
|
||||||
|
content_count * sizeof(u16));
|
||||||
|
async_data->content_info_out = &rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
ctx.RunAsync(
|
||||||
|
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
auto req = artic_client->NewRequest("AMAPP_FindDLCContentInfos");
|
||||||
|
|
||||||
|
req.AddParameterU8(async_data->media_type);
|
||||||
|
req.AddParameterU64(async_data->title_id);
|
||||||
|
req.AddParameterBuffer(async_data->content_requested.data(),
|
||||||
|
async_data->content_requested.size() * sizeof(u16));
|
||||||
|
|
||||||
|
auto resp = artic_client->Send(req);
|
||||||
|
|
||||||
|
if (!resp.has_value() || !resp->Succeeded()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = Result(static_cast<u32>(resp->GetMethodResult()));
|
||||||
|
if (res.IsError()) {
|
||||||
|
async_data->res = res;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto content_info = resp->GetResponseBuffer(0);
|
||||||
|
if (!content_info.has_value()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async_data->out.resize(content_info->second);
|
||||||
|
memcpy(async_data->out.data(), content_info->first, content_info->second);
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
[async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
if (async_data->res.IsSuccess()) {
|
||||||
|
async_data->content_info_out->Write(async_data->out.data(), 0,
|
||||||
|
async_data->out.size());
|
||||||
|
}
|
||||||
|
IPC::RequestBuilder rb(ctx, 1, 0);
|
||||||
|
rb.Push(async_data->res);
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
} else {
|
||||||
|
|
||||||
auto& content_info_out = rp.PopMappedBuffer();
|
auto& content_info_out = rp.PopMappedBuffer();
|
||||||
|
|
||||||
// Validate that only DLC TIDs are passed in
|
// Validate that only DLC TIDs are passed in
|
||||||
u32 tid_high = static_cast<u32>(title_id >> 32);
|
u32 tid_high = static_cast<u32>(title_id >> 32);
|
||||||
if (tid_high != TID_HIGH_DLC) {
|
if (tid_high != TID_HIGH_DLC) {
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(Result(ErrCodes::InvalidTIDInList, ErrorModule::AM, ErrorSummary::InvalidArgument,
|
rb.Push(Result(ErrCodes::InvalidTIDInList, ErrorModule::AM,
|
||||||
ErrorLevel::Usage));
|
ErrorSummary::InvalidArgument, ErrorLevel::Usage));
|
||||||
rb.PushMappedBuffer(content_requested_in);
|
|
||||||
rb.PushMappedBuffer(content_info_out);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -817,10 +924,8 @@ void Module::Interface::FindDLCContentInfos(Kernel::HLERequestContext& ctx) {
|
|||||||
"Attempted to get info for non-existent content index {:04x}.",
|
"Attempted to get info for non-existent content index {:04x}.",
|
||||||
content_requested[i]);
|
content_requested[i]);
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push<u32>(-1); // TODO(Steveice10): Find the right error code
|
rb.Push<u32>(-1); // TODO(Steveice10): Find the right error code
|
||||||
rb.PushMappedBuffer(content_requested_in);
|
|
||||||
rb.PushMappedBuffer(content_info_out);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -832,7 +937,8 @@ void Module::Interface::FindDLCContentInfos(Kernel::HLERequestContext& ctx) {
|
|||||||
content_info.ownership =
|
content_info.ownership =
|
||||||
OWNERSHIP_OWNED; // TODO(Steveice10): Pull this from the ticket.
|
OWNERSHIP_OWNED; // TODO(Steveice10): Pull this from the ticket.
|
||||||
|
|
||||||
if (FileUtil::Exists(GetTitleContentPath(media_type, title_id, content_requested[i]))) {
|
if (FileUtil::Exists(
|
||||||
|
GetTitleContentPath(media_type, title_id, content_requested[i]))) {
|
||||||
content_info.ownership |= OWNERSHIP_DOWNLOADED;
|
content_info.ownership |= OWNERSHIP_DOWNLOADED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -841,10 +947,9 @@ void Module::Interface::FindDLCContentInfos(Kernel::HLERequestContext& ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.PushMappedBuffer(content_requested_in);
|
}
|
||||||
rb.PushMappedBuffer(content_info_out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::ListDLCContentInfos(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::ListDLCContentInfos(Kernel::HLERequestContext& ctx) {
|
||||||
@ -854,16 +959,78 @@ void Module::Interface::ListDLCContentInfos(Kernel::HLERequestContext& ctx) {
|
|||||||
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
||||||
u64 title_id = rp.Pop<u64>();
|
u64 title_id = rp.Pop<u64>();
|
||||||
u32 start_index = rp.Pop<u32>();
|
u32 start_index = rp.Pop<u32>();
|
||||||
|
|
||||||
|
if (artic_client.get()) {
|
||||||
|
struct AsyncData {
|
||||||
|
u8 media_type;
|
||||||
|
u64 title_id;
|
||||||
|
u32 content_count;
|
||||||
|
u32 start_index;
|
||||||
|
|
||||||
|
Result res{0};
|
||||||
|
std::vector<u8> out;
|
||||||
|
Kernel::MappedBuffer* content_info_out;
|
||||||
|
};
|
||||||
|
auto async_data = std::make_shared<AsyncData>();
|
||||||
|
async_data->media_type = static_cast<u8>(media_type);
|
||||||
|
async_data->title_id = title_id;
|
||||||
|
async_data->content_count = content_count;
|
||||||
|
async_data->start_index = start_index;
|
||||||
|
async_data->content_info_out = &rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
ctx.RunAsync(
|
||||||
|
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
auto req = artic_client->NewRequest("AMAPP_ListDLCContentInfos");
|
||||||
|
|
||||||
|
req.AddParameterU32(async_data->content_count);
|
||||||
|
req.AddParameterU8(async_data->media_type);
|
||||||
|
req.AddParameterU64(async_data->title_id);
|
||||||
|
req.AddParameterU32(async_data->start_index);
|
||||||
|
|
||||||
|
auto resp = artic_client->Send(req);
|
||||||
|
|
||||||
|
if (!resp.has_value() || !resp->Succeeded()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = Result(static_cast<u32>(resp->GetMethodResult()));
|
||||||
|
if (res.IsError()) {
|
||||||
|
async_data->res = res;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto content_info = resp->GetResponseBuffer(0);
|
||||||
|
if (!content_info.has_value()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async_data->out.resize(content_info->second);
|
||||||
|
memcpy(async_data->out.data(), content_info->first, content_info->second);
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
[async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
if (async_data->res.IsSuccess()) {
|
||||||
|
async_data->content_info_out->Write(async_data->out.data(), 0,
|
||||||
|
async_data->out.size());
|
||||||
|
}
|
||||||
|
IPC::RequestBuilder rb(ctx, 2, 0);
|
||||||
|
rb.Push(async_data->res);
|
||||||
|
rb.Push<u32>(static_cast<u32>(async_data->out.size() / sizeof(ContentInfo)));
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
} else {
|
||||||
|
|
||||||
auto& content_info_out = rp.PopMappedBuffer();
|
auto& content_info_out = rp.PopMappedBuffer();
|
||||||
|
|
||||||
// Validate that only DLC TIDs are passed in
|
// Validate that only DLC TIDs are passed in
|
||||||
u32 tid_high = static_cast<u32>(title_id >> 32);
|
u32 tid_high = static_cast<u32>(title_id >> 32);
|
||||||
if (tid_high != TID_HIGH_DLC) {
|
if (tid_high != TID_HIGH_DLC) {
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
rb.Push(Result(ErrCodes::InvalidTIDInList, ErrorModule::AM, ErrorSummary::InvalidArgument,
|
rb.Push(Result(ErrCodes::InvalidTIDInList, ErrorModule::AM,
|
||||||
ErrorLevel::Usage));
|
ErrorSummary::InvalidArgument, ErrorLevel::Usage));
|
||||||
rb.Push<u32>(0);
|
rb.Push<u32>(0);
|
||||||
rb.PushMappedBuffer(content_info_out);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -894,10 +1061,10 @@ void Module::Interface::ListDLCContentInfos(Kernel::HLERequestContext& ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push(copied);
|
rb.Push(copied);
|
||||||
rb.PushMappedBuffer(content_info_out);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::DeleteContents(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::DeleteContents(Kernel::HLERequestContext& ctx) {
|
||||||
@ -916,16 +1083,77 @@ void Module::Interface::DeleteContents(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
void Module::Interface::GetProgramList(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::GetProgramList(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
|
|
||||||
u32 count = rp.Pop<u32>();
|
u32 count = rp.Pop<u32>();
|
||||||
u8 media_type = rp.Pop<u8>();
|
u8 media_type = rp.Pop<u8>();
|
||||||
|
|
||||||
|
if (artic_client.get()) {
|
||||||
|
struct AsyncData {
|
||||||
|
u32 count;
|
||||||
|
u8 media_type;
|
||||||
|
|
||||||
|
Result res{0};
|
||||||
|
std::vector<u8> out;
|
||||||
|
Kernel::MappedBuffer* title_ids_output;
|
||||||
|
};
|
||||||
|
auto async_data = std::make_shared<AsyncData>();
|
||||||
|
async_data->count = count;
|
||||||
|
async_data->media_type = media_type;
|
||||||
|
async_data->title_ids_output = &rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
ctx.RunAsync(
|
||||||
|
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
auto req = artic_client->NewRequest("AM_GetTitleList");
|
||||||
|
|
||||||
|
req.AddParameterU32(async_data->count);
|
||||||
|
req.AddParameterU8(async_data->media_type);
|
||||||
|
|
||||||
|
auto resp = artic_client->Send(req);
|
||||||
|
|
||||||
|
if (!resp.has_value() || !resp->Succeeded()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = Result(static_cast<u32>(resp->GetMethodResult()));
|
||||||
|
if (res.IsError()) {
|
||||||
|
async_data->res = res;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
async_data->res = res;
|
||||||
|
|
||||||
|
auto title_ids = resp->GetResponseBuffer(0);
|
||||||
|
if (!title_ids.has_value()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async_data->out.resize(title_ids->second);
|
||||||
|
memcpy(async_data->out.data(), title_ids->first, title_ids->second);
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
[async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
if (!async_data->res.IsSuccess()) {
|
||||||
|
IPC::RequestBuilder rb(ctx, 2, 0);
|
||||||
|
rb.Push(async_data->res);
|
||||||
|
rb.Push<u32>(0);
|
||||||
|
} else {
|
||||||
|
async_data->title_ids_output->Write(async_data->out.data(), 0,
|
||||||
|
async_data->out.size());
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb(ctx, 2, 0);
|
||||||
|
rb.Push(async_data->res);
|
||||||
|
rb.Push<u32>(static_cast<u32>(async_data->out.size() / sizeof(u64)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
|
||||||
|
} else {
|
||||||
auto& title_ids_output = rp.PopMappedBuffer();
|
auto& title_ids_output = rp.PopMappedBuffer();
|
||||||
|
|
||||||
if (media_type > 2) {
|
if (media_type > 2) {
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
rb.Push<u32>(-1); // TODO(shinyquagsire23): Find the right error code
|
rb.Push<u32>(-1); // TODO(shinyquagsire23): Find the right error code
|
||||||
rb.Push<u32>(0);
|
rb.Push<u32>(0);
|
||||||
rb.PushMappedBuffer(title_ids_output);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -934,10 +1162,10 @@ void Module::Interface::GetProgramList(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
title_ids_output.Write(am->am_title_list[media_type].data(), 0, copied * sizeof(u64));
|
title_ids_output.Write(am->am_title_list[media_type].data(), 0, copied * sizeof(u64));
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push(copied);
|
rb.Push(copied);
|
||||||
rb.PushMappedBuffer(title_ids_output);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetTitleInfoFromList(std::span<const u64> title_id_list, Service::FS::MediaType media_type,
|
Result GetTitleInfoFromList(std::span<const u64> title_id_list, Service::FS::MediaType media_type,
|
||||||
@ -967,11 +1195,88 @@ Result GetTitleInfoFromList(std::span<const u64> title_id_list, Service::FS::Med
|
|||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::GetProgramInfos(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool ignore_platform) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
|
|
||||||
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
||||||
u32 title_count = rp.Pop<u32>();
|
u32 title_count = rp.Pop<u32>();
|
||||||
|
|
||||||
|
if (artic_client.get()) {
|
||||||
|
struct AsyncData {
|
||||||
|
u8 media_type;
|
||||||
|
bool ignore_platform;
|
||||||
|
std::vector<u64> title_id_list;
|
||||||
|
|
||||||
|
Result res{0};
|
||||||
|
std::vector<u8> out;
|
||||||
|
Kernel::MappedBuffer* title_id_list_buffer;
|
||||||
|
Kernel::MappedBuffer* title_info_out;
|
||||||
|
};
|
||||||
|
auto async_data = std::make_shared<AsyncData>();
|
||||||
|
async_data->media_type = static_cast<u8>(media_type);
|
||||||
|
async_data->ignore_platform = ignore_platform;
|
||||||
|
async_data->title_id_list.resize(title_count);
|
||||||
|
async_data->title_id_list_buffer = &rp.PopMappedBuffer();
|
||||||
|
async_data->title_id_list_buffer->Read(async_data->title_id_list.data(), 0,
|
||||||
|
title_count * sizeof(u64));
|
||||||
|
async_data->title_info_out = &rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
ctx.RunAsync(
|
||||||
|
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
auto req = artic_client->NewRequest("AM_GetTitleInfo");
|
||||||
|
|
||||||
|
req.AddParameterU8(async_data->media_type);
|
||||||
|
req.AddParameterBuffer(async_data->title_id_list.data(),
|
||||||
|
async_data->title_id_list.size() * sizeof(u64));
|
||||||
|
req.AddParameterU8(async_data->ignore_platform ? 1 : 0);
|
||||||
|
|
||||||
|
auto resp = artic_client->Send(req);
|
||||||
|
|
||||||
|
if (!resp.has_value() || !resp->Succeeded()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = Result(static_cast<u32>(resp->GetMethodResult()));
|
||||||
|
if (res.IsError()) {
|
||||||
|
async_data->res = res;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
async_data->res = res;
|
||||||
|
|
||||||
|
auto title_infos = resp->GetResponseBuffer(0);
|
||||||
|
if (!title_infos.has_value()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async_data->out.resize(title_infos->second);
|
||||||
|
memcpy(async_data->out.data(), title_infos->first, title_infos->second);
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
[async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
if (!async_data->res.IsSuccess()) {
|
||||||
|
IPC::RequestBuilder rb(ctx, 1, async_data->ignore_platform ? 0 : 4);
|
||||||
|
rb.Push(async_data->res);
|
||||||
|
if (!async_data->ignore_platform) {
|
||||||
|
rb.PushMappedBuffer(*async_data->title_id_list_buffer);
|
||||||
|
rb.PushMappedBuffer(*async_data->title_info_out);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
async_data->title_info_out->Write(async_data->out.data(), 0,
|
||||||
|
async_data->out.size());
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb(ctx, 1, async_data->ignore_platform ? 0 : 4);
|
||||||
|
rb.Push(async_data->res);
|
||||||
|
if (!async_data->ignore_platform) {
|
||||||
|
rb.PushMappedBuffer(*async_data->title_id_list_buffer);
|
||||||
|
rb.PushMappedBuffer(*async_data->title_info_out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
|
||||||
|
} else {
|
||||||
auto& title_id_list_buffer = rp.PopMappedBuffer();
|
auto& title_id_list_buffer = rp.PopMappedBuffer();
|
||||||
auto& title_info_out = rp.PopMappedBuffer();
|
auto& title_info_out = rp.PopMappedBuffer();
|
||||||
|
|
||||||
@ -980,14 +1285,21 @@ void Module::Interface::GetProgramInfos(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
Result result = GetTitleInfoFromList(title_id_list, media_type, title_info_out);
|
Result result = GetTitleInfoFromList(title_id_list, media_type, title_info_out);
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, ignore_platform ? 0 : 4);
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
|
if (!ignore_platform) {
|
||||||
rb.PushMappedBuffer(title_id_list_buffer);
|
rb.PushMappedBuffer(title_id_list_buffer);
|
||||||
rb.PushMappedBuffer(title_info_out);
|
rb.PushMappedBuffer(title_info_out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetProgramInfos(Kernel::HLERequestContext& ctx) {
|
||||||
|
GetProgramInfosImpl(ctx, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::GetProgramInfosIgnorePlatform(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::GetProgramInfosIgnorePlatform(Kernel::HLERequestContext& ctx) {
|
||||||
GetProgramInfos(ctx);
|
GetProgramInfosImpl(ctx, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::DeleteUserProgram(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::DeleteUserProgram(Kernel::HLERequestContext& ctx) {
|
||||||
@ -1049,6 +1361,75 @@ void Module::Interface::GetDLCTitleInfos(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
||||||
u32 title_count = rp.Pop<u32>();
|
u32 title_count = rp.Pop<u32>();
|
||||||
|
|
||||||
|
if (artic_client.get()) {
|
||||||
|
struct AsyncData {
|
||||||
|
u8 media_type;
|
||||||
|
std::vector<u64> title_id_list;
|
||||||
|
|
||||||
|
Result res{0};
|
||||||
|
std::vector<u8> out;
|
||||||
|
Kernel::MappedBuffer* title_id_list_buffer;
|
||||||
|
Kernel::MappedBuffer* title_info_out;
|
||||||
|
};
|
||||||
|
auto async_data = std::make_shared<AsyncData>();
|
||||||
|
async_data->media_type = static_cast<u8>(media_type);
|
||||||
|
async_data->title_id_list.resize(title_count);
|
||||||
|
async_data->title_id_list_buffer = &rp.PopMappedBuffer();
|
||||||
|
async_data->title_id_list_buffer->Read(async_data->title_id_list.data(), 0,
|
||||||
|
title_count * sizeof(u64));
|
||||||
|
async_data->title_info_out = &rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
ctx.RunAsync(
|
||||||
|
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
auto req = artic_client->NewRequest("AMAPP_GetDLCTitleInfos");
|
||||||
|
|
||||||
|
req.AddParameterU8(async_data->media_type);
|
||||||
|
req.AddParameterBuffer(async_data->title_id_list.data(),
|
||||||
|
async_data->title_id_list.size() * sizeof(u64));
|
||||||
|
|
||||||
|
auto resp = artic_client->Send(req);
|
||||||
|
|
||||||
|
if (!resp.has_value() || !resp->Succeeded()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = Result(static_cast<u32>(resp->GetMethodResult()));
|
||||||
|
if (res.IsError()) {
|
||||||
|
async_data->res = res;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
async_data->res = res;
|
||||||
|
|
||||||
|
auto title_infos = resp->GetResponseBuffer(0);
|
||||||
|
if (!title_infos.has_value()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async_data->out.resize(title_infos->second);
|
||||||
|
memcpy(async_data->out.data(), title_infos->first, title_infos->second);
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
[async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
if (!async_data->res.IsSuccess()) {
|
||||||
|
IPC::RequestBuilder rb(ctx, 1, 4);
|
||||||
|
rb.Push(async_data->res);
|
||||||
|
rb.PushMappedBuffer(*async_data->title_id_list_buffer);
|
||||||
|
rb.PushMappedBuffer(*async_data->title_info_out);
|
||||||
|
} else {
|
||||||
|
async_data->title_info_out->Write(async_data->out.data(), 0,
|
||||||
|
async_data->out.size());
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb(ctx, 1, 4);
|
||||||
|
rb.Push(async_data->res);
|
||||||
|
rb.PushMappedBuffer(*async_data->title_id_list_buffer);
|
||||||
|
rb.PushMappedBuffer(*async_data->title_info_out);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
} else {
|
||||||
auto& title_id_list_buffer = rp.PopMappedBuffer();
|
auto& title_id_list_buffer = rp.PopMappedBuffer();
|
||||||
auto& title_info_out = rp.PopMappedBuffer();
|
auto& title_info_out = rp.PopMappedBuffer();
|
||||||
|
|
||||||
@ -1075,6 +1456,7 @@ void Module::Interface::GetDLCTitleInfos(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
rb.PushMappedBuffer(title_id_list_buffer);
|
rb.PushMappedBuffer(title_id_list_buffer);
|
||||||
rb.PushMappedBuffer(title_info_out);
|
rb.PushMappedBuffer(title_info_out);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::GetPatchTitleInfos(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::GetPatchTitleInfos(Kernel::HLERequestContext& ctx) {
|
||||||
@ -1082,6 +1464,75 @@ void Module::Interface::GetPatchTitleInfos(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
||||||
u32 title_count = rp.Pop<u32>();
|
u32 title_count = rp.Pop<u32>();
|
||||||
|
|
||||||
|
if (artic_client.get()) {
|
||||||
|
struct AsyncData {
|
||||||
|
u8 media_type;
|
||||||
|
std::vector<u64> title_id_list;
|
||||||
|
|
||||||
|
Result res{0};
|
||||||
|
std::vector<u8> out;
|
||||||
|
Kernel::MappedBuffer* title_id_list_buffer;
|
||||||
|
Kernel::MappedBuffer* title_info_out;
|
||||||
|
};
|
||||||
|
auto async_data = std::make_shared<AsyncData>();
|
||||||
|
async_data->media_type = static_cast<u8>(media_type);
|
||||||
|
async_data->title_id_list.resize(title_count);
|
||||||
|
async_data->title_id_list_buffer = &rp.PopMappedBuffer();
|
||||||
|
async_data->title_id_list_buffer->Read(async_data->title_id_list.data(), 0,
|
||||||
|
title_count * sizeof(u64));
|
||||||
|
async_data->title_info_out = &rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
ctx.RunAsync(
|
||||||
|
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
auto req = artic_client->NewRequest("AMAPP_GetPatchTitleInfos");
|
||||||
|
|
||||||
|
req.AddParameterU8(async_data->media_type);
|
||||||
|
req.AddParameterBuffer(async_data->title_id_list.data(),
|
||||||
|
async_data->title_id_list.size() * sizeof(u64));
|
||||||
|
|
||||||
|
auto resp = artic_client->Send(req);
|
||||||
|
|
||||||
|
if (!resp.has_value() || !resp->Succeeded()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = Result(static_cast<u32>(resp->GetMethodResult()));
|
||||||
|
if (res.IsError()) {
|
||||||
|
async_data->res = res;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
async_data->res = res;
|
||||||
|
|
||||||
|
auto title_infos = resp->GetResponseBuffer(0);
|
||||||
|
if (!title_infos.has_value()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async_data->out.resize(title_infos->second);
|
||||||
|
memcpy(async_data->out.data(), title_infos->first, title_infos->second);
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
[async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
if (!async_data->res.IsSuccess()) {
|
||||||
|
IPC::RequestBuilder rb(ctx, 1, 4);
|
||||||
|
rb.Push(async_data->res);
|
||||||
|
rb.PushMappedBuffer(*async_data->title_id_list_buffer);
|
||||||
|
rb.PushMappedBuffer(*async_data->title_info_out);
|
||||||
|
} else {
|
||||||
|
async_data->title_info_out->Write(async_data->out.data(), 0,
|
||||||
|
async_data->out.size());
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb(ctx, 1, 4);
|
||||||
|
rb.Push(async_data->res);
|
||||||
|
rb.PushMappedBuffer(*async_data->title_id_list_buffer);
|
||||||
|
rb.PushMappedBuffer(*async_data->title_info_out);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
} else {
|
||||||
auto& title_id_list_buffer = rp.PopMappedBuffer();
|
auto& title_id_list_buffer = rp.PopMappedBuffer();
|
||||||
auto& title_info_out = rp.PopMappedBuffer();
|
auto& title_info_out = rp.PopMappedBuffer();
|
||||||
|
|
||||||
@ -1108,6 +1559,7 @@ void Module::Interface::GetPatchTitleInfos(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
rb.PushMappedBuffer(title_id_list_buffer);
|
rb.PushMappedBuffer(title_id_list_buffer);
|
||||||
rb.PushMappedBuffer(title_info_out);
|
rb.PushMappedBuffer(title_info_out);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::ListDataTitleTicketInfos(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::ListDataTitleTicketInfos(Kernel::HLERequestContext& ctx) {
|
||||||
@ -1115,6 +1567,64 @@ void Module::Interface::ListDataTitleTicketInfos(Kernel::HLERequestContext& ctx)
|
|||||||
u32 ticket_count = rp.Pop<u32>();
|
u32 ticket_count = rp.Pop<u32>();
|
||||||
u64 title_id = rp.Pop<u64>();
|
u64 title_id = rp.Pop<u64>();
|
||||||
u32 start_index = rp.Pop<u32>();
|
u32 start_index = rp.Pop<u32>();
|
||||||
|
if (artic_client.get()) {
|
||||||
|
struct AsyncData {
|
||||||
|
u64 title_id;
|
||||||
|
u32 ticket_count;
|
||||||
|
u32 start_index;
|
||||||
|
|
||||||
|
Result res{0};
|
||||||
|
std::vector<u8> out;
|
||||||
|
Kernel::MappedBuffer* ticket_info_out;
|
||||||
|
};
|
||||||
|
auto async_data = std::make_shared<AsyncData>();
|
||||||
|
async_data->title_id = title_id;
|
||||||
|
async_data->ticket_count = ticket_count;
|
||||||
|
async_data->start_index = start_index;
|
||||||
|
async_data->ticket_info_out = &rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
ctx.RunAsync(
|
||||||
|
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
auto req = artic_client->NewRequest("AMAPP_ListDataTitleTicketInfos");
|
||||||
|
|
||||||
|
req.AddParameterU32(async_data->ticket_count);
|
||||||
|
req.AddParameterU64(async_data->title_id);
|
||||||
|
req.AddParameterU32(async_data->start_index);
|
||||||
|
|
||||||
|
auto resp = artic_client->Send(req);
|
||||||
|
|
||||||
|
if (!resp.has_value() || !resp->Succeeded()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = Result(static_cast<u32>(resp->GetMethodResult()));
|
||||||
|
if (res.IsError()) {
|
||||||
|
async_data->res = res;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto content_info = resp->GetResponseBuffer(0);
|
||||||
|
if (!content_info.has_value()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async_data->out.resize(content_info->second);
|
||||||
|
memcpy(async_data->out.data(), content_info->first, content_info->second);
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
[async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
if (async_data->res.IsSuccess()) {
|
||||||
|
async_data->ticket_info_out->Write(async_data->out.data(), 0,
|
||||||
|
async_data->out.size());
|
||||||
|
}
|
||||||
|
IPC::RequestBuilder rb(ctx, 2, 0);
|
||||||
|
rb.Push(async_data->res);
|
||||||
|
rb.Push<u32>(static_cast<u32>(async_data->out.size() / sizeof(TicketInfo)));
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
} else {
|
||||||
auto& ticket_info_out = rp.PopMappedBuffer();
|
auto& ticket_info_out = rp.PopMappedBuffer();
|
||||||
|
|
||||||
std::size_t write_offset = 0;
|
std::size_t write_offset = 0;
|
||||||
@ -1136,17 +1646,67 @@ void Module::Interface::ListDataTitleTicketInfos(Kernel::HLERequestContext& ctx)
|
|||||||
LOG_WARNING(Service_AM,
|
LOG_WARNING(Service_AM,
|
||||||
"(STUBBED) ticket_count=0x{:08X}, title_id=0x{:016x}, start_index=0x{:08X}",
|
"(STUBBED) ticket_count=0x{:08X}, title_id=0x{:016x}, start_index=0x{:08X}",
|
||||||
ticket_count, title_id, start_index);
|
ticket_count, title_id, start_index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::GetDLCContentInfoCount(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::GetDLCContentInfoCount(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
||||||
u64 title_id = rp.Pop<u64>();
|
u64 title_id = rp.Pop<u64>();
|
||||||
|
if (artic_client.get()) {
|
||||||
|
struct AsyncData {
|
||||||
|
u8 media_type;
|
||||||
|
u64 title_id;
|
||||||
|
|
||||||
|
ResultVal<s32> res;
|
||||||
|
};
|
||||||
|
auto async_data = std::make_shared<AsyncData>();
|
||||||
|
async_data->media_type = static_cast<u8>(media_type);
|
||||||
|
async_data->title_id = title_id;
|
||||||
|
|
||||||
|
ctx.RunAsync(
|
||||||
|
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
auto req = artic_client->NewRequest("AMAPP_GetDLCContentInfoCount");
|
||||||
|
|
||||||
|
req.AddParameterU8(async_data->media_type);
|
||||||
|
req.AddParameterU64(async_data->title_id);
|
||||||
|
|
||||||
|
auto resp = artic_client->Send(req);
|
||||||
|
|
||||||
|
if (!resp.has_value() || !resp->Succeeded()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = Result(static_cast<u32>(resp->GetMethodResult()));
|
||||||
|
if (res.IsError()) {
|
||||||
|
async_data->res = res;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto count = resp->GetResponseS32(0);
|
||||||
|
if (!count.has_value()) {
|
||||||
|
async_data->res = Result(-1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async_data->res = *count;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
[async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestBuilder rb(ctx, 2, 0);
|
||||||
|
|
||||||
|
rb.Push(async_data->res.Code());
|
||||||
|
rb.Push<u32>(
|
||||||
|
static_cast<u32>(async_data->res.Succeeded() ? async_data->res.Unwrap() : 0));
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
} else {
|
||||||
|
|
||||||
// Validate that only DLC TIDs are passed in
|
// Validate that only DLC TIDs are passed in
|
||||||
u32 tid_high = static_cast<u32>(title_id >> 32);
|
u32 tid_high = static_cast<u32>(title_id >> 32);
|
||||||
if (tid_high != TID_HIGH_DLC) {
|
if (tid_high != TID_HIGH_DLC) {
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
rb.Push(Result(ErrCodes::InvalidTID, ErrorModule::AM, ErrorSummary::InvalidArgument,
|
rb.Push(Result(ErrCodes::InvalidTID, ErrorModule::AM, ErrorSummary::InvalidArgument,
|
||||||
ErrorLevel::Usage));
|
ErrorLevel::Usage));
|
||||||
rb.Push<u32>(0);
|
rb.Push<u32>(0);
|
||||||
@ -1163,8 +1723,9 @@ void Module::Interface::GetDLCContentInfoCount(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.Push<u32>(static_cast<u32>(tmd.GetContentCount()));
|
rb.Push<u32>(static_cast<u32>(tmd.GetContentCount()));
|
||||||
} else {
|
} else {
|
||||||
rb.Push<u32>(1); // Number of content infos plus one
|
rb.Push<u32>(1); // Number of content infos plus one
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called media_type={}, title_id=0x{:016x}", media_type,
|
LOG_WARNING(Service_AM, "(STUBBED) called media_type={}, title_id=0x{:016x}",
|
||||||
title_id);
|
media_type, title_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "core/hle/kernel/mutex.h"
|
#include "core/hle/kernel/mutex.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
#include "network/artic_base/artic_base_client.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
@ -216,7 +217,13 @@ public:
|
|||||||
Interface(std::shared_ptr<Module> am, const char* name, u32 max_session);
|
Interface(std::shared_ptr<Module> am, const char* name, u32 max_session);
|
||||||
~Interface();
|
~Interface();
|
||||||
|
|
||||||
|
void UseArticClient(std::shared_ptr<Network::ArticBase::Client>& client) {
|
||||||
|
artic_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool ignore_platform);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AM::GetNumPrograms service function
|
* AM::GetNumPrograms service function
|
||||||
* Gets the number of installed titles in the requested media type
|
* Gets the number of installed titles in the requested media type
|
||||||
@ -704,6 +711,9 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<Module> am;
|
std::shared_ptr<Module> am;
|
||||||
|
|
||||||
|
// Placed on the interface level so that only am:net and am:app have it.
|
||||||
|
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -251,7 +251,7 @@ void Module::Interface::GetTransferableId(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
std::array<u8, 12> buffer;
|
std::array<u8, 12> buffer;
|
||||||
const Result result =
|
const Result result =
|
||||||
cfg->GetConfigBlock(ConsoleUniqueID2BlockID, 8, AccessFlag::SystemRead, buffer.data());
|
cfg->GetConfigBlock(ConsoleUniqueID2BlockID, 8, AccessFlag::Global, buffer.data());
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
if (result.IsSuccess()) {
|
if (result.IsSuccess()) {
|
||||||
std::memcpy(&buffer[8], &app_id_salt, sizeof(u32));
|
std::memcpy(&buffer[8], &app_id_salt, sizeof(u32));
|
||||||
@ -438,11 +438,42 @@ ResultVal<void*> Module::GetConfigBlockPointer(u32 block_id, u32 size, AccessFla
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result Module::GetConfigBlock(u32 block_id, u32 size, AccessFlag accesss_flag, void* output) {
|
Result Module::GetConfigBlock(u32 block_id, u32 size, AccessFlag accesss_flag, void* output) {
|
||||||
|
bool get_from_artic =
|
||||||
|
block_id == ConsoleUniqueID2BlockID &&
|
||||||
|
(static_cast<u16>(accesss_flag) & static_cast<u16>(AccessFlag::UserRead)) != 0;
|
||||||
|
|
||||||
|
if (get_from_artic && artic_client.get()) {
|
||||||
|
auto req = artic_client->NewRequest("CFGU_GetConfigInfoBlk2");
|
||||||
|
|
||||||
|
req.AddParameterS32(block_id);
|
||||||
|
req.AddParameterU32(size);
|
||||||
|
|
||||||
|
auto resp = artic_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 buff = resp->GetResponseBuffer(0);
|
||||||
|
if (!buff.has_value())
|
||||||
|
return Result(-1);
|
||||||
|
size_t actually_read = buff->second;
|
||||||
|
if (actually_read > size)
|
||||||
|
return Result(-1);
|
||||||
|
|
||||||
|
memcpy(output, buff->first, actually_read);
|
||||||
|
return ResultSuccess;
|
||||||
|
|
||||||
|
} else {
|
||||||
void* pointer = nullptr;
|
void* pointer = nullptr;
|
||||||
CASCADE_RESULT(pointer, GetConfigBlockPointer(block_id, size, accesss_flag));
|
CASCADE_RESULT(pointer, GetConfigBlockPointer(block_id, size, accesss_flag));
|
||||||
std::memcpy(output, pointer, size);
|
std::memcpy(output, pointer, size);
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Module::SetConfigBlock(u32 block_id, u32 size, AccessFlag accesss_flag, const void* input) {
|
Result Module::SetConfigBlock(u32 block_id, u32 size, AccessFlag accesss_flag, const void* input) {
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
#include "network/artic_base/artic_base_client.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
class ArchiveBackend;
|
class ArchiveBackend;
|
||||||
@ -188,6 +189,10 @@ public:
|
|||||||
|
|
||||||
std::shared_ptr<Module> GetModule() const;
|
std::shared_ptr<Module> GetModule() const;
|
||||||
|
|
||||||
|
void UseArticClient(std::shared_ptr<Network::ArticBase::Client>& client) {
|
||||||
|
GetModule()->artic_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CFG::GetCountryCodeString service function
|
* CFG::GetCountryCodeString service function
|
||||||
* Inputs:
|
* Inputs:
|
||||||
@ -593,6 +598,8 @@ private:
|
|||||||
bool preferred_region_chosen = false;
|
bool preferred_region_chosen = false;
|
||||||
MCUData mcu_data{};
|
MCUData mcu_data{};
|
||||||
|
|
||||||
|
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
|
||||||
|
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive& ar, const unsigned int);
|
void serialize(Archive& ar, const unsigned int);
|
||||||
friend class boost::serialization::access;
|
friend class boost::serialization::access;
|
||||||
|
@ -298,18 +298,22 @@ Result ArchiveManager::DeleteSystemSaveData(u32 high, u32 low) {
|
|||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ArchiveManager::CreateSystemSaveData(u32 high, u32 low) {
|
Result ArchiveManager::CreateSystemSaveData(u32 high, u32 low, u32 total_size, u32 block_size,
|
||||||
// Construct the binary path to the archive first
|
u32 number_directories, u32 number_files,
|
||||||
const FileSys::Path path = FileSys::ConstructSystemSaveDataBinaryPath(high, low);
|
u32 number_directory_buckets, u32 number_file_buckets,
|
||||||
|
u8 duplicate_data) {
|
||||||
|
|
||||||
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
auto archive = id_code_map.find(ArchiveIdCode::SystemSaveData);
|
||||||
const std::string base_path = FileSys::GetSystemSaveDataContainerPath(nand_directory);
|
|
||||||
const std::string systemsavedata_path = FileSys::GetSystemSaveDataPath(base_path, path);
|
if (archive == id_code_map.end()) {
|
||||||
if (!FileUtil::CreateFullPath(systemsavedata_path)) {
|
return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
|
||||||
return ResultUnknown; // TODO(Subv): Find the right error code
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultSuccess;
|
auto sys_savedata = static_cast<FileSys::ArchiveFactory_SystemSaveData*>(archive->second.get());
|
||||||
|
|
||||||
|
return sys_savedata->FormatAsSysData(high, low, total_size, block_size, number_directories,
|
||||||
|
number_files, number_directory_buckets,
|
||||||
|
number_file_buckets, duplicate_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultVal<ArchiveResource> ArchiveManager::GetArchiveResource(MediaType media_type) const {
|
ResultVal<ArchiveResource> ArchiveManager::GetArchiveResource(MediaType media_type) const {
|
||||||
@ -454,6 +458,16 @@ void ArchiveManager::RegisterArticNCCH(std::shared_ptr<Network::ArticBase::Clien
|
|||||||
reinterpret_cast<FileSys::ArchiveFactory_NCCH*>(itr->second.get())->RegisterArtic(client);
|
reinterpret_cast<FileSys::ArchiveFactory_NCCH*>(itr->second.get())->RegisterArtic(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ArchiveManager::RegisterArticSystemSaveData(
|
||||||
|
std::shared_ptr<Network::ArticBase::Client>& client) {
|
||||||
|
auto itr = id_code_map.find(ArchiveIdCode::SystemSaveData);
|
||||||
|
if (itr == id_code_map.end() || itr->second.get() == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reinterpret_cast<FileSys::ArchiveFactory_SystemSaveData*>(itr->second.get())
|
||||||
|
->RegisterArtic(client);
|
||||||
|
}
|
||||||
|
|
||||||
ArchiveManager::ArchiveManager(Core::System& system) : system(system) {
|
ArchiveManager::ArchiveManager(Core::System& system) : system(system) {
|
||||||
RegisterArchiveTypes();
|
RegisterArchiveTypes();
|
||||||
}
|
}
|
||||||
|
@ -264,11 +264,12 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the SystemSaveData archive folder for the specified save data id
|
* Creates the SystemSaveData archive folder for the specified save data id
|
||||||
* @param high The high word of the SystemSaveData archive to create
|
|
||||||
* @param low The low word of the SystemSaveData archive to create
|
|
||||||
* @return Result 0 on success or the corresponding code on error
|
* @return Result 0 on success or the corresponding code on error
|
||||||
*/
|
*/
|
||||||
Result CreateSystemSaveData(u32 high, u32 low);
|
Result CreateSystemSaveData(u32 high, u32 low, u32 total_size, u32 block_size,
|
||||||
|
u32 number_directories, u32 number_files,
|
||||||
|
u32 number_directory_buckets, u32 number_file_buckets,
|
||||||
|
u8 duplicate_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns capacity and free space information about the given media type.
|
* Returns capacity and free space information about the given media type.
|
||||||
@ -296,6 +297,8 @@ public:
|
|||||||
|
|
||||||
void RegisterArticNCCH(std::shared_ptr<Network::ArticBase::Client>& client);
|
void RegisterArticNCCH(std::shared_ptr<Network::ArticBase::Client>& client);
|
||||||
|
|
||||||
|
void RegisterArticSystemSaveData(std::shared_ptr<Network::ArticBase::Client>& client);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
||||||
|
@ -1026,7 +1026,9 @@ void FS_USER::CreateSystemSaveData(Kernel::HLERequestContext& ctx) {
|
|||||||
file_buckets, duplicate);
|
file_buckets, duplicate);
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(archives.CreateSystemSaveData(savedata_high, savedata_low));
|
rb.Push(archives.CreateSystemSaveData(savedata_high, savedata_low, total_size, block_size,
|
||||||
|
directories, files, directory_buckets, file_buckets,
|
||||||
|
duplicate ? 1 : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FS_USER::CreateLegacySystemSaveData(Kernel::HLERequestContext& ctx) {
|
void FS_USER::CreateLegacySystemSaveData(Kernel::HLERequestContext& ctx) {
|
||||||
@ -1048,7 +1050,8 @@ void FS_USER::CreateLegacySystemSaveData(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
// With this command, the SystemSaveData always has save_high = 0 (Always created in the NAND)
|
// With this command, the SystemSaveData always has save_high = 0 (Always created in the NAND)
|
||||||
rb.Push(archives.CreateSystemSaveData(0, savedata_id));
|
rb.Push(archives.CreateSystemSaveData(0, savedata_id, total_size, block_size, directories,
|
||||||
|
files, directory_buckets, file_buckets, duplicate));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FS_USER::InitializeWithSdkVersion(Kernel::HLERequestContext& ctx) {
|
void FS_USER::InitializeWithSdkVersion(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -21,7 +21,10 @@
|
|||||||
#include "core/hle/kernel/process.h"
|
#include "core/hle/kernel/process.h"
|
||||||
#include "core/hle/kernel/resource_limit.h"
|
#include "core/hle/kernel/resource_limit.h"
|
||||||
#include "core/hle/service/am/am.h"
|
#include "core/hle/service/am/am.h"
|
||||||
|
#include "core/hle/service/am/am_app.h"
|
||||||
|
#include "core/hle/service/am/am_net.h"
|
||||||
#include "core/hle/service/cfg/cfg.h"
|
#include "core/hle/service/cfg/cfg.h"
|
||||||
|
#include "core/hle/service/cfg/cfg_u.h"
|
||||||
#include "core/hle/service/fs/archive.h"
|
#include "core/hle/service/fs/archive.h"
|
||||||
#include "core/hle/service/fs/fs_user.h"
|
#include "core/hle/service/fs/fs_user.h"
|
||||||
#include "core/loader/artic.h"
|
#include "core/loader/artic.h"
|
||||||
@ -335,9 +338,28 @@ ResultStatus Apploader_Artic::Load(std::shared_ptr<Kernel::Process>& process) {
|
|||||||
system.ArchiveManager().RegisterArticSaveDataSource(client);
|
system.ArchiveManager().RegisterArticSaveDataSource(client);
|
||||||
system.ArchiveManager().RegisterArticExtData(client);
|
system.ArchiveManager().RegisterArticExtData(client);
|
||||||
system.ArchiveManager().RegisterArticNCCH(client);
|
system.ArchiveManager().RegisterArticNCCH(client);
|
||||||
|
system.ArchiveManager().RegisterArticSystemSaveData(client);
|
||||||
|
|
||||||
auto fs_user = system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
|
auto fs_user = system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
|
||||||
fs_user->RegisterSecureValueBackend(std::make_shared<FileSys::ArticSecureValueBackend>(client));
|
if (fs_user.get()) {
|
||||||
|
fs_user->RegisterSecureValueBackend(
|
||||||
|
std::make_shared<FileSys::ArticSecureValueBackend>(client));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cfg = system.ServiceManager().GetService<Service::CFG::CFG_U>("cfg:u");
|
||||||
|
if (cfg.get()) {
|
||||||
|
cfg->UseArticClient(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto amnet = system.ServiceManager().GetService<Service::AM::AM_NET>("am:net");
|
||||||
|
if (amnet.get()) {
|
||||||
|
amnet->UseArticClient(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto amapp = system.ServiceManager().GetService<Service::AM::AM_APP>("am:app");
|
||||||
|
if (amapp.get()) {
|
||||||
|
amapp->UseArticClient(client);
|
||||||
|
}
|
||||||
|
|
||||||
ParseRegionLockoutInfo(ncch_program_id);
|
ParseRegionLockoutInfo(ncch_program_id);
|
||||||
|
|
||||||
|
@ -21,8 +21,9 @@ public:
|
|||||||
Apploader_Artic(Core::System& system_, const std::string& server_addr, u16 server_port)
|
Apploader_Artic(Core::System& system_, const std::string& server_addr, u16 server_port)
|
||||||
: AppLoader(system_, FileUtil::IOFile()) {
|
: AppLoader(system_, FileUtil::IOFile()) {
|
||||||
client = std::make_shared<Network::ArticBase::Client>(server_addr, server_port);
|
client = std::make_shared<Network::ArticBase::Client>(server_addr, server_port);
|
||||||
client->SetCommunicationErrorCallback([&system_]() {
|
client->SetCommunicationErrorCallback([&system_](const std::string& msg) {
|
||||||
system_.SetStatus(Core::System::ResultStatus::ErrorArticDisconnected);
|
system_.SetStatus(Core::System::ResultStatus::ErrorArticDisconnected,
|
||||||
|
msg.empty() ? nullptr : msg.c_str());
|
||||||
});
|
});
|
||||||
client->SetArticReportTrafficCallback(
|
client->SetArticReportTrafficCallback(
|
||||||
[&system_](u32 bytes) { system_.ReportArticTraffic(bytes); });
|
[&system_](u32 bytes) { system_.ReportArticTraffic(bytes); });
|
||||||
|
@ -32,6 +32,7 @@ public:
|
|||||||
ARTIC_EXT_DATA = (1 << 1),
|
ARTIC_EXT_DATA = (1 << 1),
|
||||||
ARTIC_BOSS_EXT_DATA = (1 << 2),
|
ARTIC_BOSS_EXT_DATA = (1 << 2),
|
||||||
ARTIC_SHARED_EXT_DATA = (1 << 3),
|
ARTIC_SHARED_EXT_DATA = (1 << 3),
|
||||||
|
ARTIC_SYSTEM_SAVE_DATA = (1 << 4),
|
||||||
};
|
};
|
||||||
union PerfArticEvents {
|
union PerfArticEvents {
|
||||||
u32 raw{};
|
u32 raw{};
|
||||||
|
@ -143,7 +143,14 @@ void ConfigurePerGame::LoadConfiguration() {
|
|||||||
ui->display_title_id->setText(
|
ui->display_title_id->setText(
|
||||||
QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper());
|
QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper());
|
||||||
|
|
||||||
const auto loader = Loader::GetLoader(filename);
|
std::unique_ptr<Loader::AppLoader> loader_ptr;
|
||||||
|
Loader::AppLoader* loader;
|
||||||
|
if (system.IsPoweredOn()) {
|
||||||
|
loader = &system.GetAppLoader();
|
||||||
|
} else {
|
||||||
|
loader_ptr = Loader::GetLoader(filename);
|
||||||
|
loader = loader_ptr.get();
|
||||||
|
}
|
||||||
|
|
||||||
std::string title;
|
std::string title;
|
||||||
if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
|
if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
|
||||||
|
@ -1242,7 +1242,10 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
|||||||
case Core::System::ResultStatus::ErrorArticDisconnected:
|
case Core::System::ResultStatus::ErrorArticDisconnected:
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
this, tr("Artic Base Server"),
|
this, tr("Artic Base Server"),
|
||||||
tr("An error has occurred whilst communicating with the Artic Base Server."));
|
tr(fmt::format(
|
||||||
|
"An error has occurred whilst communicating with the Artic Base Server.\n{}",
|
||||||
|
system.GetStatusDetails())
|
||||||
|
.c_str()));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
@ -1271,6 +1274,10 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::BootGame(const QString& filename) {
|
void GMainWindow::BootGame(const QString& filename) {
|
||||||
|
if (emu_thread) {
|
||||||
|
ShutdownGame();
|
||||||
|
}
|
||||||
|
|
||||||
const bool is_artic = filename.startsWith(QString::fromStdString("articbase://"));
|
const bool is_artic = filename.startsWith(QString::fromStdString("articbase://"));
|
||||||
|
|
||||||
if (!is_artic && filename.endsWith(QStringLiteral(".cia"))) {
|
if (!is_artic && filename.endsWith(QStringLiteral(".cia"))) {
|
||||||
@ -2924,10 +2931,12 @@ void GMainWindow::UpdateStatusBar() {
|
|||||||
const bool do_mb = results.artic_transmitted >= (1000.0 * 1000.0);
|
const bool do_mb = results.artic_transmitted >= (1000.0 * 1000.0);
|
||||||
const double value = do_mb ? (results.artic_transmitted / (1000.0 * 1000.0))
|
const double value = do_mb ? (results.artic_transmitted / (1000.0 * 1000.0))
|
||||||
: (results.artic_transmitted / 1000.0);
|
: (results.artic_transmitted / 1000.0);
|
||||||
static const std::array<std::pair<Core::PerfStats::PerfArticEventBits, QString>, 4>
|
static const std::array<std::pair<Core::PerfStats::PerfArticEventBits, QString>, 5>
|
||||||
perf_events = {
|
perf_events = {
|
||||||
std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_SHARED_EXT_DATA,
|
std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_SHARED_EXT_DATA,
|
||||||
tr("(Accessing SharedExtData)")),
|
tr("(Accessing SharedExtData)")),
|
||||||
|
std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_SYSTEM_SAVE_DATA,
|
||||||
|
tr("(Accessing SystemSaveData)")),
|
||||||
std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_BOSS_EXT_DATA,
|
std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_BOSS_EXT_DATA,
|
||||||
tr("(Accessing BossExtData)")),
|
tr("(Accessing BossExtData)")),
|
||||||
std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_EXT_DATA,
|
std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_EXT_DATA,
|
||||||
@ -3152,7 +3161,9 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
|
|||||||
error_severity_icon = QMessageBox::Icon::Warning;
|
error_severity_icon = QMessageBox::Icon::Warning;
|
||||||
} else if (result == Core::System::ResultStatus::ErrorArticDisconnected) {
|
} else if (result == Core::System::ResultStatus::ErrorArticDisconnected) {
|
||||||
title = tr("Artic Base Server");
|
title = tr("Artic Base Server");
|
||||||
message = tr("A communication error has occurred. The game will quit.");
|
message =
|
||||||
|
tr(fmt::format("A communication error has occurred. The game will quit.\n{}", details)
|
||||||
|
.c_str());
|
||||||
error_severity_icon = QMessageBox::Icon::Critical;
|
error_severity_icon = QMessageBox::Icon::Critical;
|
||||||
can_continue = false;
|
can_continue = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -197,7 +197,8 @@ bool Client::Connect() {
|
|||||||
shutdown(main_socket, SHUT_RDWR);
|
shutdown(main_socket, SHUT_RDWR);
|
||||||
closesocket(main_socket);
|
closesocket(main_socket);
|
||||||
LOG_ERROR(Network, "Incompatible server version: {}", version_value);
|
LOG_ERROR(Network, "Incompatible server version: {}", version_value);
|
||||||
SignalCommunicationError();
|
SignalCommunicationError("\nIncompatible Artic Base Server version.\nCheck for updates "
|
||||||
|
"to Artic Base Server or Citra.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -369,11 +370,11 @@ std::optional<Client::Response> Client::Send(Request& request) {
|
|||||||
return std::optional<Client::Response>(std::move(resp.response));
|
return std::optional<Client::Response>(std::move(resp.response));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::SignalCommunicationError() {
|
void Client::SignalCommunicationError(const std::string& msg) {
|
||||||
StopImpl(true);
|
StopImpl(true);
|
||||||
LOG_CRITICAL(Network, "Communication error");
|
LOG_CRITICAL(Network, "Communication error");
|
||||||
if (communication_error_callback)
|
if (communication_error_callback)
|
||||||
communication_error_callback();
|
communication_error_callback(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::PingFunction() {
|
void Client::PingFunction() {
|
||||||
|
@ -80,7 +80,7 @@ public:
|
|||||||
StopImpl(false);
|
StopImpl(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetCommunicationErrorCallback(const std::function<void()>& callback) {
|
void SetCommunicationErrorCallback(const std::function<void(const std::string&)>& callback) {
|
||||||
communication_error_callback = callback;
|
communication_error_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr const int SERVER_VERSION = 0;
|
static constexpr const int SERVER_VERSION = 1;
|
||||||
|
|
||||||
std::string address;
|
std::string address;
|
||||||
u16 port;
|
u16 port;
|
||||||
@ -109,8 +109,8 @@ private:
|
|||||||
return currRequestID++;
|
return currRequestID++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SignalCommunicationError();
|
void SignalCommunicationError(const std::string& msg = "");
|
||||||
std::function<void()> communication_error_callback;
|
std::function<void(const std::string&)> communication_error_callback;
|
||||||
|
|
||||||
std::function<void(u64)> report_artic_event_callback;
|
std::function<void(u64)> report_artic_event_callback;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user