From 4780a7134d850e86bee4b4575aa66bcf262ea83c Mon Sep 17 00:00:00 2001 From: PabloMK7 Date: Tue, 9 Jul 2024 23:35:29 +0200 Subject: [PATCH] Artic Base: Implement DLC support and other fixes (#173) * Artic Base: Implement DLC support and other fixes * Fix per game settings not working with artic loader * Fix compilation error --- .../configuration/configure_per_game.cpp | 9 +- src/citra_qt/main.cpp | 17 +- src/core/file_sys/archive_systemsavedata.cpp | 81 +- src/core/file_sys/archive_systemsavedata.h | 23 +- src/core/hle/service/am/am.cpp | 947 ++++++++++++++---- src/core/hle/service/am/am.h | 10 + src/core/hle/service/cfg/cfg.cpp | 41 +- src/core/hle/service/cfg/cfg.h | 7 + src/core/hle/service/fs/archive.cpp | 32 +- src/core/hle/service/fs/archive.h | 9 +- src/core/hle/service/fs/fs_user.cpp | 7 +- src/core/loader/artic.cpp | 24 +- src/core/loader/artic.h | 5 +- src/core/perf_stats.h | 1 + src/network/artic_base/artic_base_client.cpp | 7 +- src/network/artic_base/artic_base_client.h | 8 +- 16 files changed, 992 insertions(+), 236 deletions(-) diff --git a/src/citra_qt/configuration/configure_per_game.cpp b/src/citra_qt/configuration/configure_per_game.cpp index c11847a0d..f9c249911 100644 --- a/src/citra_qt/configuration/configure_per_game.cpp +++ b/src/citra_qt/configuration/configure_per_game.cpp @@ -151,7 +151,14 @@ void ConfigurePerGame::LoadConfiguration() { ui->display_title_id->setText( QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper()); - const auto loader = Loader::GetLoader(filename); + std::unique_ptr loader_ptr; + Loader::AppLoader* loader; + if (system.IsPoweredOn()) { + loader = &system.GetAppLoader(); + } else { + loader_ptr = Loader::GetLoader(filename); + loader = loader_ptr.get(); + } std::string title; if (loader->ReadTitle(title) == Loader::ResultStatus::Success) diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 5d3d395a2..0b5b16695 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -1216,7 +1216,10 @@ bool GMainWindow::LoadROM(const QString& filename) { case Core::System::ResultStatus::ErrorArticDisconnected: QMessageBox::critical( 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; default: QMessageBox::critical( @@ -1238,6 +1241,10 @@ bool GMainWindow::LoadROM(const QString& filename) { } void GMainWindow::BootGame(const QString& filename) { + if (emu_thread) { + ShutdownGame(); + } + const bool is_artic = filename.startsWith(QString::fromStdString("articbase://")); if (!is_artic && filename.endsWith(QStringLiteral(".cia"))) { @@ -2640,10 +2647,12 @@ void GMainWindow::UpdateStatusBar() { const bool 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); - static const std::array, 4> + static const std::array, 5> perf_events = { std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_SHARED_EXT_DATA, 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, tr("(Accessing BossExtData)")), std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_EXT_DATA, @@ -2868,7 +2877,9 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det error_severity_icon = QMessageBox::Icon::Warning; } else if (result == Core::System::ResultStatus::ErrorArticDisconnected) { 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; can_continue = false; } else { diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp index 79ae33cd7..9020666b1 100644 --- a/src/core/file_sys/archive_systemsavedata.cpp +++ b/src/core/file_sys/archive_systemsavedata.cpp @@ -10,6 +10,7 @@ #include "common/archives.h" #include "common/common_types.h" #include "common/file_util.h" +#include "core/file_sys/archive_artic.h" #include "core/file_sys/archive_systemsavedata.h" #include "core/file_sys/errors.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) : 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 path_data = path.AsBinary(); + return path_data.size() == 8 && + (*reinterpret_cast(path_data.data() + 4) & APP_SYSTEM_SAVE_DATA_MASK) != 0; + } + return false; +} + ResultVal> ArchiveFactory_SystemSaveData::Open(const Path& path, u64 program_id) { - std::string fullpath = GetSystemSaveDataPath(base_path, path); - if (!FileUtil::Exists(fullpath)) { - // TODO(Subv): Check error code, this one is probably wrong - return ResultNotFound; + 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); + if (!FileUtil::Exists(fullpath)) { + // TODO(Subv): Check error code, this one is probably wrong + return ResultNotFound; + } + return std::make_unique(fullpath); } - return std::make_unique(fullpath); } Result ArchiveFactory_SystemSaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info, u64 program_id, u32 directory_buckets, u32 file_buckets) { - std::string fullpath = GetSystemSaveDataPath(base_path, path); - FileUtil::DeleteDirRecursively(fullpath); - FileUtil::CreateFullPath(fullpath); - return ResultSuccess; + const std::vector vec_data = path.AsBinary(); + u32 save_low; + u32 save_high; + 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 ArchiveFactory_SystemSaveData::GetFormatInfo(const Path& path, @@ -79,4 +101,45 @@ ResultVal ArchiveFactory_SystemSaveData::GetFormatInfo(const 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(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 diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h index 5cb109122..2c86f51e6 100644 --- a/src/core/file_sys/archive_systemsavedata.h +++ b/src/core/file_sys/archive_systemsavedata.h @@ -10,12 +10,15 @@ #include #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 { /// File system interface to the SystemSaveData archive -class ArchiveFactory_SystemSaveData final : public ArchiveFactory { +class ArchiveFactory_SystemSaveData final : public ArchiveFactory, public ArticCacheProvider { public: explicit ArchiveFactory_SystemSaveData(const std::string& mount_point); @@ -24,13 +27,31 @@ public: u32 directory_buckets, u32 file_buckets) override; ResultVal 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 { return "SystemSaveData"; } + bool IsSlow() override { + return IsUsingArtic(); + } + + void RegisterArtic(std::shared_ptr& client) { + artic_client = client; + } + + bool IsUsingArtic() const { + return artic_client.get() != nullptr; + } + private: std::string base_path; + std::shared_ptr artic_client = nullptr; + ArchiveFactory_SystemSaveData() = default; template void serialize(Archive& ar, const unsigned int) { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index cd3f49651..aee587734 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -804,76 +804,181 @@ Module::Interface::~Interface() = default; void Module::Interface::GetNumPrograms(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - u32 media_type = rp.Pop(); + u8 media_type = rp.Pop(); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); - rb.Push(ResultSuccess); - rb.Push(static_cast(am->am_title_list[media_type].size())); + if (artic_client.get()) { + struct AsyncData { + u8 media_type; + + ResultVal res; + }; + auto async_data = std::make_shared(); + 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(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( + static_cast(async_data->res.Succeeded() ? async_data->res.Unwrap() : 0)); + }, + true); + } else { + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(ResultSuccess); + rb.Push(static_cast(am->am_title_list[media_type].size())); + } } void Module::Interface::FindDLCContentInfos(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx); + IPC::RequestParser rp(ctx); auto media_type = static_cast(rp.Pop()); u64 title_id = rp.Pop(); u32 content_count = rp.Pop(); auto& content_requested_in = rp.PopMappedBuffer(); - auto& content_info_out = rp.PopMappedBuffer(); + if (artic_client.get()) { + struct AsyncData { + u8 media_type; + u64 title_id; + std::vector content_requested; - // Validate that only DLC TIDs are passed in - u32 tid_high = static_cast(title_id >> 32); - if (tid_high != TID_HIGH_DLC) { - IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); - rb.Push(Result(ErrCodes::InvalidTIDInList, ErrorModule::AM, ErrorSummary::InvalidArgument, - ErrorLevel::Usage)); - rb.PushMappedBuffer(content_requested_in); - rb.PushMappedBuffer(content_info_out); - return; - } + Result res{0}; + std::vector out; + Kernel::MappedBuffer* content_info_out; + }; + auto async_data = std::make_shared(); + async_data->media_type = static_cast(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(); - std::vector content_requested(content_count); - content_requested_in.Read(content_requested.data(), 0, content_count * sizeof(u16)); + ctx.RunAsync( + [this, async_data](Kernel::HLERequestContext& ctx) { + auto req = artic_client->NewRequest("AMAPP_FindDLCContentInfos"); - std::string tmd_path = GetTitleMetadataPath(media_type, title_id); + 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)); - FileSys::TitleMetadata tmd; - if (tmd.Load(tmd_path) == Loader::ResultStatus::Success) { - std::size_t write_offset = 0; - // Get info for each content index requested - for (std::size_t i = 0; i < content_count; i++) { - if (content_requested[i] >= tmd.GetContentCount()) { - LOG_ERROR(Service_AM, - "Attempted to get info for non-existent content index {:04x}.", - content_requested[i]); + auto resp = artic_client->Send(req); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); - rb.Push(-1); // TODO(Steveice10): Find the right error code - rb.PushMappedBuffer(content_requested_in); - rb.PushMappedBuffer(content_info_out); - return; - } + if (!resp.has_value() || !resp->Succeeded()) { + async_data->res = Result(-1); + return 0; + } - ContentInfo content_info = {}; - content_info.index = content_requested[i]; - content_info.type = tmd.GetContentTypeByIndex(content_requested[i]); - content_info.content_id = tmd.GetContentIDByIndex(content_requested[i]); - content_info.size = tmd.GetContentSizeByIndex(content_requested[i]); - content_info.ownership = - OWNERSHIP_OWNED; // TODO(Steveice10): Pull this from the ticket. + auto res = Result(static_cast(resp->GetMethodResult())); + if (res.IsError()) { + async_data->res = res; + return 0; + } - if (FileUtil::Exists(GetTitleContentPath(media_type, title_id, content_requested[i]))) { - content_info.ownership |= OWNERSHIP_DOWNLOADED; - } + auto content_info = resp->GetResponseBuffer(0); + if (!content_info.has_value()) { + async_data->res = Result(-1); + return 0; + } - content_info_out.Write(&content_info, write_offset, sizeof(ContentInfo)); - write_offset += sizeof(ContentInfo); + 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(); + + // Validate that only DLC TIDs are passed in + u32 tid_high = static_cast(title_id >> 32); + if (tid_high != TID_HIGH_DLC) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(Result(ErrCodes::InvalidTIDInList, ErrorModule::AM, + ErrorSummary::InvalidArgument, ErrorLevel::Usage)); + return; } - } - IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); - rb.Push(ResultSuccess); - rb.PushMappedBuffer(content_requested_in); - rb.PushMappedBuffer(content_info_out); + std::vector content_requested(content_count); + content_requested_in.Read(content_requested.data(), 0, content_count * sizeof(u16)); + + std::string tmd_path = GetTitleMetadataPath(media_type, title_id); + + FileSys::TitleMetadata tmd; + if (tmd.Load(tmd_path) == Loader::ResultStatus::Success) { + std::size_t write_offset = 0; + // Get info for each content index requested + for (std::size_t i = 0; i < content_count; i++) { + if (content_requested[i] >= tmd.GetContentCount()) { + LOG_ERROR(Service_AM, + "Attempted to get info for non-existent content index {:04x}.", + content_requested[i]); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(-1); // TODO(Steveice10): Find the right error code + return; + } + + ContentInfo content_info = {}; + content_info.index = content_requested[i]; + content_info.type = tmd.GetContentTypeByIndex(content_requested[i]); + content_info.content_id = tmd.GetContentIDByIndex(content_requested[i]); + content_info.size = tmd.GetContentSizeByIndex(content_requested[i]); + content_info.ownership = + OWNERSHIP_OWNED; // TODO(Steveice10): Pull this from the ticket. + + if (FileUtil::Exists( + GetTitleContentPath(media_type, title_id, content_requested[i]))) { + content_info.ownership |= OWNERSHIP_DOWNLOADED; + } + + content_info_out.Write(&content_info, write_offset, sizeof(ContentInfo)); + write_offset += sizeof(ContentInfo); + } + } + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultSuccess); + } } void Module::Interface::ListDLCContentInfos(Kernel::HLERequestContext& ctx) { @@ -883,50 +988,112 @@ void Module::Interface::ListDLCContentInfos(Kernel::HLERequestContext& ctx) { auto media_type = static_cast(rp.Pop()); u64 title_id = rp.Pop(); u32 start_index = rp.Pop(); - auto& content_info_out = rp.PopMappedBuffer(); - // Validate that only DLC TIDs are passed in - u32 tid_high = static_cast(title_id >> 32); - if (tid_high != TID_HIGH_DLC) { - IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); - rb.Push(Result(ErrCodes::InvalidTIDInList, ErrorModule::AM, ErrorSummary::InvalidArgument, - ErrorLevel::Usage)); - rb.Push(0); - rb.PushMappedBuffer(content_info_out); - return; - } + if (artic_client.get()) { + struct AsyncData { + u8 media_type; + u64 title_id; + u32 content_count; + u32 start_index; - std::string tmd_path = GetTitleMetadataPath(media_type, title_id); + Result res{0}; + std::vector out; + Kernel::MappedBuffer* content_info_out; + }; + auto async_data = std::make_shared(); + async_data->media_type = static_cast(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(); - u32 copied = 0; - FileSys::TitleMetadata tmd; - if (tmd.Load(tmd_path) == Loader::ResultStatus::Success) { - u32 end_index = - std::min(start_index + content_count, static_cast(tmd.GetContentCount())); - std::size_t write_offset = 0; - for (u32 i = start_index; i < end_index; i++) { - ContentInfo content_info = {}; - content_info.index = static_cast(i); - content_info.type = tmd.GetContentTypeByIndex(i); - content_info.content_id = tmd.GetContentIDByIndex(i); - content_info.size = tmd.GetContentSizeByIndex(i); - content_info.ownership = - OWNERSHIP_OWNED; // TODO(Steveice10): Pull this from the ticket. + ctx.RunAsync( + [this, async_data](Kernel::HLERequestContext& ctx) { + auto req = artic_client->NewRequest("AMAPP_ListDLCContentInfos"); - if (FileUtil::Exists(GetTitleContentPath(media_type, title_id, i))) { - content_info.ownership |= OWNERSHIP_DOWNLOADED; - } + req.AddParameterU32(async_data->content_count); + req.AddParameterU8(async_data->media_type); + req.AddParameterU64(async_data->title_id); + req.AddParameterU32(async_data->start_index); - content_info_out.Write(&content_info, write_offset, sizeof(ContentInfo)); - write_offset += sizeof(ContentInfo); - copied++; + auto resp = artic_client->Send(req); + + if (!resp.has_value() || !resp->Succeeded()) { + async_data->res = Result(-1); + return 0; + } + + auto res = Result(static_cast(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(static_cast(async_data->out.size() / sizeof(ContentInfo))); + }, + true); + } else { + + auto& content_info_out = rp.PopMappedBuffer(); + + // Validate that only DLC TIDs are passed in + u32 tid_high = static_cast(title_id >> 32); + if (tid_high != TID_HIGH_DLC) { + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(Result(ErrCodes::InvalidTIDInList, ErrorModule::AM, + ErrorSummary::InvalidArgument, ErrorLevel::Usage)); + rb.Push(0); + return; } - } - IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); - rb.Push(ResultSuccess); - rb.Push(copied); - rb.PushMappedBuffer(content_info_out); + std::string tmd_path = GetTitleMetadataPath(media_type, title_id); + + u32 copied = 0; + FileSys::TitleMetadata tmd; + if (tmd.Load(tmd_path) == Loader::ResultStatus::Success) { + u32 end_index = + std::min(start_index + content_count, static_cast(tmd.GetContentCount())); + std::size_t write_offset = 0; + for (u32 i = start_index; i < end_index; i++) { + ContentInfo content_info = {}; + content_info.index = static_cast(i); + content_info.type = tmd.GetContentTypeByIndex(i); + content_info.content_id = tmd.GetContentIDByIndex(i); + content_info.size = tmd.GetContentSizeByIndex(i); + content_info.ownership = + OWNERSHIP_OWNED; // TODO(Steveice10): Pull this from the ticket. + + if (FileUtil::Exists(GetTitleContentPath(media_type, title_id, i))) { + content_info.ownership |= OWNERSHIP_DOWNLOADED; + } + + content_info_out.Write(&content_info, write_offset, sizeof(ContentInfo)); + write_offset += sizeof(ContentInfo); + copied++; + } + } + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(ResultSuccess); + rb.Push(copied); + } } void Module::Interface::DeleteContents(Kernel::HLERequestContext& ctx) { @@ -945,28 +1112,89 @@ void Module::Interface::DeleteContents(Kernel::HLERequestContext& ctx) { void Module::Interface::GetProgramList(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - u32 count = rp.Pop(); u8 media_type = rp.Pop(); - auto& title_ids_output = rp.PopMappedBuffer(); - if (media_type > 2) { - IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); - rb.Push(-1); // TODO(shinyquagsire23): Find the right error code - rb.Push(0); - rb.PushMappedBuffer(title_ids_output); - return; + if (artic_client.get()) { + struct AsyncData { + u32 count; + u8 media_type; + + Result res{0}; + std::vector out; + Kernel::MappedBuffer* title_ids_output; + }; + auto async_data = std::make_shared(); + 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(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(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(static_cast(async_data->out.size() / sizeof(u64))); + } + }, + true); + + } else { + auto& title_ids_output = rp.PopMappedBuffer(); + + if (media_type > 2) { + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(-1); // TODO(shinyquagsire23): Find the right error code + rb.Push(0); + return; + } + + u32 media_count = static_cast(am->am_title_list[media_type].size()); + u32 copied = std::min(media_count, count); + + title_ids_output.Write(am->am_title_list[media_type].data(), 0, copied * sizeof(u64)); + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(ResultSuccess); + rb.Push(copied); } - - u32 media_count = static_cast(am->am_title_list[media_type].size()); - u32 copied = std::min(media_count, count); - - title_ids_output.Write(am->am_title_list[media_type].data(), 0, copied * sizeof(u64)); - - IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); - rb.Push(ResultSuccess); - rb.Push(copied); - rb.PushMappedBuffer(title_ids_output); } Result GetTitleInfoFromList(std::span title_id_list, Service::FS::MediaType media_type, @@ -996,27 +1224,111 @@ Result GetTitleInfoFromList(std::span title_id_list, Service::FS::Med return ResultSuccess; } -void Module::Interface::GetProgramInfos(Kernel::HLERequestContext& ctx) { +void Module::Interface::GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool ignore_platform) { IPC::RequestParser rp(ctx); auto media_type = static_cast(rp.Pop()); u32 title_count = rp.Pop(); - auto& title_id_list_buffer = rp.PopMappedBuffer(); - auto& title_info_out = rp.PopMappedBuffer(); - std::vector title_id_list(title_count); - title_id_list_buffer.Read(title_id_list.data(), 0, title_count * sizeof(u64)); + if (artic_client.get()) { + struct AsyncData { + u8 media_type; + bool ignore_platform; + std::vector title_id_list; - Result result = GetTitleInfoFromList(title_id_list, media_type, title_info_out); + Result res{0}; + std::vector out; + Kernel::MappedBuffer* title_id_list_buffer; + Kernel::MappedBuffer* title_info_out; + }; + auto async_data = std::make_shared(); + async_data->media_type = static_cast(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(); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); - rb.Push(result); - rb.PushMappedBuffer(title_id_list_buffer); - rb.PushMappedBuffer(title_info_out); + 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(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_info_out = rp.PopMappedBuffer(); + + std::vector title_id_list(title_count); + title_id_list_buffer.Read(title_id_list.data(), 0, title_count * sizeof(u64)); + + Result result = GetTitleInfoFromList(title_id_list, media_type, title_info_out); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, ignore_platform ? 0 : 4); + rb.Push(result); + if (!ignore_platform) { + rb.PushMappedBuffer(title_id_list_buffer); + rb.PushMappedBuffer(title_info_out); + } + } +} + +void Module::Interface::GetProgramInfos(Kernel::HLERequestContext& ctx) { + GetProgramInfosImpl(ctx, false); } void Module::Interface::GetProgramInfosIgnorePlatform(Kernel::HLERequestContext& ctx) { - GetProgramInfos(ctx); + GetProgramInfosImpl(ctx, true); } void Module::Interface::DeleteUserProgram(Kernel::HLERequestContext& ctx) { @@ -1078,32 +1390,102 @@ void Module::Interface::GetDLCTitleInfos(Kernel::HLERequestContext& ctx) { auto media_type = static_cast(rp.Pop()); u32 title_count = rp.Pop(); - auto& title_id_list_buffer = rp.PopMappedBuffer(); - auto& title_info_out = rp.PopMappedBuffer(); - std::vector title_id_list(title_count); - title_id_list_buffer.Read(title_id_list.data(), 0, title_count * sizeof(u64)); + if (artic_client.get()) { + struct AsyncData { + u8 media_type; + std::vector title_id_list; - Result result = ResultSuccess; + Result res{0}; + std::vector out; + Kernel::MappedBuffer* title_id_list_buffer; + Kernel::MappedBuffer* title_info_out; + }; + auto async_data = std::make_shared(); + async_data->media_type = static_cast(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(); - // Validate that DLC TIDs were passed in - for (u32 i = 0; i < title_count; i++) { - u32 tid_high = static_cast(title_id_list[i] >> 32); - if (tid_high != TID_HIGH_DLC) { - result = Result(ErrCodes::InvalidTIDInList, ErrorModule::AM, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); - break; + 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(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_info_out = rp.PopMappedBuffer(); + + std::vector title_id_list(title_count); + title_id_list_buffer.Read(title_id_list.data(), 0, title_count * sizeof(u64)); + + Result result = ResultSuccess; + + // Validate that DLC TIDs were passed in + for (u32 i = 0; i < title_count; i++) { + u32 tid_high = static_cast(title_id_list[i] >> 32); + if (tid_high != TID_HIGH_DLC) { + result = Result(ErrCodes::InvalidTIDInList, ErrorModule::AM, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); + break; + } } - } - if (result.IsSuccess()) { - result = GetTitleInfoFromList(title_id_list, media_type, title_info_out); - } + if (result.IsSuccess()) { + result = GetTitleInfoFromList(title_id_list, media_type, title_info_out); + } - IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); - rb.Push(result); - rb.PushMappedBuffer(title_id_list_buffer); - rb.PushMappedBuffer(title_info_out); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); + rb.Push(result); + rb.PushMappedBuffer(title_id_list_buffer); + rb.PushMappedBuffer(title_info_out); + } } void Module::Interface::GetPatchTitleInfos(Kernel::HLERequestContext& ctx) { @@ -1111,32 +1493,102 @@ void Module::Interface::GetPatchTitleInfos(Kernel::HLERequestContext& ctx) { auto media_type = static_cast(rp.Pop()); u32 title_count = rp.Pop(); - auto& title_id_list_buffer = rp.PopMappedBuffer(); - auto& title_info_out = rp.PopMappedBuffer(); - std::vector title_id_list(title_count); - title_id_list_buffer.Read(title_id_list.data(), 0, title_count * sizeof(u64)); + if (artic_client.get()) { + struct AsyncData { + u8 media_type; + std::vector title_id_list; - Result result = ResultSuccess; + Result res{0}; + std::vector out; + Kernel::MappedBuffer* title_id_list_buffer; + Kernel::MappedBuffer* title_info_out; + }; + auto async_data = std::make_shared(); + async_data->media_type = static_cast(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(); - // Validate that update TIDs were passed in - for (u32 i = 0; i < title_count; i++) { - u32 tid_high = static_cast(title_id_list[i] >> 32); - if (tid_high != TID_HIGH_UPDATE) { - result = Result(ErrCodes::InvalidTIDInList, ErrorModule::AM, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); - break; + 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(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_info_out = rp.PopMappedBuffer(); + + std::vector title_id_list(title_count); + title_id_list_buffer.Read(title_id_list.data(), 0, title_count * sizeof(u64)); + + Result result = ResultSuccess; + + // Validate that update TIDs were passed in + for (u32 i = 0; i < title_count; i++) { + u32 tid_high = static_cast(title_id_list[i] >> 32); + if (tid_high != TID_HIGH_UPDATE) { + result = Result(ErrCodes::InvalidTIDInList, ErrorModule::AM, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); + break; + } } - } - if (result.IsSuccess()) { - result = GetTitleInfoFromList(title_id_list, media_type, title_info_out); - } + if (result.IsSuccess()) { + result = GetTitleInfoFromList(title_id_list, media_type, title_info_out); + } - IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); - rb.Push(result); - rb.PushMappedBuffer(title_id_list_buffer); - rb.PushMappedBuffer(title_info_out); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); + rb.Push(result); + rb.PushMappedBuffer(title_id_list_buffer); + rb.PushMappedBuffer(title_info_out); + } } void Module::Interface::ListDataTitleTicketInfos(Kernel::HLERequestContext& ctx) { @@ -1144,56 +1596,165 @@ void Module::Interface::ListDataTitleTicketInfos(Kernel::HLERequestContext& ctx) u32 ticket_count = rp.Pop(); u64 title_id = rp.Pop(); u32 start_index = rp.Pop(); - auto& ticket_info_out = rp.PopMappedBuffer(); + if (artic_client.get()) { + struct AsyncData { + u64 title_id; + u32 ticket_count; + u32 start_index; - std::size_t write_offset = 0; - for (u32 i = 0; i < ticket_count; i++) { - TicketInfo ticket_info = {}; - ticket_info.title_id = title_id; - ticket_info.version = 0; // TODO - ticket_info.size = 0; // TODO + Result res{0}; + std::vector out; + Kernel::MappedBuffer* ticket_info_out; + }; + auto async_data = std::make_shared(); + 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(); - ticket_info_out.Write(&ticket_info, write_offset, sizeof(TicketInfo)); - write_offset += sizeof(TicketInfo); + 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(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(static_cast(async_data->out.size() / sizeof(TicketInfo))); + }, + true); + } else { + auto& ticket_info_out = rp.PopMappedBuffer(); + + std::size_t write_offset = 0; + for (u32 i = 0; i < ticket_count; i++) { + TicketInfo ticket_info = {}; + ticket_info.title_id = title_id; + ticket_info.version = 0; // TODO + ticket_info.size = 0; // TODO + + ticket_info_out.Write(&ticket_info, write_offset, sizeof(TicketInfo)); + write_offset += sizeof(TicketInfo); + } + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); + rb.Push(ResultSuccess); + rb.Push(ticket_count); + rb.PushMappedBuffer(ticket_info_out); + + LOG_WARNING(Service_AM, + "(STUBBED) ticket_count=0x{:08X}, title_id=0x{:016x}, start_index=0x{:08X}", + ticket_count, title_id, start_index); } - - IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); - rb.Push(ResultSuccess); - rb.Push(ticket_count); - rb.PushMappedBuffer(ticket_info_out); - - LOG_WARNING(Service_AM, - "(STUBBED) ticket_count=0x{:08X}, title_id=0x{:016x}, start_index=0x{:08X}", - ticket_count, title_id, start_index); } void Module::Interface::GetDLCContentInfoCount(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); auto media_type = static_cast(rp.Pop()); u64 title_id = rp.Pop(); + if (artic_client.get()) { + struct AsyncData { + u8 media_type; + u64 title_id; - // Validate that only DLC TIDs are passed in - u32 tid_high = static_cast(title_id >> 32); - if (tid_high != TID_HIGH_DLC) { - IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); - rb.Push(Result(ErrCodes::InvalidTID, ErrorModule::AM, ErrorSummary::InvalidArgument, - ErrorLevel::Usage)); - rb.Push(0); - return; - } + ResultVal res; + }; + auto async_data = std::make_shared(); + async_data->media_type = static_cast(media_type); + async_data->title_id = title_id; - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); - rb.Push(ResultSuccess); // No error + ctx.RunAsync( + [this, async_data](Kernel::HLERequestContext& ctx) { + auto req = artic_client->NewRequest("AMAPP_GetDLCContentInfoCount"); - std::string tmd_path = GetTitleMetadataPath(media_type, title_id); + req.AddParameterU8(async_data->media_type); + req.AddParameterU64(async_data->title_id); - FileSys::TitleMetadata tmd; - if (tmd.Load(tmd_path) == Loader::ResultStatus::Success) { - rb.Push(static_cast(tmd.GetContentCount())); + auto resp = artic_client->Send(req); + + if (!resp.has_value() || !resp->Succeeded()) { + async_data->res = Result(-1); + return 0; + } + + auto res = Result(static_cast(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( + static_cast(async_data->res.Succeeded() ? async_data->res.Unwrap() : 0)); + }, + true); } else { - rb.Push(1); // Number of content infos plus one - LOG_WARNING(Service_AM, "(STUBBED) called media_type={}, title_id=0x{:016x}", media_type, - title_id); + + // Validate that only DLC TIDs are passed in + u32 tid_high = static_cast(title_id >> 32); + if (tid_high != TID_HIGH_DLC) { + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(Result(ErrCodes::InvalidTID, ErrorModule::AM, ErrorSummary::InvalidArgument, + ErrorLevel::Usage)); + rb.Push(0); + return; + } + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(ResultSuccess); // No error + + std::string tmd_path = GetTitleMetadataPath(media_type, title_id); + + FileSys::TitleMetadata tmd; + if (tmd.Load(tmd_path) == Loader::ResultStatus::Success) { + rb.Push(static_cast(tmd.GetContentCount())); + } else { + rb.Push(1); // Number of content infos plus one + LOG_WARNING(Service_AM, "(STUBBED) called media_type={}, title_id=0x{:016x}", + media_type, title_id); + } } } diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 7ef96f985..c56fa0888 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -20,6 +20,7 @@ #include "core/hle/kernel/mutex.h" #include "core/hle/result.h" #include "core/hle/service/service.h" +#include "network/artic_base/artic_base_client.h" namespace Core { class System; @@ -245,7 +246,13 @@ public: return am; } + void UseArticClient(std::shared_ptr& client) { + artic_client = client; + } + protected: + void GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool ignore_platform); + /** * AM::GetNumPrograms service function * Gets the number of installed titles in the requested media type @@ -753,6 +760,9 @@ public: protected: std::shared_ptr am; + + // Placed on the interface level so that only am:net and am:app have it. + std::shared_ptr artic_client = nullptr; }; /** diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 1fa50e1df..0eef32aca 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -278,7 +278,7 @@ void Module::Interface::GetTransferableId(Kernel::HLERequestContext& ctx) { std::array buffer; const Result result = - cfg->GetConfigBlock(ConsoleUniqueID2BlockID, 8, AccessFlag::SystemRead, buffer.data()); + cfg->GetConfigBlock(ConsoleUniqueID2BlockID, 8, AccessFlag::Global, buffer.data()); rb.Push(result); if (result.IsSuccess()) { std::memcpy(&buffer[8], &app_id_salt, sizeof(u32)); @@ -502,11 +502,42 @@ ResultVal Module::GetConfigBlockPointer(u32 block_id, u32 size, AccessFla } Result Module::GetConfigBlock(u32 block_id, u32 size, AccessFlag accesss_flag, void* output) { - void* pointer = nullptr; - CASCADE_RESULT(pointer, GetConfigBlockPointer(block_id, size, accesss_flag)); - std::memcpy(output, pointer, size); + bool get_from_artic = + block_id == ConsoleUniqueID2BlockID && + (static_cast(accesss_flag) & static_cast(AccessFlag::UserRead)) != 0; - return ResultSuccess; + 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(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; + CASCADE_RESULT(pointer, GetConfigBlockPointer(block_id, size, accesss_flag)); + std::memcpy(output, pointer, size); + + return ResultSuccess; + } } Result Module::SetConfigBlock(u32 block_id, u32 size, AccessFlag accesss_flag, const void* input) { diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h index 7bd6a1310..263cd51e6 100644 --- a/src/core/hle/service/cfg/cfg.h +++ b/src/core/hle/service/cfg/cfg.h @@ -11,6 +11,7 @@ #include #include "common/common_types.h" #include "core/hle/service/service.h" +#include "network/artic_base/artic_base_client.h" namespace FileSys { class ArchiveBackend; @@ -210,6 +211,10 @@ public: std::shared_ptr GetModule() const; + void UseArticClient(std::shared_ptr& client) { + GetModule()->artic_client = client; + } + /** * CFG::GetCountryCodeString service function * Inputs: @@ -680,6 +685,8 @@ private: bool preferred_region_chosen = false; MCUData mcu_data{}; + std::shared_ptr artic_client = nullptr; + template void serialize(Archive& ar, const unsigned int); friend class boost::serialization::access; diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index f70f03c7e..2a48a741a 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -298,18 +298,22 @@ Result ArchiveManager::DeleteSystemSaveData(u32 high, u32 low) { return ResultSuccess; } -Result ArchiveManager::CreateSystemSaveData(u32 high, u32 low) { - // Construct the binary path to the archive first - const FileSys::Path path = FileSys::ConstructSystemSaveDataBinaryPath(high, low); +Result ArchiveManager::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) { - 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 + auto archive = id_code_map.find(ArchiveIdCode::SystemSaveData); + + if (archive == id_code_map.end()) { + return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error } - return ResultSuccess; + auto sys_savedata = static_cast(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 ArchiveManager::GetArchiveResource(MediaType media_type) const { @@ -454,6 +458,16 @@ void ArchiveManager::RegisterArticNCCH(std::shared_ptr(itr->second.get())->RegisterArtic(client); } +void ArchiveManager::RegisterArticSystemSaveData( + std::shared_ptr& client) { + auto itr = id_code_map.find(ArchiveIdCode::SystemSaveData); + if (itr == id_code_map.end() || itr->second.get() == nullptr) { + return; + } + reinterpret_cast(itr->second.get()) + ->RegisterArtic(client); +} + ArchiveManager::ArchiveManager(Core::System& system) : system(system) { RegisterArchiveTypes(); } diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index a20713066..c9d5df87c 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -264,11 +264,12 @@ public: /** * 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 */ - 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. @@ -296,6 +297,8 @@ public: void RegisterArticNCCH(std::shared_ptr& client); + void RegisterArticSystemSaveData(std::shared_ptr& client); + private: Core::System& system; diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index e5b4cb240..ac1c70b2d 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -1026,7 +1026,9 @@ void FS_USER::CreateSystemSaveData(Kernel::HLERequestContext& ctx) { file_buckets, duplicate); 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) { @@ -1048,7 +1050,8 @@ void FS_USER::CreateLegacySystemSaveData(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); // 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) { diff --git a/src/core/loader/artic.cpp b/src/core/loader/artic.cpp index f2e74b9a6..d4ba70060 100644 --- a/src/core/loader/artic.cpp +++ b/src/core/loader/artic.cpp @@ -21,7 +21,10 @@ #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.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_u.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/fs_user.h" #include "core/loader/artic.h" @@ -335,9 +338,28 @@ ResultStatus Apploader_Artic::Load(std::shared_ptr& process) { system.ArchiveManager().RegisterArticSaveDataSource(client); system.ArchiveManager().RegisterArticExtData(client); system.ArchiveManager().RegisterArticNCCH(client); + system.ArchiveManager().RegisterArticSystemSaveData(client); auto fs_user = system.ServiceManager().GetService("fs:USER"); - fs_user->RegisterSecureValueBackend(std::make_shared(client)); + if (fs_user.get()) { + fs_user->RegisterSecureValueBackend( + std::make_shared(client)); + } + + auto cfg = system.ServiceManager().GetService("cfg:u"); + if (cfg.get()) { + cfg->UseArticClient(client); + } + + auto amnet = system.ServiceManager().GetService("am:net"); + if (amnet.get()) { + amnet->UseArticClient(client); + } + + auto amapp = system.ServiceManager().GetService("am:app"); + if (amapp.get()) { + amapp->UseArticClient(client); + } ParseRegionLockoutInfo(ncch_program_id); diff --git a/src/core/loader/artic.h b/src/core/loader/artic.h index d51202b2e..477153f08 100644 --- a/src/core/loader/artic.h +++ b/src/core/loader/artic.h @@ -21,8 +21,9 @@ public: Apploader_Artic(Core::System& system_, const std::string& server_addr, u16 server_port) : AppLoader(system_, FileUtil::IOFile()) { client = std::make_shared(server_addr, server_port); - client->SetCommunicationErrorCallback([&system_]() { - system_.SetStatus(Core::System::ResultStatus::ErrorArticDisconnected); + client->SetCommunicationErrorCallback([&system_](const std::string& msg) { + system_.SetStatus(Core::System::ResultStatus::ErrorArticDisconnected, + msg.empty() ? nullptr : msg.c_str()); }); client->SetArticReportTrafficCallback( [&system_](u32 bytes) { system_.ReportArticTraffic(bytes); }); diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index 5cd69afb2..d68ff4dda 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -32,6 +32,7 @@ public: ARTIC_EXT_DATA = (1 << 1), ARTIC_BOSS_EXT_DATA = (1 << 2), ARTIC_SHARED_EXT_DATA = (1 << 3), + ARTIC_SYSTEM_SAVE_DATA = (1 << 4), }; union PerfArticEvents { u32 raw{}; diff --git a/src/network/artic_base/artic_base_client.cpp b/src/network/artic_base/artic_base_client.cpp index 460d8f9ee..fd5974d11 100644 --- a/src/network/artic_base/artic_base_client.cpp +++ b/src/network/artic_base/artic_base_client.cpp @@ -197,7 +197,8 @@ bool Client::Connect() { shutdown(main_socket, SHUT_RDWR); closesocket(main_socket); 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; } } else { @@ -369,11 +370,11 @@ std::optional Client::Send(Request& request) { return std::optional(std::move(resp.response)); } -void Client::SignalCommunicationError() { +void Client::SignalCommunicationError(const std::string& msg) { StopImpl(true); LOG_CRITICAL(Network, "Communication error"); if (communication_error_callback) - communication_error_callback(); + communication_error_callback(msg); } void Client::PingFunction() { diff --git a/src/network/artic_base/artic_base_client.h b/src/network/artic_base/artic_base_client.h index 4e9bb4ec8..23079f832 100644 --- a/src/network/artic_base/artic_base_client.h +++ b/src/network/artic_base/artic_base_client.h @@ -80,7 +80,7 @@ public: StopImpl(false); } - void SetCommunicationErrorCallback(const std::function& callback) { + void SetCommunicationErrorCallback(const std::function& callback) { communication_error_callback = callback; } @@ -98,7 +98,7 @@ public: } private: - static constexpr const int SERVER_VERSION = 0; + static constexpr const int SERVER_VERSION = 1; std::string address; u16 port; @@ -109,8 +109,8 @@ private: return currRequestID++; } - void SignalCommunicationError(); - std::function communication_error_callback; + void SignalCommunicationError(const std::string& msg = ""); + std::function communication_error_callback; std::function report_artic_event_callback;