From f2073217a4b074f53e1932eaf41cf08d6296b21f Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Apr 2019 18:42:36 -0400 Subject: filesystem: Add getter for BCAT temporary directory --- src/core/hle/service/filesystem/filesystem.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 14cd0e322..9cb107668 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -674,6 +674,15 @@ FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) return bis_factory->GetModificationDumpRoot(title_id); } +FileSys::VirtualDir GetBCATDirectory(u64 title_id) { + LOG_TRACE(Service_FS, "Opening BCAT root for tid={:016X}", title_id); + + if (bis_factory == nullptr) + return nullptr; + + return bis_factory->GetBCATDirectory(title_id); +} + void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { if (overwrite) { bis_factory = nullptr; -- cgit v1.2.3 From 943662dc3c59537d7c3e05b98cfc08212491ac87 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Apr 2019 18:43:10 -0400 Subject: applets: Add accessor for AppletFrontendSet Allows other services to call applets without using LLE. --- src/core/hle/service/am/applets/applets.cpp | 4 ++++ src/core/hle/service/am/applets/applets.h | 2 ++ 2 files changed, 6 insertions(+) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index d2e35362f..720fe766f 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -157,6 +157,10 @@ AppletManager::AppletManager(Core::System& system_) : system{system_} {} AppletManager::~AppletManager() = default; +const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const { + return frontend; +} + void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { if (set.parental_controls != nullptr) frontend.parental_controls = std::move(set.parental_controls); diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 764c3418c..226be88b1 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -190,6 +190,8 @@ public: explicit AppletManager(Core::System& system_); ~AppletManager(); + const AppletFrontendSet& GetAppletFrontendSet() const; + void SetAppletFrontendSet(AppletFrontendSet set); void SetDefaultAppletFrontendSet(); void SetDefaultAppletsIfMissing(); -- cgit v1.2.3 From 532ec459b8661cd5fe0bdff73758b474a54ed94c Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Apr 2019 18:44:13 -0400 Subject: nifm: Signal to applications that internet access is available --- src/core/hle/service/nifm/nifm.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 24d1813a7..756a2af57 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -12,6 +12,13 @@ namespace Service::NIFM { +enum class RequestState : u32 { + NotSubmitted = 1, + Error = 1, ///< The duplicate 1 is intentional; it means both not submitted and error on HW. + Pending = 2, + Connected = 3, +}; + class IScanRequest final : public ServiceFramework { public: explicit IScanRequest() : ServiceFramework("IScanRequest") { @@ -81,7 +88,7 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(0); + rb.PushEnum(RequestState::Connected); } void GetResult(Kernel::HLERequestContext& ctx) { @@ -189,14 +196,14 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(0); + rb.Push(1); } void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(0); + rb.Push(1); } Core::System& system; }; -- cgit v1.2.3 From 2c0b75a7448ab3878d159548858b397e1bcc305b Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Apr 2019 18:46:46 -0400 Subject: bcat: Add backend class to generify the functions of BCAT Provides the most abstract simplified functions of BCAT as functions. Also includes a NullBackend class which is just a no-op. --- src/core/hle/service/bcat/backend/backend.cpp | 47 ++++++++++++++++++++++++ src/core/hle/service/bcat/backend/backend.h | 53 +++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/core/hle/service/bcat/backend/backend.cpp create mode 100644 src/core/hle/service/bcat/backend/backend.h (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp new file mode 100644 index 000000000..aefa2208d --- /dev/null +++ b/src/core/hle/service/bcat/backend/backend.cpp @@ -0,0 +1,47 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/hex_util.h" +#include "common/logging/log.h" +#include "core/hle/service/bcat/backend/backend.h" + +namespace Service::BCAT { + +Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {} + +Backend::~Backend() = default; + +NullBackend::NullBackend(const DirectoryGetter& getter) : Backend(std::move(getter)) {} + +NullBackend::~NullBackend() = default; + +bool NullBackend::Synchronize(TitleIDVersion title, CompletionCallback callback) { + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, + title.build_id); + + callback(true); + return true; +} + +bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name, + CompletionCallback callback) { + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id, + title.build_id, name); + + callback(true); + return true; +} + +bool NullBackend::Clear(u64 title_id) { + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}"); + + return true; +} + +void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) { + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase = {}", title_id, + Common::HexArrayToString(passphrase)); +} + +} // namespace Service::BCAT diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h new file mode 100644 index 000000000..2e9511f3f --- /dev/null +++ b/src/core/hle/service/bcat/backend/backend.h @@ -0,0 +1,53 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "common/common_types.h" +#include "core/file_sys/vfs_types.h" + +namespace Service::BCAT { + +using CompletionCallback = std::function; +using DirectoryGetter = std::function; +using Passphrase = std::array; + +struct TitleIDVersion { + u64 title_id; + u64 build_id; +}; + +class Backend { +public: + explicit Backend(DirectoryGetter getter); + virtual ~Backend(); + + virtual bool Synchronize(TitleIDVersion title, CompletionCallback callback) = 0; + virtual bool SynchronizeDirectory(TitleIDVersion title, std::string name, + CompletionCallback callback) = 0; + + virtual bool Clear(u64 title_id) = 0; + + virtual void SetPassphrase(u64 title_id, const Passphrase& passphrase) = 0; + +protected: + DirectoryGetter dir_getter; +}; + +class NullBackend : public Backend { +public: + explicit NullBackend(const DirectoryGetter& getter); + ~NullBackend() override; + + bool Synchronize(TitleIDVersion title, CompletionCallback callback) override; + bool SynchronizeDirectory(TitleIDVersion title, std::string name, + CompletionCallback callback) override; + + bool Clear(u64 title_id) override; + + void SetPassphrase(u64 title_id, const Passphrase& passphrase) override; +}; + +} // namespace Service::BCAT -- cgit v1.2.3 From 2903f3524e7b9d802da4d23ae6d25d07f7eba8f5 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Apr 2019 18:47:58 -0400 Subject: bcat: Add BCAT backend for Boxcat service Downloads content from yuzu servers and unpacks it into the temporary directory provided. Fully supports all Backend features except passphrase. --- src/core/hle/service/bcat/backend/boxcat.cpp | 351 +++++++++++++++++++++++++++ src/core/hle/service/bcat/backend/boxcat.h | 56 +++++ 2 files changed, 407 insertions(+) create mode 100644 src/core/hle/service/bcat/backend/boxcat.cpp create mode 100644 src/core/hle/service/bcat/backend/boxcat.h (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp new file mode 100644 index 000000000..539140f30 --- /dev/null +++ b/src/core/hle/service/bcat/backend/boxcat.cpp @@ -0,0 +1,351 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include "common/hex_util.h" +#include "common/logging/backend.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs_libzip.h" +#include "core/file_sys/vfs_vector.h" +#include "core/frontend/applets/error.h" +#include "core/hle/lock.h" +#include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/bcat/backend/boxcat.h" +#include "core/settings.h" + +namespace Service::BCAT { + +constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org"; + +// Formatted using fmt with arg[0] = hex title id +constexpr char BOXCAT_PATHNAME_DATA[] = "/boxcat/titles/{:016X}/data"; + +constexpr char BOXCAT_PATHNAME_EVENTS[] = "/boxcat/events"; + +constexpr char BOXCAT_API_VERSION[] = "1"; + +// HTTP status codes for Boxcat +enum class ResponseStatus { + BadClientVersion = 301, ///< The Boxcat-Client-Version doesn't match the server. + NoUpdate = 304, ///< The digest provided would match the new data, no need to update. + NoMatchTitleId = 404, ///< The title ID provided doesn't have a boxcat implementation. + NoMatchBuildId = 406, ///< The build ID provided is blacklisted (potentially because of format + ///< issues or whatnot) and has no data. +}; + +enum class DownloadResult { + Success = 0, + NoResponse, + GeneralWebError, + NoMatchTitleId, + NoMatchBuildId, + InvalidContentType, + GeneralFSError, + BadClientVersion, +}; + +constexpr std::array DOWNLOAD_RESULT_LOG_MESSAGES{ + "Success", + "There was no response from the server.", + "There was a general web error code returned from the server.", + "The title ID of the current game doesn't have a boxcat implementation. If you believe an " + "implementation should be added, contact yuzu support.", + "The build ID of the current version of the game is marked as incompatible with the current " + "BCAT distribution. Try upgrading or downgrading your game version or contacting yuzu support.", + "The content type of the web response was invalid.", + "There was a general filesystem error while saving the zip file.", + "The server is either too new or too old to serve the request. Try using the latest version of " + "an official release of yuzu.", +}; + +std::ostream& operator<<(std::ostream& os, DownloadResult result) { + return os << DOWNLOAD_RESULT_LOG_MESSAGES.at(static_cast(result)); +} + +constexpr u32 PORT = 443; +constexpr u32 TIMEOUT_SECONDS = 30; +constexpr u64 VFS_COPY_BLOCK_SIZE = 1ull << 24; // 4MB + +namespace { + +std::string GetZIPFilePath(u64 title_id) { + return fmt::format("{}bcat/{:016X}/data.zip", + FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); +} + +// If the error is something the user should know about (build ID mismatch, bad client version), +// display an error. +void HandleDownloadDisplayResult(DownloadResult res) { + if (res == DownloadResult::Success || res == DownloadResult::NoResponse || + res == DownloadResult::GeneralWebError || res == DownloadResult::GeneralFSError || + res == DownloadResult::NoMatchTitleId || res == DownloadResult::InvalidContentType) { + return; + } + + const auto& frontend{Core::System::GetInstance().GetAppletManager().GetAppletFrontendSet()}; + frontend.error->ShowCustomErrorText( + ResultCode(-1), "There was an error while attempting to use Boxcat.", + DOWNLOAD_RESULT_LOG_MESSAGES[static_cast(res)], [] {}); +} + +} // namespace + +class Boxcat::Client { +public: + Client(std::string zip_path, u64 title_id, u64 build_id) + : zip_path(std::move(zip_path)), title_id(title_id), build_id(build_id) {} + + DownloadResult Download() { + const auto resolved_path = fmt::format(BOXCAT_PATHNAME_DATA, title_id); + if (client == nullptr) { + client = std::make_unique(BOXCAT_HOSTNAME, PORT, TIMEOUT_SECONDS); + } + + httplib::Headers headers{ + {std::string("Boxcat-Client-Version"), std::string(BOXCAT_API_VERSION)}, + {std::string("Boxcat-Build-Id"), fmt::format("{:016X}", build_id)}, + }; + + if (FileUtil::Exists(zip_path)) { + FileUtil::IOFile file{zip_path, "rb"}; + std::vector bytes(file.GetSize()); + file.ReadBytes(bytes.data(), bytes.size()); + const auto digest = DigestFile(bytes); + headers.insert({std::string("Boxcat-Current-Zip-Digest"), + Common::HexArrayToString(digest, false)}); + } + + const auto response = client->Get(resolved_path.c_str(), headers); + if (response == nullptr) + return DownloadResult::NoResponse; + + if (response->status == static_cast(ResponseStatus::NoUpdate)) + return DownloadResult::Success; + if (response->status == static_cast(ResponseStatus::BadClientVersion)) + return DownloadResult::BadClientVersion; + if (response->status == static_cast(ResponseStatus::NoMatchTitleId)) + return DownloadResult::NoMatchTitleId; + if (response->status == static_cast(ResponseStatus::NoMatchBuildId)) + return DownloadResult::NoMatchBuildId; + if (response->status >= 400) + return DownloadResult::GeneralWebError; + + const auto content_type = response->headers.find("content-type"); + if (content_type == response->headers.end() || + content_type->second.find("application/zip") == std::string::npos) { + return DownloadResult::InvalidContentType; + } + + FileUtil::CreateFullPath(zip_path); + FileUtil::IOFile file{zip_path, "wb"}; + if (!file.IsOpen()) + return DownloadResult::GeneralFSError; + if (!file.Resize(response->body.size())) + return DownloadResult::GeneralFSError; + if (file.WriteBytes(response->body.data(), response->body.size()) != response->body.size()) + return DownloadResult::GeneralFSError; + + return DownloadResult::Success; + } + +private: + using Digest = std::array; + static Digest DigestFile(std::vector bytes) { + Digest out{}; + mbedtls_sha256(bytes.data(), bytes.size(), out.data(), 0); + return out; + } + + std::unique_ptr client; + std::string zip_path; + u64 title_id; + u64 build_id; +}; + +Boxcat::Boxcat(DirectoryGetter getter) : Backend(std::move(getter)) {} + +Boxcat::~Boxcat() = default; + +void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, + CompletionCallback callback, std::optional dir_name = {}) { + const auto failure = [&callback] { + // Acquire the HLE mutex + std::lock_guard lock{HLE::g_hle_lock}; + callback(false); + }; + + if (Settings::values.bcat_boxcat_local) { + LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download."); + // Acquire the HLE mutex + std::lock_guard lock{HLE::g_hle_lock}; + callback(true); + return; + } + + const auto zip_path{GetZIPFilePath(title.title_id)}; + Boxcat::Client client{zip_path, title.title_id, title.build_id}; + + const auto res = client.Download(); + if (res != DownloadResult::Success) { + LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); + HandleDownloadDisplayResult(res); + failure(); + return; + } + + FileUtil::IOFile zip{zip_path, "rb"}; + const auto size = zip.GetSize(); + std::vector bytes(size); + if (size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { + LOG_ERROR(Service_BCAT, "Boxcat failed to read ZIP file at path '{}'!", zip_path); + failure(); + return; + } + + const auto extracted = FileSys::ExtractZIP(std::make_shared(bytes)); + if (extracted == nullptr) { + LOG_ERROR(Service_BCAT, "Boxcat failed to extract ZIP file!"); + failure(); + return; + } + + if (dir_name == std::nullopt) { + const auto target_dir = dir_getter(title.title_id); + if (target_dir == nullptr || + !FileSys::VfsRawCopyD(extracted, target_dir, VFS_COPY_BLOCK_SIZE)) { + LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!"); + failure(); + return; + } + } else { + const auto target_dir = dir_getter(title.title_id); + if (target_dir == nullptr) { + LOG_ERROR(Service_BCAT, "Boxcat failed to get directory for title ID!"); + failure(); + return; + } + + const auto target_sub = target_dir->GetSubdirectory(*dir_name); + const auto source_sub = extracted->GetSubdirectory(*dir_name); + + if (target_sub == nullptr || source_sub == nullptr || + !FileSys::VfsRawCopyD(source_sub, target_sub, VFS_COPY_BLOCK_SIZE)) { + LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!"); + failure(); + return; + } + } + + // Acquire the HLE mutex + std::lock_guard lock{HLE::g_hle_lock}; + callback(true); +} + +bool Boxcat::Synchronize(TitleIDVersion title, CompletionCallback callback) { + is_syncing.exchange(true); + std::thread(&SynchronizeInternal, dir_getter, title, callback, std::nullopt).detach(); + return true; +} + +bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name, + CompletionCallback callback) { + is_syncing.exchange(true); + std::thread(&SynchronizeInternal, dir_getter, title, callback, name).detach(); + return true; +} + +bool Boxcat::Clear(u64 title_id) { + if (Settings::values.bcat_boxcat_local) { + LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping clear."); + return true; + } + + const auto dir = dir_getter(title_id); + + std::vector dirnames; + + for (const auto& subdir : dir->GetSubdirectories()) + dirnames.push_back(subdir->GetName()); + + for (const auto& subdir : dirnames) { + if (!dir->DeleteSubdirectoryRecursive(subdir)) + return false; + } + + return true; +} + +void Boxcat::SetPassphrase(u64 title_id, const Passphrase& passphrase) { + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, + Common::HexArrayToString(passphrase)); +} + +Boxcat::StatusResult Boxcat::GetStatus(std::optional& global, + std::map& games) { + httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast(PORT), + static_cast(TIMEOUT_SECONDS)}; + + httplib::Headers headers{ + {std::string("Boxcat-Client-Version"), std::string(BOXCAT_API_VERSION)}, + }; + + const auto response = client.Get(BOXCAT_PATHNAME_EVENTS, headers); + if (response == nullptr) + return StatusResult::Offline; + + if (response->status == static_cast(ResponseStatus::BadClientVersion)) + return StatusResult::BadClientVersion; + + try { + nlohmann::json json = nlohmann::json::parse(response->body); + + if (!json["online"].get()) + return StatusResult::Offline; + + if (json["global"].is_null()) + global = std::nullopt; + else + global = json["global"].get(); + + if (json["games"].is_array()) { + for (const auto object : json["games"]) { + if (object.is_object() && object.find("name") != object.end()) { + EventStatus detail{}; + if (object["header"].is_string()) { + detail.header = object["header"].get(); + } else { + detail.header = std::nullopt; + } + + if (object["footer"].is_string()) { + detail.footer = object["footer"].get(); + } else { + detail.footer = std::nullopt; + } + + if (object["events"].is_array()) { + for (const auto& event : object["events"]) { + if (!event.is_string()) + continue; + detail.events.push_back(event.get()); + } + } + + games.insert_or_assign(object["name"], std::move(detail)); + } + } + } + + return StatusResult::Success; + } catch (const nlohmann::json::parse_error& e) { + return StatusResult::ParseError; + } +} + +} // namespace Service::BCAT diff --git a/src/core/hle/service/bcat/backend/boxcat.h b/src/core/hle/service/bcat/backend/boxcat.h new file mode 100644 index 000000000..f4e60f264 --- /dev/null +++ b/src/core/hle/service/bcat/backend/boxcat.h @@ -0,0 +1,56 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include "core/hle/service/bcat/backend/backend.h" + +namespace Service::BCAT { + +struct EventStatus { + std::optional header; + std::optional footer; + std::vector events; +}; + +/// Boxcat is yuzu's custom backend implementation of Nintendo's BCAT service. It is free to use and +/// doesn't require a switch or nintendo account. The content is controlled by the yuzu team. +class Boxcat final : public Backend { + friend void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, + CompletionCallback callback, + std::optional dir_name); + +public: + explicit Boxcat(DirectoryGetter getter); + ~Boxcat() override; + + bool Synchronize(TitleIDVersion title, CompletionCallback callback) override; + bool SynchronizeDirectory(TitleIDVersion title, std::string name, + CompletionCallback callback) override; + + bool Clear(u64 title_id) override; + + void SetPassphrase(u64 title_id, const Passphrase& passphrase) override; + + enum class StatusResult { + Success, + Offline, + ParseError, + BadClientVersion, + }; + + static StatusResult GetStatus(std::optional& global, + std::map& games); + +private: + std::atomic_bool is_syncing{false}; + + class Client; + std::unique_ptr client; +}; + +} // namespace Service::BCAT -- cgit v1.2.3 From 68658a8385b74454c8523efe95ceb81b34bb8812 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Apr 2019 18:49:46 -0400 Subject: module: Create BCAT backend based upon Settings value on construction --- src/core/hle/service/bcat/module.cpp | 14 +++++++++++++- src/core/hle/service/bcat/module.h | 3 +++ 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index b7bd738fc..32d3d5cfc 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -38,10 +38,22 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface(); +namespace { +std::unique_ptr CreateBackendFromSettings(DirectoryGetter getter) { + const auto backend = Settings::values.bcat_backend; + +#ifdef YUZU_ENABLE_BOXCAT + if (backend == "boxcat") + return std::make_unique(std::move(getter)); +#endif + + return std::make_unique(std::move(getter)); } +} // Anonymous namespace Module::Interface::Interface(std::shared_ptr module, const char* name) - : ServiceFramework(name), module(std::move(module)) {} + : ServiceFramework(name), module(std::move(module)), + backend(CreateBackendFromSettings(&Service::FileSystem::GetBCATDirectory)) {} Module::Interface::~Interface() = default; diff --git a/src/core/hle/service/bcat/module.h b/src/core/hle/service/bcat/module.h index f0d63cab0..4af363bfd 100644 --- a/src/core/hle/service/bcat/module.h +++ b/src/core/hle/service/bcat/module.h @@ -8,6 +8,8 @@ namespace Service::BCAT { +class Backend; + class Module final { public: class Interface : public ServiceFramework { @@ -19,6 +21,7 @@ public: protected: std::shared_ptr module; + std::unique_ptr backend; }; }; -- cgit v1.2.3 From 78d146f907cbab60026f972e1be7cc8eb83e05bb Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Apr 2019 18:51:18 -0400 Subject: bcat: Add commands to create IDeliveryCacheStorageService Used to access contents of download. --- src/core/hle/service/bcat/bcat.cpp | 4 ++++ src/core/hle/service/bcat/module.cpp | 28 ++++++++++++++++++++++++++-- src/core/hle/service/bcat/module.h | 2 ++ 3 files changed, 32 insertions(+), 2 deletions(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp index 179aa4949..391f599ee 100644 --- a/src/core/hle/service/bcat/bcat.cpp +++ b/src/core/hle/service/bcat/bcat.cpp @@ -8,9 +8,13 @@ namespace Service::BCAT { BCAT::BCAT(std::shared_ptr module, const char* name) : Module::Interface(std::move(module), name) { + // clang-format off static const FunctionInfo functions[] = { {0, &BCAT::CreateBcatService, "CreateBcatService"}, + {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"}, + {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"}, }; + // clang-format on RegisterHandlers(functions); } diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 32d3d5cfc..fd742fde2 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -11,7 +11,8 @@ namespace Service::BCAT { class IBcatService final : public ServiceFramework { public: - IBcatService() : ServiceFramework("IBcatService") { + IBcatService(Backend& backend) : ServiceFramework("IBcatService"), backend(backend) { + // clang-format off static const FunctionInfo functions[] = { {10100, nullptr, "RequestSyncDeliveryCache"}, {10101, nullptr, "RequestSyncDeliveryCacheWithDirectoryName"}, @@ -28,6 +29,7 @@ public: {90201, nullptr, "ClearDeliveryCacheStorage"}, {90300, nullptr, "GetPushNotificationLog"}, }; + // clang-format on RegisterHandlers(functions); } }; @@ -37,7 +39,29 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(); + rb.PushIpcInterface(*backend); +void Module::Interface::CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface( + Service::FileSystem::GetBCATDirectory(Core::CurrentProcess()->GetTitleID())); +} + +void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId( + Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto title_id = rp.PopRaw(); + + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface( + Service::FileSystem::GetBCATDirectory(title_id)); +} + namespace { std::unique_ptr CreateBackendFromSettings(DirectoryGetter getter) { const auto backend = Settings::values.bcat_backend; diff --git a/src/core/hle/service/bcat/module.h b/src/core/hle/service/bcat/module.h index 4af363bfd..fc52574c2 100644 --- a/src/core/hle/service/bcat/module.h +++ b/src/core/hle/service/bcat/module.h @@ -18,6 +18,8 @@ public: ~Interface() override; void CreateBcatService(Kernel::HLERequestContext& ctx); + void CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx); + void CreateDeliveryCacheStorageServiceWithApplicationId(Kernel::HLERequestContext& ctx); protected: std::shared_ptr module; -- cgit v1.2.3 From 862131ead9cf8e10e4ff220d01cb1be16533d208 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Apr 2019 18:53:03 -0400 Subject: bcat: Implement IDeliveryCacheStorageService commands Used to create subclasses to manage files and directories and to list directories. --- src/core/hle/service/bcat/module.cpp | 58 ++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index fd742fde2..2d8341359 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -40,6 +40,64 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface(*backend); +class IDeliveryCacheStorageService final : public ServiceFramework { +public: + IDeliveryCacheStorageService(FileSys::VirtualDir root_) + : ServiceFramework{"IDeliveryCacheStorageService"}, root(std::move(root_)) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IDeliveryCacheStorageService::CreateFileService, "CreateFileService"}, + {1, &IDeliveryCacheStorageService::CreateDirectoryService, "CreateDirectoryService"}, + {10, &IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory, "EnumerateDeliveryCacheDirectory"}, + }; + // clang-format on + + RegisterHandlers(functions); + + for (const auto& subdir : root->GetSubdirectories()) { + DirectoryName name{}; + std::memcpy(name.data(), subdir->GetName().data(), + std::min(sizeof(DirectoryName) - 1, subdir->GetName().size())); + entries.push_back(name); + } + } + +private: + void CreateFileService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(root); + } + + void CreateDirectoryService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(root); + } + + void EnumerateDeliveryCacheDirectory(Kernel::HLERequestContext& ctx) { + auto size = ctx.GetWriteBufferSize() / sizeof(DirectoryName); + + LOG_DEBUG(Service_BCAT, "called, size={:016X}", size); + + size = std::min(size, entries.size() - next_read_index); + ctx.WriteBuffer(entries.data() + next_read_index, size * sizeof(DirectoryName)); + next_read_index += size; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(size); + } + + FileSys::VirtualDir root; + std::vector entries; + u64 next_read_index = 0; +}; + void Module::Interface::CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_BCAT, "called"); -- cgit v1.2.3 From 8812018c1defbd76b9a44aaf8e050bf97c73aeae Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Apr 2019 18:53:39 -0400 Subject: bcat: Implement IDeliveryCacheDirectoryService commands Used to list and get directories at the root level. --- src/core/hle/service/bcat/module.cpp | 99 ++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 2d8341359..6645599f2 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -40,6 +40,105 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface(*backend); +class IDeliveryCacheDirectoryService final + : public ServiceFramework { +public: + IDeliveryCacheDirectoryService(FileSys::VirtualDir root_) + : ServiceFramework{"IDeliveryCacheDirectoryService"}, root(std::move(root_)) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IDeliveryCacheDirectoryService::Open, "Open"}, + {1, &IDeliveryCacheDirectoryService::Read, "Read"}, + {2, &IDeliveryCacheDirectoryService::GetCount, "GetCount"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void Open(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto name_raw = rp.PopRaw(); + const auto name = + Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size()); + + LOG_DEBUG(Service_BCAT, "called, name={}", name); + + if (!VerifyNameValidDir(ctx, name_raw)) + return; + + if (current_dir != nullptr) { + LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_ENTITY_ALREADY_OPEN); + return; + } + + current_dir = root->GetSubdirectory(name); + + if (current_dir == nullptr) { + LOG_ERROR(Service_BCAT, "Failed to open the directory name={}!", name); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_FAILED_OPEN_ENTITY); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void Read(Kernel::HLERequestContext& ctx) { + auto write_size = ctx.GetWriteBufferSize() / sizeof(DeliveryCacheDirectoryEntry); + + LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size); + + if (current_dir == nullptr) { + LOG_ERROR(Service_BCAT, "There is no open directory!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NO_OPEN_ENTITY); + return; + } + + const auto files = current_dir->GetFiles(); + write_size = std::min(write_size, files.size()); + std::vector entries(write_size); + std::transform( + files.begin(), files.begin() + write_size, entries.begin(), [](const auto& file) { + FileName name{}; + std::memcpy(name.data(), file->GetName().data(), + std::min(file->GetName().size(), name.size())); + return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)}; + }); + + ctx.WriteBuffer(entries); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(write_size * sizeof(DeliveryCacheDirectoryEntry)); + } + + void GetCount(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + if (current_dir == nullptr) { + LOG_ERROR(Service_BCAT, "There is no open directory!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NO_OPEN_ENTITY); + return; + } + + const auto files = current_dir->GetFiles(); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(files.size()); + } + + FileSys::VirtualDir root; + FileSys::VirtualDir current_dir; +}; + class IDeliveryCacheStorageService final : public ServiceFramework { public: IDeliveryCacheStorageService(FileSys::VirtualDir root_) -- cgit v1.2.3 From f352ad5c93aa0266cba8b4c15ee3f0c5f0b0aed2 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Apr 2019 18:54:26 -0400 Subject: bcat: Implement IDeliveryCacheFileService commands Used to read the contents of files and access their metadata. --- src/core/hle/service/bcat/module.cpp | 117 +++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 6645599f2..25f68ed63 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -40,6 +40,123 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface(*backend); + +class IDeliveryCacheFileService final : public ServiceFramework { +public: + IDeliveryCacheFileService(FileSys::VirtualDir root_) + : ServiceFramework{"IDeliveryCacheFileService"}, root(std::move(root_)) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IDeliveryCacheFileService::Open, "Open"}, + {1, &IDeliveryCacheFileService::Read, "Read"}, + {2, &IDeliveryCacheFileService::GetSize, "GetSize"}, + {3, &IDeliveryCacheFileService::GetDigest, "GetDigest"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void Open(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto dir_name_raw = rp.PopRaw(); + const auto file_name_raw = rp.PopRaw(); + + const auto dir_name = + Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size()); + const auto file_name = + Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size()); + + LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name); + + if (!VerifyNameValidDir(ctx, dir_name_raw) || !VerifyNameValidFile(ctx, file_name_raw)) + return; + + if (current_file != nullptr) { + LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_ENTITY_ALREADY_OPEN); + return; + } + + const auto dir = root->GetSubdirectory(dir_name); + + if (dir == nullptr) { + LOG_ERROR(Service_BCAT, "The directory of name={} couldn't be opened!", dir_name); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_FAILED_OPEN_ENTITY); + return; + } + + current_file = dir->GetFile(file_name); + + if (current_file == nullptr) { + LOG_ERROR(Service_BCAT, "The file of name={} couldn't be opened!", file_name); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_FAILED_OPEN_ENTITY); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto offset{rp.PopRaw()}; + + auto size = ctx.GetWriteBufferSize(); + + LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, size); + + if (current_file == nullptr) { + LOG_ERROR(Service_BCAT, "There is no file currently open!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NO_OPEN_ENTITY); + } + + size = std::min(current_file->GetSize() - offset, size); + const auto buffer = current_file->ReadBytes(size, offset); + ctx.WriteBuffer(buffer); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(buffer.size()); + } + + void GetSize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + if (current_file == nullptr) { + LOG_ERROR(Service_BCAT, "There is no file currently open!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NO_OPEN_ENTITY); + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(current_file->GetSize()); + } + + void GetDigest(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + if (current_file == nullptr) { + LOG_ERROR(Service_BCAT, "There is no file currently open!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NO_OPEN_ENTITY); + } + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(DigestFile(current_file)); + } + + FileSys::VirtualDir root; + FileSys::VirtualFile current_file; +}; + class IDeliveryCacheDirectoryService final : public ServiceFramework { public: -- cgit v1.2.3 From cb7c96b96a0ad781d5bb4387072d5d0becd74dd7 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Apr 2019 18:55:56 -0400 Subject: bcat: Implement IDeliveryCacheProgressService commands Used to query completion status and events for the current delivery task. --- src/core/hle/service/bcat/module.cpp | 131 +++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 25f68ed63..1459fab11 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -2,13 +2,144 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include +#include "backend/boxcat.h" +#include "common/hex_util.h" #include "common/logging/log.h" +#include "common/string_util.h" +#include "core/file_sys/vfs.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" +#include "core/hle/service/bcat/backend/backend.h" #include "core/hle/service/bcat/bcat.h" #include "core/hle/service/bcat/module.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/settings.h" namespace Service::BCAT { +constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1}; +constexpr ResultCode ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2}; +constexpr ResultCode ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6}; +constexpr ResultCode ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7}; + +// The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files +// and if any of them have a non-zero result it just forwards that result. This is the FS error code +// for permission denied, which is the closest approximation of this scenario. +constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400}; + +using BCATDigest = std::array; + +struct DeliveryCacheProgressImpl { + enum class Status : u8 { + Incomplete = 0x1, + Complete = 0x9, + }; + + Status status = Status::Incomplete; + INSERT_PADDING_BYTES( + 0x1FF); ///< TODO(DarkLordZach): RE this structure. It just seems to convey info about the + ///< progress of the BCAT sync, but for us just setting completion works. +}; +static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200, + "DeliveryCacheProgressImpl has incorrect size."); + +namespace { + +u64 GetCurrentBuildID() { + const auto& id = Core::System::GetInstance().GetCurrentProcessBuildID(); + u64 out{}; + std::memcpy(&out, id.data(), sizeof(u64)); + return out; +} + +// The digest is only used to determine if a file is unique compared to others of the same name. +// Since the algorithm isn't ever checked in game, MD5 is safe. +BCATDigest DigestFile(const FileSys::VirtualFile& file) { + BCATDigest out{}; + const auto bytes = file->ReadAllBytes(); + mbedtls_md5(bytes.data(), bytes.size(), out.data()); + return out; +} + +// For a name to be valid it must be non-empty, must have a null terminating character as the final +// char, can only contain numbers, letters, underscores and a hyphen if directory and a period if +// file. +bool VerifyNameValidInternal(Kernel::HLERequestContext& ctx, std::array name, + char match_char) { + const auto null_chars = std::count(name.begin(), name.end(), 0); + const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) { + return !std::isalnum(static_cast(c)) && c != '_' && c != match_char && c != '\0'; + }); + if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') { + LOG_ERROR(Service_BCAT, "Name passed was invalid!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + return false; + } + + return true; +} + +bool VerifyNameValidDir(Kernel::HLERequestContext& ctx, std::array name) { + return VerifyNameValidInternal(ctx, name, '-'); +} + +bool VerifyNameValidFile(Kernel::HLERequestContext& ctx, std::array name) { + return VerifyNameValidInternal(ctx, name, '.'); +} + +} // Anonymous namespace + +using DirectoryName = std::array; +using FileName = std::array; + +struct DeliveryCacheDirectoryEntry { + FileName name; + u64 size; + BCATDigest digest; +}; + +class IDeliveryCacheProgressService final : public ServiceFramework { +public: + IDeliveryCacheProgressService(Kernel::SharedPtr event, + const DeliveryCacheProgressImpl& impl) + : ServiceFramework{"IDeliveryCacheProgressService"}, event(std::move(event)), impl(impl) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IDeliveryCacheProgressService::GetEvent, "GetEvent"}, + {1, &IDeliveryCacheProgressService::GetImpl, "GetImpl"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void GetEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(event); + } + + void GetImpl(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + Kernel::SharedPtr event; + const DeliveryCacheProgressImpl& impl; +}; + class IBcatService final : public ServiceFramework { public: IBcatService(Backend& backend) : ServiceFramework("IBcatService"), backend(backend) { -- cgit v1.2.3 From 86773a7f081a8a6c71643ecdc6573b65dbf0ccd3 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Apr 2019 18:57:37 -0400 Subject: bcat: Implement cmd RequestSyncDeliveryCache and variant Variant also supports only updating a single directory. These just both invoke backend commands. --- src/core/hle/service/bcat/module.cpp | 72 +++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 1459fab11..605aa6e00 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -145,8 +145,8 @@ public: IBcatService(Backend& backend) : ServiceFramework("IBcatService"), backend(backend) { // clang-format off static const FunctionInfo functions[] = { - {10100, nullptr, "RequestSyncDeliveryCache"}, - {10101, nullptr, "RequestSyncDeliveryCacheWithDirectoryName"}, + {10100, &IBcatService::RequestSyncDeliveryCache, "RequestSyncDeliveryCache"}, + {10101, &IBcatService::RequestSyncDeliveryCacheWithDirectoryName, "RequestSyncDeliveryCacheWithDirectoryName"}, {10200, nullptr, "CancelSyncDeliveryCacheRequest"}, {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"}, {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"}, @@ -162,7 +162,74 @@ public: }; // clang-format on RegisterHandlers(functions); + + auto& kernel{Core::System::GetInstance().Kernel()}; + progress.at(static_cast(SyncType::Normal)).event = + Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + "BCAT::IDeliveryCacheProgressEvent"); + progress.at(static_cast(SyncType::Directory)).event = + Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, + "BCAT::IDeliveryCacheProgressEvent::DirectoryName"); + } + +private: + enum class SyncType { + Normal, + Directory, + Count, + }; + + std::function CreateCallback(SyncType type) { + return [this, type](bool success) { + auto& pair{progress.at(static_cast(type))}; + pair.impl.status = DeliveryCacheProgressImpl::Status::Complete; + pair.event.writable->Signal(); + }; + } + + std::shared_ptr CreateProgressService(SyncType type) { + const auto& pair{progress.at(static_cast(type))}; + return std::make_shared(pair.event.readable, pair.impl); } + + void RequestSyncDeliveryCache(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + backend.Synchronize({Core::CurrentProcess()->GetTitleID(), GetCurrentBuildID()}, + CreateCallback(SyncType::Normal)); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(CreateProgressService(SyncType::Normal)); + } + + void RequestSyncDeliveryCacheWithDirectoryName(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto name_raw = rp.PopRaw(); + const auto name = + Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size()); + + LOG_DEBUG(Service_BCAT, "called, name={}", name); + + backend.SynchronizeDirectory({Core::CurrentProcess()->GetTitleID(), GetCurrentBuildID()}, + name, CreateCallback(SyncType::Directory)); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(CreateProgressService(SyncType::Directory)); + } + + } + + Backend& backend; + + struct ProgressPair { + Kernel::EventPair event; + DeliveryCacheProgressImpl impl; + }; + + std::array(SyncType::Count)> progress{}; }; void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { @@ -171,6 +238,7 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface(*backend); +} class IDeliveryCacheFileService final : public ServiceFramework { public: -- cgit v1.2.3 From 1bde5a3c6a205de445b2086f2fda2a830d70b8f6 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Apr 2019 18:59:35 -0400 Subject: bcat: Implement cmd 30100 SetPassphrase Takes a title ID and passphrase (0x40 byte string) and passes it to the backend. --- src/core/hle/service/bcat/module.cpp | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 605aa6e00..cbda8e0d3 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -150,7 +150,7 @@ public: {10200, nullptr, "CancelSyncDeliveryCacheRequest"}, {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"}, {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"}, - {30100, nullptr, "SetPassphrase"}, + {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, {30200, nullptr, "RegisterBackgroundDeliveryTask"}, {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, {30202, nullptr, "BlockDeliveryTask"}, @@ -220,6 +220,38 @@ private: rb.PushIpcInterface(CreateProgressService(SyncType::Directory)); } + void SetPassphrase(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto title_id = rp.PopRaw(); + + const auto passphrase_raw = ctx.ReadBuffer(); + + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, + Common::HexVectorToString(passphrase_raw)); + + if (title_id == 0) { + LOG_ERROR(Service_BCAT, "Invalid title ID!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + } + + if (passphrase_raw.size() > 0x40) { + LOG_ERROR(Service_BCAT, "Passphrase too large!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + return; + } + + Passphrase passphrase{}; + std::memcpy(passphrase.data(), passphrase_raw.data(), + std::min(passphrase.size(), passphrase_raw.size())); + + backend.SetPassphrase(title_id, passphrase); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + } Backend& backend; -- cgit v1.2.3 From 102db206e0cf8c0332e6ec0c2c6f4fa8c7d4f05c Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Apr 2019 19:00:36 -0400 Subject: bcat: Implement cmd 90201 ClearDeliveryCacheStorage Takes a title ID and simply deletes all the data for that title ID's bcat. Invokes the respective backend command. --- src/core/hle/service/bcat/module.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index cbda8e0d3..9244c265a 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -157,7 +157,7 @@ public: {30203, nullptr, "UnblockDeliveryTask"}, {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, {90200, nullptr, "GetDeliveryList"}, - {90201, nullptr, "ClearDeliveryCacheStorage"}, + {90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"}, {90300, nullptr, "GetPushNotificationLog"}, }; // clang-format on @@ -252,6 +252,28 @@ private: rb.Push(RESULT_SUCCESS); } + void ClearDeliveryCacheStorage(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto title_id = rp.PopRaw(); + + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); + + if (title_id == 0) { + LOG_ERROR(Service_BCAT, "Invalid title ID!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + return; + } + + if (!backend.Clear(title_id)) { + LOG_ERROR(Service_BCAT, "Could not clear the directory successfully!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_FAILED_CLEAR_CACHE); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); } Backend& backend; -- cgit v1.2.3 From fe8c7e66e291b1fb3bef206e0d5809ad80441e9b Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Wed, 1 May 2019 22:40:51 -0400 Subject: am: Unstub PopLaunchParameter and add bcat connection for app-specific data Previously we were simply returning the account-preselect structure all times but if passed with a different mode the game expects application-specific data. This also adds a hook for BCAT into this allowing us to send the launch parameter through bcat, --- src/core/hle/service/am/am.cpp | 66 ++++++++++++++++++++++++++++++++---------- src/core/hle/service/am/am.h | 2 ++ 2 files changed, 52 insertions(+), 16 deletions(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 797c9a06f..79f9a393e 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -31,6 +31,7 @@ #include "core/hle/service/am/tcap.h" #include "core/hle/service/apm/controller.h" #include "core/hle/service/apm/interface.h" +#include "core/hle/service/bcat/backend/backend.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -46,15 +47,20 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2}; constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3}; constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; -constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; +enum class LaunchParameterKind : u32 { + ApplicationSpecific = 1, + AccountPreselectedUser = 2, +}; + +constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA; -struct LaunchParameters { +struct LaunchParameterAccountPreselectedUser { u32_le magic; u32_le is_account_selected; u128 current_user; INSERT_PADDING_BYTES(0x70); }; -static_assert(sizeof(LaunchParameters) == 0x88); +static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88); IWindowController::IWindowController(Core::System& system_) : ServiceFramework("IWindowController"), system{system_} { @@ -1128,26 +1134,54 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx } void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); + IPC::RequestParser rp{ctx}; + const auto kind = rp.PopEnum(); - LaunchParameters params{}; + LOG_DEBUG(Service_AM, "called, kind={:08X}", static_cast(kind)); - params.magic = POP_LAUNCH_PARAMETER_MAGIC; - params.is_account_selected = 1; + if (kind == LaunchParameterKind::ApplicationSpecific && !launch_popped_application_specific) { + const auto backend = BCAT::CreateBackendFromSettings(&FileSystem::GetBCATDirectory); + const auto build_id_full = Core::System::GetInstance().GetCurrentProcessBuildID(); + u64 build_id{}; + std::memcpy(&build_id, build_id_full.data(), sizeof(u64)); - Account::ProfileManager profile_manager{}; - const auto uuid = profile_manager.GetUser(Settings::values.current_user); - ASSERT(uuid); - params.current_user = uuid->uuid; + const auto data = + backend->GetLaunchParameter({Core::CurrentProcess()->GetTitleID(), build_id}); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + if (data.has_value()) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(*data); + launch_popped_application_specific = true; + return; + } + } else if (kind == LaunchParameterKind::AccountPreselectedUser && + !launch_popped_account_preselect) { + LaunchParameterAccountPreselectedUser params{}; - rb.Push(RESULT_SUCCESS); + params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC; + params.is_account_selected = 1; - std::vector buffer(sizeof(LaunchParameters)); - std::memcpy(buffer.data(), ¶ms, buffer.size()); + Account::ProfileManager profile_manager{}; + const auto uuid = profile_manager.GetUser(Settings::values.current_user); + ASSERT(uuid); + params.current_user = uuid->uuid; - rb.PushIpcInterface(buffer); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + + std::vector buffer(sizeof(LaunchParameterAccountPreselectedUser)); + std::memcpy(buffer.data(), ¶ms, buffer.size()); + + rb.PushIpcInterface(buffer); + launch_popped_account_preselect = true; + return; + } + + LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NO_DATA_IN_CHANNEL); } void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest( diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index a3baeb673..9169eb2bd 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -255,6 +255,8 @@ private: void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx); void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); + bool launch_popped_application_specific = false; + bool launch_popped_account_preselect = false; Kernel::EventPair gpu_error_detected_event; Core::System& system; }; -- cgit v1.2.3 From ea17b294ea04a00d94025cda97b2cd4d5f730b17 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Wed, 1 May 2019 22:41:32 -0400 Subject: bcat: Expose CreateBackendFromSettings helper function --- src/core/hle/service/bcat/backend/backend.h | 2 ++ src/core/hle/service/bcat/module.cpp | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h index 2e9511f3f..e412819e1 100644 --- a/src/core/hle/service/bcat/backend/backend.h +++ b/src/core/hle/service/bcat/backend/backend.h @@ -50,4 +50,6 @@ public: void SetPassphrase(u64 title_id, const Passphrase& passphrase) override; }; +std::unique_ptr CreateBackendFromSettings(DirectoryGetter getter); + } // namespace Service::BCAT diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 9244c265a..a8d545992 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -589,7 +589,6 @@ void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId( Service::FileSystem::GetBCATDirectory(title_id)); } -namespace { std::unique_ptr CreateBackendFromSettings(DirectoryGetter getter) { const auto backend = Settings::values.bcat_backend; @@ -600,7 +599,6 @@ std::unique_ptr CreateBackendFromSettings(DirectoryGetter getter) { return std::make_unique(std::move(getter)); } -} // Anonymous namespace Module::Interface::Interface(std::shared_ptr module, const char* name) : ServiceFramework(name), module(std::move(module)), -- cgit v1.2.3 From b8ce87103d1aa382a9b96f3379a599f869b02a68 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Wed, 1 May 2019 22:42:17 -0400 Subject: bcat: Add backend function for BCAT Indirect (launch parameter) Returns the data that should be returned by PopLaunchParameter kind=ApplicationSpecific. --- src/core/hle/service/bcat/backend/backend.cpp | 6 ++++++ src/core/hle/service/bcat/backend/backend.h | 5 +++++ 2 files changed, 11 insertions(+) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp index aefa2208d..9a67da2ef 100644 --- a/src/core/hle/service/bcat/backend/backend.cpp +++ b/src/core/hle/service/bcat/backend/backend.cpp @@ -44,4 +44,10 @@ void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) { Common::HexArrayToString(passphrase)); } +std::optional> NullBackend::GetLaunchParameter(TitleIDVersion title) { + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, + title.build_id); + return std::nullopt; +} + } // namespace Service::BCAT diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h index e412819e1..5b4118814 100644 --- a/src/core/hle/service/bcat/backend/backend.h +++ b/src/core/hle/service/bcat/backend/backend.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "common/common_types.h" #include "core/file_sys/vfs_types.h" @@ -32,6 +33,8 @@ public: virtual void SetPassphrase(u64 title_id, const Passphrase& passphrase) = 0; + virtual std::optional> GetLaunchParameter(TitleIDVersion title) = 0; + protected: DirectoryGetter dir_getter; }; @@ -48,6 +51,8 @@ public: bool Clear(u64 title_id) override; void SetPassphrase(u64 title_id, const Passphrase& passphrase) override; + + std::optional> GetLaunchParameter(TitleIDVersion title) override; }; std::unique_ptr CreateBackendFromSettings(DirectoryGetter getter); -- cgit v1.2.3 From e8183f9ef0296cad233c6d7679f5f83b4e0dc5a8 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Wed, 1 May 2019 22:42:50 -0400 Subject: boxcat: Add downloading and client for launch parameter data --- src/core/hle/service/bcat/backend/boxcat.cpp | 91 +++++++++++++++++++++++----- src/core/hle/service/bcat/backend/boxcat.h | 2 + 2 files changed, 77 insertions(+), 16 deletions(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index 539140f30..f37f92bf4 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp @@ -25,13 +25,16 @@ constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org"; // Formatted using fmt with arg[0] = hex title id constexpr char BOXCAT_PATHNAME_DATA[] = "/boxcat/titles/{:016X}/data"; +constexpr char BOXCAT_PATHNAME_LAUNCHPARAM[] = "/boxcat/titles/{:016X}/launchparam"; constexpr char BOXCAT_PATHNAME_EVENTS[] = "/boxcat/events"; constexpr char BOXCAT_API_VERSION[] = "1"; +constexpr char BOXCAT_CLIENT_TYPE[] = "yuzu"; // HTTP status codes for Boxcat enum class ResponseStatus { + Ok = 200, ///< Operation completed successfully. BadClientVersion = 301, ///< The Boxcat-Client-Version doesn't match the server. NoUpdate = 304, ///< The digest provided would match the new data, no need to update. NoMatchTitleId = 404, ///< The title ID provided doesn't have a boxcat implementation. @@ -74,6 +77,11 @@ constexpr u64 VFS_COPY_BLOCK_SIZE = 1ull << 24; // 4MB namespace { +std::string GetBINFilePath(u64 title_id) { + return fmt::format("{}bcat/{:016X}/launchparam.bin", + FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); +} + std::string GetZIPFilePath(u64 title_id) { return fmt::format("{}bcat/{:016X}/data.zip", FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); @@ -98,27 +106,40 @@ void HandleDownloadDisplayResult(DownloadResult res) { class Boxcat::Client { public: - Client(std::string zip_path, u64 title_id, u64 build_id) - : zip_path(std::move(zip_path)), title_id(title_id), build_id(build_id) {} + Client(std::string path, u64 title_id, u64 build_id) + : path(std::move(path)), title_id(title_id), build_id(build_id) {} + + DownloadResult DownloadDataZip() { + return DownloadInternal(fmt::format(BOXCAT_PATHNAME_DATA, title_id), TIMEOUT_SECONDS, + "Boxcat-Data-Digest", "application/zip"); + } + + DownloadResult DownloadLaunchParam() { + return DownloadInternal(fmt::format(BOXCAT_PATHNAME_LAUNCHPARAM, title_id), + TIMEOUT_SECONDS / 3, "Boxcat-LaunchParam-Digest", + "application/octet-stream"); + } - DownloadResult Download() { - const auto resolved_path = fmt::format(BOXCAT_PATHNAME_DATA, title_id); +private: + DownloadResult DownloadInternal(const std::string& resolved_path, u32 timeout_seconds, + const std::string& digest_header_name, + const std::string& content_type_name) { if (client == nullptr) { - client = std::make_unique(BOXCAT_HOSTNAME, PORT, TIMEOUT_SECONDS); + client = std::make_unique(BOXCAT_HOSTNAME, PORT, timeout_seconds); } httplib::Headers headers{ {std::string("Boxcat-Client-Version"), std::string(BOXCAT_API_VERSION)}, + {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)}, {std::string("Boxcat-Build-Id"), fmt::format("{:016X}", build_id)}, }; - if (FileUtil::Exists(zip_path)) { - FileUtil::IOFile file{zip_path, "rb"}; + if (FileUtil::Exists(path)) { + FileUtil::IOFile file{path, "rb"}; std::vector bytes(file.GetSize()); file.ReadBytes(bytes.data(), bytes.size()); const auto digest = DigestFile(bytes); - headers.insert({std::string("Boxcat-Current-Zip-Digest"), - Common::HexArrayToString(digest, false)}); + headers.insert({digest_header_name, Common::HexArrayToString(digest, false)}); } const auto response = client->Get(resolved_path.c_str(), headers); @@ -133,17 +154,17 @@ public: return DownloadResult::NoMatchTitleId; if (response->status == static_cast(ResponseStatus::NoMatchBuildId)) return DownloadResult::NoMatchBuildId; - if (response->status >= 400) + if (response->status != static_cast(ResponseStatus::Ok)) return DownloadResult::GeneralWebError; const auto content_type = response->headers.find("content-type"); if (content_type == response->headers.end() || - content_type->second.find("application/zip") == std::string::npos) { + content_type->second.find(content_type_name) == std::string::npos) { return DownloadResult::InvalidContentType; } - FileUtil::CreateFullPath(zip_path); - FileUtil::IOFile file{zip_path, "wb"}; + FileUtil::CreateFullPath(path); + FileUtil::IOFile file{path, "wb"}; if (!file.IsOpen()) return DownloadResult::GeneralFSError; if (!file.Resize(response->body.size())) @@ -154,7 +175,6 @@ public: return DownloadResult::Success; } -private: using Digest = std::array; static Digest DigestFile(std::vector bytes) { Digest out{}; @@ -163,7 +183,7 @@ private: } std::unique_ptr client; - std::string zip_path; + std::string path; u64 title_id; u64 build_id; }; @@ -191,9 +211,14 @@ void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, const auto zip_path{GetZIPFilePath(title.title_id)}; Boxcat::Client client{zip_path, title.title_id, title.build_id}; - const auto res = client.Download(); + const auto res = client.DownloadDataZip(); if (res != DownloadResult::Success) { LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); + + if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { + FileUtil::Delete(zip_path); + } + HandleDownloadDisplayResult(res); failure(); return; @@ -286,6 +311,39 @@ void Boxcat::SetPassphrase(u64 title_id, const Passphrase& passphrase) { Common::HexArrayToString(passphrase)); } +std::optional> Boxcat::GetLaunchParameter(TitleIDVersion title) { + const auto path{GetBINFilePath(title.title_id)}; + + if (Settings::values.bcat_boxcat_local) { + LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download."); + } else { + Boxcat::Client client{path, title.title_id, title.build_id}; + + const auto res = client.DownloadLaunchParam(); + if (res != DownloadResult::Success) { + LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); + + if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { + FileUtil::Delete(path); + } + + HandleDownloadDisplayResult(res); + return std::nullopt; + } + } + + FileUtil::IOFile bin{path, "rb"}; + const auto size = bin.GetSize(); + std::vector bytes(size); + if (size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { + LOG_ERROR(Service_BCAT, "Boxcat failed to read launch parameter binary at path '{}'!", + path); + return std::nullopt; + } + + return bytes; +} + Boxcat::StatusResult Boxcat::GetStatus(std::optional& global, std::map& games) { httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast(PORT), @@ -293,6 +351,7 @@ Boxcat::StatusResult Boxcat::GetStatus(std::optional& global, httplib::Headers headers{ {std::string("Boxcat-Client-Version"), std::string(BOXCAT_API_VERSION)}, + {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)}, }; const auto response = client.Get(BOXCAT_PATHNAME_EVENTS, headers); diff --git a/src/core/hle/service/bcat/backend/boxcat.h b/src/core/hle/service/bcat/backend/boxcat.h index f4e60f264..1148a4eca 100644 --- a/src/core/hle/service/bcat/backend/boxcat.h +++ b/src/core/hle/service/bcat/backend/boxcat.h @@ -36,6 +36,8 @@ public: void SetPassphrase(u64 title_id, const Passphrase& passphrase) override; + std::optional> GetLaunchParameter(TitleIDVersion title) override; + enum class StatusResult { Success, Offline, -- cgit v1.2.3 From 92b70a3bf9d4657dccc5a5f2cffdd7789946ca14 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 6 May 2019 18:47:27 -0400 Subject: boxcat: Use Etag header names for file digest --- src/core/hle/service/bcat/backend/boxcat.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index f37f92bf4..31d2e045c 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp @@ -111,18 +111,16 @@ public: DownloadResult DownloadDataZip() { return DownloadInternal(fmt::format(BOXCAT_PATHNAME_DATA, title_id), TIMEOUT_SECONDS, - "Boxcat-Data-Digest", "application/zip"); + "application/zip"); } DownloadResult DownloadLaunchParam() { return DownloadInternal(fmt::format(BOXCAT_PATHNAME_LAUNCHPARAM, title_id), - TIMEOUT_SECONDS / 3, "Boxcat-LaunchParam-Digest", - "application/octet-stream"); + TIMEOUT_SECONDS / 3, "application/octet-stream"); } private: DownloadResult DownloadInternal(const std::string& resolved_path, u32 timeout_seconds, - const std::string& digest_header_name, const std::string& content_type_name) { if (client == nullptr) { client = std::make_unique(BOXCAT_HOSTNAME, PORT, timeout_seconds); @@ -136,10 +134,13 @@ private: if (FileUtil::Exists(path)) { FileUtil::IOFile file{path, "rb"}; - std::vector bytes(file.GetSize()); - file.ReadBytes(bytes.data(), bytes.size()); - const auto digest = DigestFile(bytes); - headers.insert({digest_header_name, Common::HexArrayToString(digest, false)}); + if (file.IsOpen()) { + std::vector bytes(file.GetSize()); + file.ReadBytes(bytes.data(), bytes.size()); + const auto digest = DigestFile(bytes); + headers.insert( + {std::string("If-None-Match"), Common::HexArrayToString(digest, false)}); + } } const auto response = client->Get(resolved_path.c_str(), headers); @@ -227,7 +228,7 @@ void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, FileUtil::IOFile zip{zip_path, "rb"}; const auto size = zip.GetSize(); std::vector bytes(size); - if (size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { + if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { LOG_ERROR(Service_BCAT, "Boxcat failed to read ZIP file at path '{}'!", zip_path); failure(); return; @@ -335,7 +336,7 @@ std::optional> Boxcat::GetLaunchParameter(TitleIDVersion title) FileUtil::IOFile bin{path, "rb"}; const auto size = bin.GetSize(); std::vector bytes(size); - if (size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { + if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { LOG_ERROR(Service_BCAT, "Boxcat failed to read launch parameter binary at path '{}'!", path); return std::nullopt; -- cgit v1.2.3 From 2d410ddf4d9c0109d64fdf3319efeb9e6cc0bce1 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 13 May 2019 18:51:02 -0400 Subject: bcat: Implement DeliveryCacheProgressImpl structure Huge thanks to lioncash for re-ing this for me. --- src/core/hle/service/bcat/backend/backend.cpp | 88 ++++++++++++++- src/core/hle/service/bcat/backend/backend.h | 97 ++++++++++++++++- src/core/hle/service/bcat/backend/boxcat.cpp | 147 +++++++++++++++++++++----- src/core/hle/service/bcat/backend/boxcat.h | 6 +- src/core/hle/service/bcat/module.cpp | 56 ++-------- 5 files changed, 310 insertions(+), 84 deletions(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp index 9a67da2ef..e389ad568 100644 --- a/src/core/hle/service/bcat/backend/backend.cpp +++ b/src/core/hle/service/bcat/backend/backend.cpp @@ -4,10 +4,90 @@ #include "common/hex_util.h" #include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/lock.h" #include "core/hle/service/bcat/backend/backend.h" namespace Service::BCAT { +ProgressServiceBackend::ProgressServiceBackend(std::string event_name) : impl{} { + auto& kernel{Core::System::GetInstance().Kernel()}; + event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "ProgressServiceBackend:UpdateEvent:" + event_name); +} + +Kernel::SharedPtr ProgressServiceBackend::GetEvent() { + return event.readable; +} + +DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() { + return impl; +} + +void ProgressServiceBackend::SetNeedHLELock(bool need) { + need_hle_lock = need; +} + +void ProgressServiceBackend::SetTotalSize(u64 size) { + impl.total_bytes = size; + SignalUpdate(); +} + +void ProgressServiceBackend::StartConnecting() { + impl.status = DeliveryCacheProgressImpl::Status::Connecting; + SignalUpdate(); +} + +void ProgressServiceBackend::StartProcessingDataList() { + impl.status = DeliveryCacheProgressImpl::Status::ProcessingDataList; + SignalUpdate(); +} + +void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name, + std::string_view file_name, u64 file_size) { + impl.status = DeliveryCacheProgressImpl::Status::Downloading; + impl.current_downloaded_bytes = 0; + impl.current_total_bytes = file_size; + std::memcpy(impl.current_directory.data(), dir_name.data(), std::min(dir_name.size(), 0x31ull)); + std::memcpy(impl.current_file.data(), file_name.data(), std::min(file_name.size(), 0x31ull)); + SignalUpdate(); +} + +void ProgressServiceBackend::UpdateFileProgress(u64 downloaded) { + impl.current_downloaded_bytes = downloaded; + SignalUpdate(); +} + +void ProgressServiceBackend::FinishDownloadingFile() { + impl.total_downloaded_bytes += impl.current_total_bytes; + SignalUpdate(); +} + +void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) { + impl.status = DeliveryCacheProgressImpl::Status::Committing; + impl.current_file.fill(0); + impl.current_downloaded_bytes = 0; + impl.current_total_bytes = 0; + std::memcpy(impl.current_directory.data(), dir_name.data(), std::min(dir_name.size(), 0x31ull)); + SignalUpdate(); +} + +void ProgressServiceBackend::FinishDownload(ResultCode result) { + impl.total_downloaded_bytes = impl.total_bytes; + impl.status = DeliveryCacheProgressImpl::Status::Done; + impl.result = result; + SignalUpdate(); +} + +void ProgressServiceBackend::SignalUpdate() const { + if (need_hle_lock) { + std::lock_guard lock(HLE::g_hle_lock); + event.writable->Signal(); + } else { + event.writable->Signal(); + } +} + Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {} Backend::~Backend() = default; @@ -16,20 +96,20 @@ NullBackend::NullBackend(const DirectoryGetter& getter) : Backend(std::move(gett NullBackend::~NullBackend() = default; -bool NullBackend::Synchronize(TitleIDVersion title, CompletionCallback callback) { +bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) { LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, title.build_id); - callback(true); + progress.FinishDownload(RESULT_SUCCESS); return true; } bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name, - CompletionCallback callback) { + ProgressServiceBackend& progress) { LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id, title.build_id, name); - callback(true); + progress.FinishDownload(RESULT_SUCCESS); return true; } diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h index 5b4118814..50973a13a 100644 --- a/src/core/hle/service/bcat/backend/backend.h +++ b/src/core/hle/service/bcat/backend/backend.h @@ -8,10 +8,14 @@ #include #include "common/common_types.h" #include "core/file_sys/vfs_types.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" +#include "core/hle/result.h" namespace Service::BCAT { -using CompletionCallback = std::function; +struct DeliveryCacheProgressImpl; + using DirectoryGetter = std::function; using Passphrase = std::array; @@ -20,33 +24,116 @@ struct TitleIDVersion { u64 build_id; }; +using DirectoryName = std::array; +using FileName = std::array; + +struct DeliveryCacheProgressImpl { + enum class Status : s32 { + None = 0x0, + Queued = 0x1, + Connecting = 0x2, + ProcessingDataList = 0x3, + Downloading = 0x4, + Committing = 0x5, + Done = 0x9, + }; + + Status status; + ResultCode result = RESULT_SUCCESS; + DirectoryName current_directory; + FileName current_file; + s64 current_downloaded_bytes; ///< Bytes downloaded on current file. + s64 current_total_bytes; ///< Bytes total on current file. + s64 total_downloaded_bytes; ///< Bytes downloaded on overall download. + s64 total_bytes; ///< Bytes total on overall download. + INSERT_PADDING_BYTES( + 0x198); ///< Appears to be unused in official code, possibly reserved for future use. +}; +static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200, + "DeliveryCacheProgressImpl has incorrect size."); + +// A class to manage the signalling to the game about BCAT download progress. +// Some of this class is implemented in module.cpp to avoid exposing the implementation structure. +class ProgressServiceBackend { + friend class IBcatService; + + ProgressServiceBackend(std::string event_name); + + Kernel::SharedPtr GetEvent(); + DeliveryCacheProgressImpl& GetImpl(); + +public: + // Clients should call this with true if any of the functions are going to be called from a + // non-HLE thread and this class need to lock the hle mutex. (default is false) + void SetNeedHLELock(bool need); + + // Sets the number of bytes total in the entire download. + void SetTotalSize(u64 size); + + // Notifies the application that the backend has started connecting to the server. + void StartConnecting(); + // Notifies the application that the backend has begun accumulating and processing metadata. + void StartProcessingDataList(); + + // Notifies the application that a file is starting to be downloaded. + void StartDownloadingFile(std::string_view dir_name, std::string_view file_name, u64 file_size); + // Updates the progress of the current file to the size passed. + void UpdateFileProgress(u64 downloaded); + // Notifies the application that the current file has completed download. + void FinishDownloadingFile(); + + // Notifies the application that all files in this directory have completed and are being + // finalized. + void CommitDirectory(std::string_view dir_name); + + // Notifies the application that the operation completed with result code result. + void FinishDownload(ResultCode result); + +private: + void SignalUpdate() const; + + DeliveryCacheProgressImpl impl; + Kernel::EventPair event; + bool need_hle_lock = false; +}; + +// A class representing an abstract backend for BCAT functionality. class Backend { public: explicit Backend(DirectoryGetter getter); virtual ~Backend(); - virtual bool Synchronize(TitleIDVersion title, CompletionCallback callback) = 0; + // Called when the backend is needed to synchronize the data for the game with title ID and + // version in title. A ProgressServiceBackend object is provided to alert the application of + // status. + virtual bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) = 0; + // Very similar to Synchronize, but only for the directory provided. Backends should not alter + // the data for any other directories. virtual bool SynchronizeDirectory(TitleIDVersion title, std::string name, - CompletionCallback callback) = 0; + ProgressServiceBackend& progress) = 0; + // Removes all cached data associated with title id provided. virtual bool Clear(u64 title_id) = 0; + // Sets the BCAT Passphrase to be used with the associated title ID. virtual void SetPassphrase(u64 title_id, const Passphrase& passphrase) = 0; + // Gets the launch parameter used by AM associated with the title ID and version provided. virtual std::optional> GetLaunchParameter(TitleIDVersion title) = 0; protected: DirectoryGetter dir_getter; }; +// A backend of BCAT that provides no operation. class NullBackend : public Backend { public: explicit NullBackend(const DirectoryGetter& getter); ~NullBackend() override; - bool Synchronize(TitleIDVersion title, CompletionCallback callback) override; + bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override; bool SynchronizeDirectory(TitleIDVersion title, std::string name, - CompletionCallback callback) override; + ProgressServiceBackend& progress) override; bool Clear(u64 title_id) override; diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index 31d2e045c..3754594df 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp @@ -14,13 +14,28 @@ #include "core/file_sys/vfs_libzip.h" #include "core/file_sys/vfs_vector.h" #include "core/frontend/applets/error.h" -#include "core/hle/lock.h" #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/bcat/backend/boxcat.h" #include "core/settings.h" +namespace { + +// Prevents conflicts with windows macro called CreateFile +FileSys::VirtualFile VfsCreateFileWrap(FileSys::VirtualDir dir, std::string_view name) { + return dir->CreateFile(name); +} + +// Prevents conflicts with windows macro called DeleteFile +bool VfsDeleteFileWrap(FileSys::VirtualDir dir, std::string_view name) { + return dir->DeleteFile(name); +} + +} // Anonymous namespace + namespace Service::BCAT { +constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1}; + constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org"; // Formatted using fmt with arg[0] = hex title id @@ -102,7 +117,68 @@ void HandleDownloadDisplayResult(DownloadResult res) { DOWNLOAD_RESULT_LOG_MESSAGES[static_cast(res)], [] {}); } -} // namespace +bool VfsRawCopyProgress(FileSys::VirtualFile src, FileSys::VirtualFile dest, + std::string_view dir_name, ProgressServiceBackend& progress, + std::size_t block_size = 0x1000) { + if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) + return false; + if (!dest->Resize(src->GetSize())) + return false; + + progress.StartDownloadingFile(dir_name, src->GetName(), src->GetSize()); + + std::vector temp(std::min(block_size, src->GetSize())); + for (std::size_t i = 0; i < src->GetSize(); i += block_size) { + const auto read = std::min(block_size, src->GetSize() - i); + + if (src->Read(temp.data(), read, i) != read) { + return false; + } + + if (dest->Write(temp.data(), read, i) != read) { + return false; + } + + progress.UpdateFileProgress(i); + } + + progress.FinishDownloadingFile(); + + return true; +} + +bool VfsRawCopyDProgressSingle(FileSys::VirtualDir src, FileSys::VirtualDir dest, + ProgressServiceBackend& progress, std::size_t block_size = 0x1000) { + if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) + return false; + + for (const auto& file : src->GetFiles()) { + const auto out_file = VfsCreateFileWrap(dest, file->GetName()); + if (!VfsRawCopyProgress(file, out_file, src->GetName(), progress, block_size)) { + return false; + } + } + progress.CommitDirectory(src->GetName()); + + return true; +} + +bool VfsRawCopyDProgress(FileSys::VirtualDir src, FileSys::VirtualDir dest, + ProgressServiceBackend& progress, std::size_t block_size = 0x1000) { + if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) + return false; + + for (const auto& dir : src->GetSubdirectories()) { + const auto out = dest->CreateSubdirectory(dir->GetName()); + if (!VfsRawCopyDProgressSingle(dir, out, progress, block_size)) { + return false; + } + } + + return true; +} + +} // Anonymous namespace class Boxcat::Client { public: @@ -194,24 +270,24 @@ Boxcat::Boxcat(DirectoryGetter getter) : Backend(std::move(getter)) {} Boxcat::~Boxcat() = default; void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, - CompletionCallback callback, std::optional dir_name = {}) { - const auto failure = [&callback] { - // Acquire the HLE mutex - std::lock_guard lock{HLE::g_hle_lock}; - callback(false); - }; + ProgressServiceBackend& progress, + std::optional dir_name = {}) { + progress.SetNeedHLELock(true); if (Settings::values.bcat_boxcat_local) { LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download."); - // Acquire the HLE mutex - std::lock_guard lock{HLE::g_hle_lock}; - callback(true); + const auto dir = dir_getter(title.title_id); + if (dir) + progress.SetTotalSize(dir->GetSize()); + progress.FinishDownload(RESULT_SUCCESS); return; } const auto zip_path{GetZIPFilePath(title.title_id)}; Boxcat::Client client{zip_path, title.title_id, title.build_id}; + progress.StartConnecting(); + const auto res = client.DownloadDataZip(); if (res != DownloadResult::Success) { LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); @@ -221,68 +297,85 @@ void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, } HandleDownloadDisplayResult(res); - failure(); + progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); return; } + progress.StartProcessingDataList(); + FileUtil::IOFile zip{zip_path, "rb"}; const auto size = zip.GetSize(); std::vector bytes(size); if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { LOG_ERROR(Service_BCAT, "Boxcat failed to read ZIP file at path '{}'!", zip_path); - failure(); + progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); return; } const auto extracted = FileSys::ExtractZIP(std::make_shared(bytes)); if (extracted == nullptr) { LOG_ERROR(Service_BCAT, "Boxcat failed to extract ZIP file!"); - failure(); + progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); return; } if (dir_name == std::nullopt) { + progress.SetTotalSize(extracted->GetSize()); + const auto target_dir = dir_getter(title.title_id); - if (target_dir == nullptr || - !FileSys::VfsRawCopyD(extracted, target_dir, VFS_COPY_BLOCK_SIZE)) { + if (target_dir == nullptr || !VfsRawCopyDProgress(extracted, target_dir, progress)) { LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!"); - failure(); + progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); return; } } else { const auto target_dir = dir_getter(title.title_id); if (target_dir == nullptr) { LOG_ERROR(Service_BCAT, "Boxcat failed to get directory for title ID!"); - failure(); + progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); return; } const auto target_sub = target_dir->GetSubdirectory(*dir_name); const auto source_sub = extracted->GetSubdirectory(*dir_name); + progress.SetTotalSize(source_sub->GetSize()); + + std::vector filenames; + { + const auto files = target_sub->GetFiles(); + std::transform(files.begin(), files.end(), std::back_inserter(filenames), + [](const auto& vfile) { return vfile->GetName(); }); + } + + for (const auto& filename : filenames) { + VfsDeleteFileWrap(target_sub, filename); + } + if (target_sub == nullptr || source_sub == nullptr || - !FileSys::VfsRawCopyD(source_sub, target_sub, VFS_COPY_BLOCK_SIZE)) { + !VfsRawCopyDProgressSingle(source_sub, target_sub, progress)) { LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!"); - failure(); + progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); return; } } - // Acquire the HLE mutex - std::lock_guard lock{HLE::g_hle_lock}; - callback(true); + progress.FinishDownload(RESULT_SUCCESS); } -bool Boxcat::Synchronize(TitleIDVersion title, CompletionCallback callback) { +bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) { is_syncing.exchange(true); - std::thread(&SynchronizeInternal, dir_getter, title, callback, std::nullopt).detach(); + std::thread([this, title, &progress] { SynchronizeInternal(dir_getter, title, progress); }) + .detach(); return true; } bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name, - CompletionCallback callback) { + ProgressServiceBackend& progress) { is_syncing.exchange(true); - std::thread(&SynchronizeInternal, dir_getter, title, callback, name).detach(); + std::thread( + [this, title, name, &progress] { SynchronizeInternal(dir_getter, title, progress, name); }) + .detach(); return true; } diff --git a/src/core/hle/service/bcat/backend/boxcat.h b/src/core/hle/service/bcat/backend/boxcat.h index 1148a4eca..601151189 100644 --- a/src/core/hle/service/bcat/backend/boxcat.h +++ b/src/core/hle/service/bcat/backend/boxcat.h @@ -21,16 +21,16 @@ struct EventStatus { /// doesn't require a switch or nintendo account. The content is controlled by the yuzu team. class Boxcat final : public Backend { friend void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, - CompletionCallback callback, + ProgressServiceBackend& progress, std::optional dir_name); public: explicit Boxcat(DirectoryGetter getter); ~Boxcat() override; - bool Synchronize(TitleIDVersion title, CompletionCallback callback) override; + bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override; bool SynchronizeDirectory(TitleIDVersion title, std::string name, - CompletionCallback callback) override; + ProgressServiceBackend& progress) override; bool Clear(u64 title_id) override; diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index a8d545992..d5f9e9d3b 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -33,20 +33,6 @@ constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400}; using BCATDigest = std::array; -struct DeliveryCacheProgressImpl { - enum class Status : u8 { - Incomplete = 0x1, - Complete = 0x9, - }; - - Status status = Status::Incomplete; - INSERT_PADDING_BYTES( - 0x1FF); ///< TODO(DarkLordZach): RE this structure. It just seems to convey info about the - ///< progress of the BCAT sync, but for us just setting completion works. -}; -static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200, - "DeliveryCacheProgressImpl has incorrect size."); - namespace { u64 GetCurrentBuildID() { @@ -84,19 +70,16 @@ bool VerifyNameValidInternal(Kernel::HLERequestContext& ctx, std::array name) { +bool VerifyNameValidDir(Kernel::HLERequestContext& ctx, DirectoryName name) { return VerifyNameValidInternal(ctx, name, '-'); } -bool VerifyNameValidFile(Kernel::HLERequestContext& ctx, std::array name) { +bool VerifyNameValidFile(Kernel::HLERequestContext& ctx, FileName name) { return VerifyNameValidInternal(ctx, name, '.'); } } // Anonymous namespace -using DirectoryName = std::array; -using FileName = std::array; - struct DeliveryCacheDirectoryEntry { FileName name; u64 size; @@ -162,15 +145,6 @@ public: }; // clang-format on RegisterHandlers(functions); - - auto& kernel{Core::System::GetInstance().Kernel()}; - progress.at(static_cast(SyncType::Normal)).event = - Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, - "BCAT::IDeliveryCacheProgressEvent"); - progress.at(static_cast(SyncType::Directory)).event = - Kernel::WritableEvent::CreateEventPair( - kernel, Kernel::ResetType::OneShot, - "BCAT::IDeliveryCacheProgressEvent::DirectoryName"); } private: @@ -180,24 +154,17 @@ private: Count, }; - std::function CreateCallback(SyncType type) { - return [this, type](bool success) { - auto& pair{progress.at(static_cast(type))}; - pair.impl.status = DeliveryCacheProgressImpl::Status::Complete; - pair.event.writable->Signal(); - }; - } - std::shared_ptr CreateProgressService(SyncType type) { - const auto& pair{progress.at(static_cast(type))}; - return std::make_shared(pair.event.readable, pair.impl); + auto& backend{progress.at(static_cast(type))}; + return std::make_shared(backend.GetEvent(), + backend.GetImpl()); } void RequestSyncDeliveryCache(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_BCAT, "called"); backend.Synchronize({Core::CurrentProcess()->GetTitleID(), GetCurrentBuildID()}, - CreateCallback(SyncType::Normal)); + progress.at(static_cast(SyncType::Normal))); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -213,7 +180,8 @@ private: LOG_DEBUG(Service_BCAT, "called, name={}", name); backend.SynchronizeDirectory({Core::CurrentProcess()->GetTitleID(), GetCurrentBuildID()}, - name, CreateCallback(SyncType::Directory)); + name, + progress.at(static_cast(SyncType::Directory))); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -278,12 +246,10 @@ private: Backend& backend; - struct ProgressPair { - Kernel::EventPair event; - DeliveryCacheProgressImpl impl; + std::array(SyncType::Count)> progress{ + ProgressServiceBackend{"Normal"}, + ProgressServiceBackend{"Directory"}, }; - - std::array(SyncType::Count)> progress{}; }; void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { -- cgit v1.2.3 From bcf1eafb8bd1a810fd33a7e7e06a86173b4bfb9f Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 20 Jun 2019 20:31:17 -0400 Subject: boxcat: Implement events global field --- src/core/hle/service/bcat/backend/backend.cpp | 13 ++++++++----- src/core/hle/service/bcat/backend/boxcat.cpp | 5 ++--- src/core/hle/service/bcat/module.cpp | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp index e389ad568..9b677debe 100644 --- a/src/core/hle/service/bcat/backend/backend.cpp +++ b/src/core/hle/service/bcat/backend/backend.cpp @@ -13,7 +13,7 @@ namespace Service::BCAT { ProgressServiceBackend::ProgressServiceBackend(std::string event_name) : impl{} { auto& kernel{Core::System::GetInstance().Kernel()}; event = Kernel::WritableEvent::CreateEventPair( - kernel, Kernel::ResetType::OneShot, "ProgressServiceBackend:UpdateEvent:" + event_name); + kernel, Kernel::ResetType::Automatic, "ProgressServiceBackend:UpdateEvent:" + event_name); } Kernel::SharedPtr ProgressServiceBackend::GetEvent() { @@ -48,8 +48,10 @@ void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name, impl.status = DeliveryCacheProgressImpl::Status::Downloading; impl.current_downloaded_bytes = 0; impl.current_total_bytes = file_size; - std::memcpy(impl.current_directory.data(), dir_name.data(), std::min(dir_name.size(), 0x31ull)); - std::memcpy(impl.current_file.data(), file_name.data(), std::min(file_name.size(), 0x31ull)); + std::memcpy(impl.current_directory.data(), dir_name.data(), + std::min(dir_name.size(), 0x31ull)); + std::memcpy(impl.current_file.data(), file_name.data(), + std::min(file_name.size(), 0x31ull)); SignalUpdate(); } @@ -68,7 +70,8 @@ void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) { impl.current_file.fill(0); impl.current_downloaded_bytes = 0; impl.current_total_bytes = 0; - std::memcpy(impl.current_directory.data(), dir_name.data(), std::min(dir_name.size(), 0x31ull)); + std::memcpy(impl.current_directory.data(), dir_name.data(), + std::min(dir_name.size(), 0x31ull)); SignalUpdate(); } @@ -121,7 +124,7 @@ bool NullBackend::Clear(u64 title_id) { void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) { LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase = {}", title_id, - Common::HexArrayToString(passphrase)); + Common::HexToString(passphrase)); } std::optional> NullBackend::GetLaunchParameter(TitleIDVersion title) { diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index 3754594df..5bc2e22d7 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp @@ -214,8 +214,7 @@ private: std::vector bytes(file.GetSize()); file.ReadBytes(bytes.data(), bytes.size()); const auto digest = DigestFile(bytes); - headers.insert( - {std::string("If-None-Match"), Common::HexArrayToString(digest, false)}); + headers.insert({std::string("If-None-Match"), Common::HexToString(digest, false)}); } } @@ -402,7 +401,7 @@ bool Boxcat::Clear(u64 title_id) { void Boxcat::SetPassphrase(u64 title_id, const Passphrase& passphrase) { LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, - Common::HexArrayToString(passphrase)); + Common::HexToString(passphrase)); } std::optional> Boxcat::GetLaunchParameter(TitleIDVersion title) { diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index d5f9e9d3b..1b9a75a1c 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -195,7 +195,7 @@ private: const auto passphrase_raw = ctx.ReadBuffer(); LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, - Common::HexVectorToString(passphrase_raw)); + Common::HexToString(passphrase_raw)); if (title_id == 0) { LOG_ERROR(Service_BCAT, "Invalid title ID!"); @@ -335,7 +335,7 @@ private: rb.Push(ERROR_NO_OPEN_ENTITY); } - size = std::min(current_file->GetSize() - offset, size); + size = std::min(current_file->GetSize() - offset, size); const auto buffer = current_file->ReadBytes(size, offset); ctx.WriteBuffer(buffer); @@ -437,7 +437,7 @@ private: } const auto files = current_dir->GetFiles(); - write_size = std::min(write_size, files.size()); + write_size = std::min(write_size, files.size()); std::vector entries(write_size); std::transform( files.begin(), files.begin() + write_size, entries.begin(), [](const auto& file) { @@ -519,7 +519,7 @@ private: LOG_DEBUG(Service_BCAT, "called, size={:016X}", size); - size = std::min(size, entries.size() - next_read_index); + size = std::min(size, entries.size() - next_read_index); ctx.WriteBuffer(entries.data() + next_read_index, size * sizeof(DirectoryName)); next_read_index += size; -- cgit v1.2.3 From 19c466dfb1f997eaa16fc9d9b832aaf3321adc40 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 1 Oct 2019 09:13:09 -0400 Subject: bcat: Add FSC accessors for BCAT data Ports BCAT to use FSC interface --- src/core/hle/service/am/am.cpp | 3 ++- src/core/hle/service/bcat/backend/backend.h | 10 +++++----- src/core/hle/service/bcat/backend/boxcat.cpp | 11 ++++++----- src/core/hle/service/bcat/bcat.cpp | 4 ++-- src/core/hle/service/bcat/bcat.h | 3 ++- src/core/hle/service/bcat/module.cpp | 26 +++++++++++++++----------- src/core/hle/service/bcat/module.h | 19 +++++++++++++++---- src/core/hle/service/filesystem/filesystem.cpp | 2 +- src/core/hle/service/filesystem/filesystem.h | 2 ++ src/core/hle/service/service.cpp | 2 +- 10 files changed, 51 insertions(+), 31 deletions(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 79f9a393e..34409e0c3 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1140,7 +1140,8 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called, kind={:08X}", static_cast(kind)); if (kind == LaunchParameterKind::ApplicationSpecific && !launch_popped_application_specific) { - const auto backend = BCAT::CreateBackendFromSettings(&FileSystem::GetBCATDirectory); + const auto backend = BCAT::CreateBackendFromSettings( + [this](u64 tid) { return system.GetFileSystemController().GetBCATDirectory(tid); }); const auto build_id_full = Core::System::GetInstance().GetCurrentProcessBuildID(); u64 build_id{}; std::memcpy(&build_id, build_id_full.data(), sizeof(u64)); diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h index 50973a13a..3f5d8b5dd 100644 --- a/src/core/hle/service/bcat/backend/backend.h +++ b/src/core/hle/service/bcat/backend/backend.h @@ -57,11 +57,6 @@ static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200, class ProgressServiceBackend { friend class IBcatService; - ProgressServiceBackend(std::string event_name); - - Kernel::SharedPtr GetEvent(); - DeliveryCacheProgressImpl& GetImpl(); - public: // Clients should call this with true if any of the functions are going to be called from a // non-HLE thread and this class need to lock the hle mutex. (default is false) @@ -90,6 +85,11 @@ public: void FinishDownload(ResultCode result); private: + explicit ProgressServiceBackend(std::string event_name); + + Kernel::SharedPtr GetEvent(); + DeliveryCacheProgressImpl& GetImpl(); + void SignalUpdate() const; DeliveryCacheProgressImpl impl; diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index 5bc2e22d7..2c3309268 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp @@ -364,17 +364,18 @@ void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) { is_syncing.exchange(true); - std::thread([this, title, &progress] { SynchronizeInternal(dir_getter, title, progress); }) - .detach(); + std::thread([this, title, &progress] { + SynchronizeInternal(dir_getter, title, progress); + }).detach(); return true; } bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name, ProgressServiceBackend& progress) { is_syncing.exchange(true); - std::thread( - [this, title, name, &progress] { SynchronizeInternal(dir_getter, title, progress, name); }) - .detach(); + std::thread([this, title, name, &progress] { + SynchronizeInternal(dir_getter, title, progress, name); + }).detach(); return true; } diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp index 391f599ee..c2f946424 100644 --- a/src/core/hle/service/bcat/bcat.cpp +++ b/src/core/hle/service/bcat/bcat.cpp @@ -6,8 +6,8 @@ namespace Service::BCAT { -BCAT::BCAT(std::shared_ptr module, const char* name) - : Module::Interface(std::move(module), name) { +BCAT::BCAT(std::shared_ptr module, FileSystem::FileSystemController& fsc, const char* name) + : Module::Interface(std::move(module), fsc, name) { // clang-format off static const FunctionInfo functions[] = { {0, &BCAT::CreateBcatService, "CreateBcatService"}, diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h index 802bd689a..813073658 100644 --- a/src/core/hle/service/bcat/bcat.h +++ b/src/core/hle/service/bcat/bcat.h @@ -10,7 +10,8 @@ namespace Service::BCAT { class BCAT final : public Module::Interface { public: - explicit BCAT(std::shared_ptr module, const char* name); + explicit BCAT(std::shared_ptr module, FileSystem::FileSystemController& fsc, + const char* name); ~BCAT() override; }; diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 1b9a75a1c..b3fed56c7 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -539,7 +539,7 @@ void Module::Interface::CreateDeliveryCacheStorageService(Kernel::HLERequestCont IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface( - Service::FileSystem::GetBCATDirectory(Core::CurrentProcess()->GetTitleID())); + fsc.GetBCATDirectory(Core::CurrentProcess()->GetTitleID())); } void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId( @@ -551,8 +551,7 @@ void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId( IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface( - Service::FileSystem::GetBCATDirectory(title_id)); + rb.PushIpcInterface(fsc.GetBCATDirectory(title_id)); } std::unique_ptr CreateBackendFromSettings(DirectoryGetter getter) { @@ -566,18 +565,23 @@ std::unique_ptr CreateBackendFromSettings(DirectoryGetter getter) { return std::make_unique(std::move(getter)); } -Module::Interface::Interface(std::shared_ptr module, const char* name) - : ServiceFramework(name), module(std::move(module)), - backend(CreateBackendFromSettings(&Service::FileSystem::GetBCATDirectory)) {} +Module::Interface::Interface(std::shared_ptr module, FileSystem::FileSystemController& fsc, + const char* name) + : ServiceFramework(name), module(std::move(module)), fsc(fsc), + backend(CreateBackendFromSettings([&fsc](u64 tid) { return fsc.GetBCATDirectory(tid); })) {} Module::Interface::~Interface() = default; -void InstallInterfaces(SM::ServiceManager& service_manager) { +void InstallInterfaces(Core::System& system) { auto module = std::make_shared(); - std::make_shared(module, "bcat:a")->InstallAsService(service_manager); - std::make_shared(module, "bcat:m")->InstallAsService(service_manager); - std::make_shared(module, "bcat:u")->InstallAsService(service_manager); - std::make_shared(module, "bcat:s")->InstallAsService(service_manager); + std::make_shared(module, system.GetFileSystemController(), "bcat:a") + ->InstallAsService(system.ServiceManager()); + std::make_shared(module, system.GetFileSystemController(), "bcat:m") + ->InstallAsService(system.ServiceManager()); + std::make_shared(module, system.GetFileSystemController(), "bcat:u") + ->InstallAsService(system.ServiceManager()); + std::make_shared(module, system.GetFileSystemController(), "bcat:s") + ->InstallAsService(system.ServiceManager()); } } // namespace Service::BCAT diff --git a/src/core/hle/service/bcat/module.h b/src/core/hle/service/bcat/module.h index fc52574c2..27469926a 100644 --- a/src/core/hle/service/bcat/module.h +++ b/src/core/hle/service/bcat/module.h @@ -6,7 +6,13 @@ #include "core/hle/service/service.h" -namespace Service::BCAT { +namespace Service { + +namespace FileSystem { +class FileSystemController; +} // namespace FileSystem + +namespace BCAT { class Backend; @@ -14,7 +20,8 @@ class Module final { public: class Interface : public ServiceFramework { public: - explicit Interface(std::shared_ptr module, const char* name); + explicit Interface(std::shared_ptr module, FileSystem::FileSystemController& fsc, + const char* name); ~Interface() override; void CreateBcatService(Kernel::HLERequestContext& ctx); @@ -22,12 +29,16 @@ public: void CreateDeliveryCacheStorageServiceWithApplicationId(Kernel::HLERequestContext& ctx); protected: + FileSystem::FileSystemController& fsc; + std::shared_ptr module; std::unique_ptr backend; }; }; /// Registers all BCAT services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(Core::System& system); + +} // namespace BCAT -} // namespace Service::BCAT +} // namespace Service diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 9cb107668..7fa4e820b 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -674,7 +674,7 @@ FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) return bis_factory->GetModificationDumpRoot(title_id); } -FileSys::VirtualDir GetBCATDirectory(u64 title_id) { +FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const { LOG_TRACE(Service_FS, "Opening BCAT root for tid={:016X}", title_id); if (bis_factory == nullptr) diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 3e0c03ec0..e6b49d8a2 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -110,6 +110,8 @@ public: FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const; FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const; + FileSys::VirtualDir GetBCATDirectory(u64 title_id) const; + // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function // above is called. void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 831a427de..f2c6fe9dc 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -208,7 +208,7 @@ void Init(std::shared_ptr& sm, Core::System& system) { AOC::InstallInterfaces(*sm, system); APM::InstallInterfaces(system); Audio::InstallInterfaces(*sm, system); - BCAT::InstallInterfaces(*sm); + BCAT::InstallInterfaces(system); BPC::InstallInterfaces(*sm); BtDrv::InstallInterfaces(*sm, system); BTM::InstallInterfaces(*sm, system); -- cgit v1.2.3 From 5d86c52a3a5ec1037cc30e1a6d8abf914e3055a4 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 1 Oct 2019 09:13:31 -0400 Subject: boxcat: Use updated game-asset API URL and tags --- src/core/hle/service/bcat/backend/boxcat.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index 2c3309268..36eb2c094 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp @@ -39,10 +39,10 @@ constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1}; constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org"; // Formatted using fmt with arg[0] = hex title id -constexpr char BOXCAT_PATHNAME_DATA[] = "/boxcat/titles/{:016X}/data"; -constexpr char BOXCAT_PATHNAME_LAUNCHPARAM[] = "/boxcat/titles/{:016X}/launchparam"; +constexpr char BOXCAT_PATHNAME_DATA[] = "/game-assets/{:016X}/boxcat"; +constexpr char BOXCAT_PATHNAME_LAUNCHPARAM[] = "/game-assets/{:016X}/launchparam"; -constexpr char BOXCAT_PATHNAME_EVENTS[] = "/boxcat/events"; +constexpr char BOXCAT_PATHNAME_EVENTS[] = "/game-assets/boxcat/events"; constexpr char BOXCAT_API_VERSION[] = "1"; constexpr char BOXCAT_CLIENT_TYPE[] = "yuzu"; @@ -203,9 +203,9 @@ private: } httplib::Headers headers{ - {std::string("Boxcat-Client-Version"), std::string(BOXCAT_API_VERSION)}, + {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)}, {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)}, - {std::string("Boxcat-Build-Id"), fmt::format("{:016X}", build_id)}, + {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)}, }; if (FileUtil::Exists(path)) { @@ -444,7 +444,7 @@ Boxcat::StatusResult Boxcat::GetStatus(std::optional& global, static_cast(TIMEOUT_SECONDS)}; httplib::Headers headers{ - {std::string("Boxcat-Client-Version"), std::string(BOXCAT_API_VERSION)}, + {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)}, {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)}, }; -- cgit v1.2.3 From e55d086cc93ea33829e77a2e92be52bcf900767b Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Wed, 2 Oct 2019 08:35:39 -0400 Subject: qt: Add service dialog --- src/core/hle/service/bcat/backend/boxcat.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index 36eb2c094..e6ee0810b 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp @@ -364,18 +364,17 @@ void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) { is_syncing.exchange(true); - std::thread([this, title, &progress] { - SynchronizeInternal(dir_getter, title, progress); - }).detach(); + std::thread([this, title, &progress] { SynchronizeInternal(dir_getter, title, progress); }) + .detach(); return true; } bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name, ProgressServiceBackend& progress) { is_syncing.exchange(true); - std::thread([this, title, name, &progress] { - SynchronizeInternal(dir_getter, title, progress, name); - }).detach(); + std::thread( + [this, title, name, &progress] { SynchronizeInternal(dir_getter, title, progress, name); }) + .detach(); return true; } -- cgit v1.2.3