summaryrefslogtreecommitdiffstats
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/hle/kernel/handle_table.cpp2
-rw-r--r--src/core/hle/service/am/am.cpp69
-rw-r--r--src/core/hle/service/am/am.h5
-rw-r--r--src/core/hle/service/am/applets/applets.cpp4
-rw-r--r--src/core/hle/service/am/applets/applets.h2
-rw-r--r--src/core/hle/service/apm/controller.cpp3
-rw-r--r--src/core/hle/service/apm/controller.h6
-rw-r--r--src/core/hle/service/audio/audout_u.cpp2
-rw-r--r--src/core/hle/service/bcat/backend/backend.cpp137
-rw-r--r--src/core/hle/service/bcat/backend/backend.h150
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp504
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.h58
-rw-r--r--src/core/hle/service/bcat/bcat.cpp9
-rw-r--r--src/core/hle/service/bcat/bcat.h7
-rw-r--r--src/core/hle/service/bcat/module.cpp561
-rw-r--r--src/core/hle/service/bcat/module.h31
-rw-r--r--src/core/hle/service/fatal/fatal.cpp2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp29
-rw-r--r--src/core/hle/service/filesystem/filesystem.h10
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp2
-rw-r--r--src/core/hle/service/friend/friend.cpp1
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp5
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h1
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h1
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h1
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h1
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp18
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h1
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h1
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h1
-rw-r--r--src/core/hle/service/hid/hid.cpp19
-rw-r--r--src/core/hle/service/hid/hid.h1
-rw-r--r--src/core/hle/service/ldr/ldr.cpp6
-rw-r--r--src/core/hle/service/nfp/nfp.cpp9
-rw-r--r--src/core/hle/service/nifm/nifm.cpp13
-rw-r--r--src/core/hle/service/ns/pl_u.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp2
-rw-r--r--src/core/hle/service/service.cpp2
47 files changed, 1601 insertions, 111 deletions
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index bdfaa977f..2cc5d536b 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -103,7 +103,7 @@ SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
if (handle == CurrentThread) {
return GetCurrentThread();
} else if (handle == CurrentProcess) {
- return Core::CurrentProcess();
+ return Core::System::GetInstance().CurrentProcess();
}
if (!IsValid(handle)) {
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 797c9a06f..941ebc93a 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,55 @@ 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<LaunchParameterKind>();
- LaunchParameters params{};
+ LOG_DEBUG(Service_AM, "called, kind={:08X}", static_cast<u8>(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(
+ [this](u64 tid) { return system.GetFileSystemController().GetBCATDirectory(tid); });
+ const auto build_id_full = system.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({system.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<AM::IStorage>(*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<u8> buffer(sizeof(LaunchParameters));
- std::memcpy(buffer.data(), &params, 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<AM::IStorage>(buffer);
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+
+ rb.Push(RESULT_SUCCESS);
+
+ std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));
+ std::memcpy(buffer.data(), &params, buffer.size());
+
+ rb.PushIpcInterface<AM::IStorage>(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(
@@ -1165,7 +1200,7 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
FileSys::SaveDataDescriptor descriptor{};
- descriptor.title_id = Core::CurrentProcess()->GetTitleID();
+ descriptor.title_id = system.CurrentProcess()->GetTitleID();
descriptor.user_id = user_id;
descriptor.type = FileSys::SaveDataType::SaveData;
const auto res = system.GetFileSystemController().CreateSaveData(
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index a3baeb673..ccd053c13 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -147,6 +147,7 @@ private:
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
+ Core::System& system;
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
Kernel::EventPair launchable_event;
Kernel::EventPair accumulated_suspended_tick_changed_event;
@@ -154,8 +155,6 @@ private:
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
bool is_auto_sleep_disabled = false;
-
- Core::System& system;
};
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
@@ -255,6 +254,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;
};
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();
diff --git a/src/core/hle/service/apm/controller.cpp b/src/core/hle/service/apm/controller.cpp
index 4376612eb..073d0f6fa 100644
--- a/src/core/hle/service/apm/controller.cpp
+++ b/src/core/hle/service/apm/controller.cpp
@@ -13,7 +13,7 @@ constexpr PerformanceConfiguration DEFAULT_PERFORMANCE_CONFIGURATION =
PerformanceConfiguration::Config7;
Controller::Controller(Core::Timing::CoreTiming& core_timing)
- : core_timing(core_timing), configs{
+ : core_timing{core_timing}, configs{
{PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION},
{PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION},
} {}
@@ -63,6 +63,7 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa
void Controller::SetClockSpeed(u32 mhz) {
LOG_INFO(Service_APM, "called, mhz={:08X}", mhz);
// TODO(DarkLordZach): Actually signal core_timing to change clock speed.
+ // TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used.
}
} // namespace Service::APM
diff --git a/src/core/hle/service/apm/controller.h b/src/core/hle/service/apm/controller.h
index 8ac80eaea..454caa6eb 100644
--- a/src/core/hle/service/apm/controller.h
+++ b/src/core/hle/service/apm/controller.h
@@ -50,7 +50,7 @@ enum class PerformanceMode : u8 {
// system during times of high load -- this simply maps to different PerformanceConfigs to use.
class Controller {
public:
- Controller(Core::Timing::CoreTiming& core_timing);
+ explicit Controller(Core::Timing::CoreTiming& core_timing);
~Controller();
void SetPerformanceConfiguration(PerformanceMode mode, PerformanceConfiguration config);
@@ -62,9 +62,9 @@ public:
private:
void SetClockSpeed(u32 mhz);
- std::map<PerformanceMode, PerformanceConfiguration> configs;
+ [[maybe_unused]] Core::Timing::CoreTiming& core_timing;
- Core::Timing::CoreTiming& core_timing;
+ std::map<PerformanceMode, PerformanceConfiguration> configs;
};
} // namespace Service::APM
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index fb84a8f13..9afefb5c6 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -205,7 +205,7 @@ private:
AudioCore::StreamPtr stream;
std::string device_name;
- AudoutParams audio_params{};
+ [[maybe_unused]] AudoutParams audio_params {};
/// This is the event handle used to check if the audio buffer was released
Kernel::EventPair buffer_event;
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..9d6946bc5
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/backend.cpp
@@ -0,0 +1,137 @@
+// 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/core.h"
+#include "core/hle/lock.h"
+#include "core/hle/service/bcat/backend/backend.h"
+
+namespace Service::BCAT {
+
+ProgressServiceBackend::ProgressServiceBackend(std::string_view event_name) {
+ auto& kernel{Core::System::GetInstance().Kernel()};
+ event = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::Automatic,
+ std::string("ProgressServiceBackend:UpdateEvent:").append(event_name));
+}
+
+Kernel::SharedPtr<Kernel::ReadableEvent> ProgressServiceBackend::GetEvent() const {
+ 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<u64>(dir_name.size(), 0x31ull));
+ std::memcpy(impl.current_file.data(), file_name.data(),
+ std::min<u64>(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<u64>(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<std::recursive_mutex> lock(HLE::g_hle_lock);
+ event.writable->Signal();
+ } else {
+ event.writable->Signal();
+ }
+}
+
+Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
+
+Backend::~Backend() = default;
+
+NullBackend::NullBackend(DirectoryGetter getter) : Backend(std::move(getter)) {}
+
+NullBackend::~NullBackend() = default;
+
+bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
+ LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
+ title.build_id);
+
+ progress.FinishDownload(RESULT_SUCCESS);
+ return true;
+}
+
+bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
+ ProgressServiceBackend& progress) {
+ LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id,
+ title.build_id, name);
+
+ progress.FinishDownload(RESULT_SUCCESS);
+ 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::HexToString(passphrase));
+}
+
+std::optional<std::vector<u8>> 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
new file mode 100644
index 000000000..51dbd3316
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/backend.h
@@ -0,0 +1,150 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <functional>
+#include <optional>
+#include <string>
+#include <string_view>
+
+#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 {
+
+struct DeliveryCacheProgressImpl;
+
+using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>;
+using Passphrase = std::array<u8, 0x20>;
+
+struct TitleIDVersion {
+ u64 title_id;
+ u64 build_id;
+};
+
+using DirectoryName = std::array<char, 0x20>;
+using FileName = std::array<char, 0x20>;
+
+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;
+
+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:
+ explicit ProgressServiceBackend(std::string_view event_name);
+
+ Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent() const;
+ DeliveryCacheProgressImpl& GetImpl();
+
+ 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();
+
+ // 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,
+ 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<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) = 0;
+
+protected:
+ DirectoryGetter dir_getter;
+};
+
+// A backend of BCAT that provides no operation.
+class NullBackend : public Backend {
+public:
+ explicit NullBackend(DirectoryGetter getter);
+ ~NullBackend() override;
+
+ bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
+ bool SynchronizeDirectory(TitleIDVersion title, std::string name,
+ ProgressServiceBackend& progress) override;
+
+ bool Clear(u64 title_id) override;
+
+ void SetPassphrase(u64 title_id, const Passphrase& passphrase) override;
+
+ std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
+};
+
+std::unique_ptr<Backend> CreateBackendFromSettings(DirectoryGetter getter);
+
+} // namespace Service::BCAT
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..64022982b
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -0,0 +1,504 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <fmt/ostream.h>
+#include <httplib.h>
+#include <json.hpp>
+#include <mbedtls/sha256.h>
+#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/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
+constexpr char BOXCAT_PATHNAME_DATA[] = "/game-assets/{:016X}/boxcat";
+constexpr char BOXCAT_PATHNAME_LAUNCHPARAM[] = "/game-assets/{:016X}/launchparam";
+
+constexpr char BOXCAT_PATHNAME_EVENTS[] = "/game-assets/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.
+ 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<const char*, 8> 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<std::size_t>(result));
+}
+
+constexpr u32 PORT = 443;
+constexpr u32 TIMEOUT_SECONDS = 30;
+[[maybe_unused]] 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);
+}
+
+// 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<std::size_t>(res)], [] {});
+}
+
+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<u8> 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:
+ 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,
+ "application/zip");
+ }
+
+ DownloadResult DownloadLaunchParam() {
+ return DownloadInternal(fmt::format(BOXCAT_PATHNAME_LAUNCHPARAM, title_id),
+ TIMEOUT_SECONDS / 3, "application/octet-stream");
+ }
+
+private:
+ DownloadResult DownloadInternal(const std::string& resolved_path, u32 timeout_seconds,
+ const std::string& content_type_name) {
+ if (client == nullptr) {
+ client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT, timeout_seconds);
+ }
+
+ httplib::Headers headers{
+ {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
+ {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)},
+ {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)},
+ };
+
+ if (FileUtil::Exists(path)) {
+ FileUtil::IOFile file{path, "rb"};
+ if (file.IsOpen()) {
+ std::vector<u8> bytes(file.GetSize());
+ file.ReadBytes(bytes.data(), bytes.size());
+ const auto digest = DigestFile(bytes);
+ headers.insert({std::string("If-None-Match"), Common::HexToString(digest, false)});
+ }
+ }
+
+ const auto response = client->Get(resolved_path.c_str(), headers);
+ if (response == nullptr)
+ return DownloadResult::NoResponse;
+
+ if (response->status == static_cast<int>(ResponseStatus::NoUpdate))
+ return DownloadResult::Success;
+ if (response->status == static_cast<int>(ResponseStatus::BadClientVersion))
+ return DownloadResult::BadClientVersion;
+ if (response->status == static_cast<int>(ResponseStatus::NoMatchTitleId))
+ return DownloadResult::NoMatchTitleId;
+ if (response->status == static_cast<int>(ResponseStatus::NoMatchBuildId))
+ return DownloadResult::NoMatchBuildId;
+ if (response->status != static_cast<int>(ResponseStatus::Ok))
+ return DownloadResult::GeneralWebError;
+
+ const auto content_type = response->headers.find("content-type");
+ if (content_type == response->headers.end() ||
+ content_type->second.find(content_type_name) == std::string::npos) {
+ return DownloadResult::InvalidContentType;
+ }
+
+ FileUtil::CreateFullPath(path);
+ FileUtil::IOFile file{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;
+ }
+
+ using Digest = std::array<u8, 0x20>;
+ static Digest DigestFile(std::vector<u8> bytes) {
+ Digest out{};
+ mbedtls_sha256(bytes.data(), bytes.size(), out.data(), 0);
+ return out;
+ }
+
+ std::unique_ptr<httplib::Client> client;
+ std::string 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,
+ ProgressServiceBackend& progress,
+ std::optional<std::string> dir_name = {}) {
+ progress.SetNeedHLELock(true);
+
+ if (Settings::values.bcat_boxcat_local) {
+ LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download.");
+ 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);
+
+ if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
+ FileUtil::Delete(zip_path);
+ }
+
+ HandleDownloadDisplayResult(res);
+ progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
+ return;
+ }
+
+ progress.StartProcessingDataList();
+
+ FileUtil::IOFile zip{zip_path, "rb"};
+ const auto size = zip.GetSize();
+ std::vector<u8> 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);
+ progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
+ return;
+ }
+
+ const auto extracted = FileSys::ExtractZIP(std::make_shared<FileSys::VectorVfsFile>(bytes));
+ if (extracted == nullptr) {
+ LOG_ERROR(Service_BCAT, "Boxcat failed to extract ZIP file!");
+ 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 || !VfsRawCopyDProgress(extracted, target_dir, progress)) {
+ LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!");
+ 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!");
+ 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<std::string> 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 ||
+ !VfsRawCopyDProgressSingle(source_sub, target_sub, progress)) {
+ LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!");
+ progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
+ return;
+ }
+ }
+
+ progress.FinishDownload(RESULT_SUCCESS);
+}
+
+bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
+ is_syncing.exchange(true);
+ 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();
+ 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<std::string> 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::HexToString(passphrase));
+}
+
+std::optional<std::vector<u8>> 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<u8> 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;
+ }
+
+ return bytes;
+}
+
+Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global,
+ std::map<std::string, EventStatus>& games) {
+ httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT),
+ static_cast<int>(TIMEOUT_SECONDS)};
+
+ httplib::Headers headers{
+ {std::string("Game-Assets-API-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);
+ if (response == nullptr)
+ return StatusResult::Offline;
+
+ if (response->status == static_cast<int>(ResponseStatus::BadClientVersion))
+ return StatusResult::BadClientVersion;
+
+ try {
+ nlohmann::json json = nlohmann::json::parse(response->body);
+
+ if (!json["online"].get<bool>())
+ return StatusResult::Offline;
+
+ if (json["global"].is_null())
+ global = std::nullopt;
+ else
+ global = json["global"].get<std::string>();
+
+ 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<std::string>();
+ } else {
+ detail.header = std::nullopt;
+ }
+
+ if (object["footer"].is_string()) {
+ detail.footer = object["footer"].get<std::string>();
+ } 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<std::string>());
+ }
+ }
+
+ games.insert_or_assign(object["name"], std::move(detail));
+ }
+ }
+ }
+
+ return StatusResult::Success;
+ } catch (const nlohmann::json::parse_error& error) {
+ LOG_ERROR(Service_BCAT, "{}", error.what());
+ 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..601151189
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/boxcat.h
@@ -0,0 +1,58 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <map>
+#include <optional>
+#include "core/hle/service/bcat/backend/backend.h"
+
+namespace Service::BCAT {
+
+struct EventStatus {
+ std::optional<std::string> header;
+ std::optional<std::string> footer;
+ std::vector<std::string> 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,
+ ProgressServiceBackend& progress,
+ std::optional<std::string> dir_name);
+
+public:
+ explicit Boxcat(DirectoryGetter getter);
+ ~Boxcat() override;
+
+ bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
+ bool SynchronizeDirectory(TitleIDVersion title, std::string name,
+ ProgressServiceBackend& progress) override;
+
+ bool Clear(u64 title_id) override;
+
+ void SetPassphrase(u64 title_id, const Passphrase& passphrase) override;
+
+ std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
+
+ enum class StatusResult {
+ Success,
+ Offline,
+ ParseError,
+ BadClientVersion,
+ };
+
+ static StatusResult GetStatus(std::optional<std::string>& global,
+ std::map<std::string, EventStatus>& games);
+
+private:
+ std::atomic_bool is_syncing{false};
+
+ class Client;
+ std::unique_ptr<Client> client;
+};
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp
index 179aa4949..8bb2528c9 100644
--- a/src/core/hle/service/bcat/bcat.cpp
+++ b/src/core/hle/service/bcat/bcat.cpp
@@ -6,11 +6,16 @@
namespace Service::BCAT {
-BCAT::BCAT(std::shared_ptr<Module> module, const char* name)
- : Module::Interface(std::move(module), name) {
+BCAT::BCAT(Core::System& system, std::shared_ptr<Module> module,
+ FileSystem::FileSystemController& fsc, const char* name)
+ : Interface(system, std::move(module), fsc, 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/bcat.h b/src/core/hle/service/bcat/bcat.h
index 802bd689a..6354465fc 100644
--- a/src/core/hle/service/bcat/bcat.h
+++ b/src/core/hle/service/bcat/bcat.h
@@ -6,11 +6,16 @@
#include "core/hle/service/bcat/module.h"
+namespace Core {
+class System;
+}
+
namespace Service::BCAT {
class BCAT final : public Module::Interface {
public:
- explicit BCAT(std::shared_ptr<Module> module, const char* name);
+ explicit BCAT(Core::System& system, std::shared_ptr<Module> 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 b7bd738fc..4e4aa758b 100644
--- a/src/core/hle/service/bcat/module.cpp
+++ b/src/core/hle/service/bcat/module.cpp
@@ -2,34 +2,257 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <cctype>
+#include <mbedtls/md5.h>
+#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<u8, 0x10>;
+
+namespace {
+
+u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
+ 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<char, 0x20> 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<u8>(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, DirectoryName name) {
+ return VerifyNameValidInternal(ctx, name, '-');
+}
+
+bool VerifyNameValidFile(Kernel::HLERequestContext& ctx, FileName name) {
+ return VerifyNameValidInternal(ctx, name, '.');
+}
+
+} // Anonymous namespace
+
+struct DeliveryCacheDirectoryEntry {
+ FileName name;
+ u64 size;
+ BCATDigest digest;
+};
+
+class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> {
+public:
+ IDeliveryCacheProgressService(Kernel::SharedPtr<Kernel::ReadableEvent> 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<Kernel::ReadableEvent> event;
+ const DeliveryCacheProgressImpl& impl;
+};
+
class IBcatService final : public ServiceFramework<IBcatService> {
public:
- IBcatService() : ServiceFramework("IBcatService") {
+ explicit IBcatService(Core::System& system_, Backend& backend_)
+ : ServiceFramework("IBcatService"), system{system_}, 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"},
- {30100, nullptr, "SetPassphrase"},
+ {30100, &IBcatService::SetPassphrase, "SetPassphrase"},
{30200, nullptr, "RegisterBackgroundDeliveryTask"},
{30201, nullptr, "UnregisterBackgroundDeliveryTask"},
{30202, nullptr, "BlockDeliveryTask"},
{30203, nullptr, "UnblockDeliveryTask"},
{90100, nullptr, "EnumerateBackgroundDeliveryTask"},
{90200, nullptr, "GetDeliveryList"},
- {90201, nullptr, "ClearDeliveryCacheStorage"},
+ {90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"},
{90300, nullptr, "GetPushNotificationLog"},
};
+ // clang-format on
RegisterHandlers(functions);
}
+
+private:
+ enum class SyncType {
+ Normal,
+ Directory,
+ Count,
+ };
+
+ std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) {
+ auto& backend{progress.at(static_cast<std::size_t>(type))};
+ return std::make_shared<IDeliveryCacheProgressService>(backend.GetEvent(),
+ backend.GetImpl());
+ }
+
+ void RequestSyncDeliveryCache(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ backend.Synchronize({system.CurrentProcess()->GetTitleID(),
+ GetCurrentBuildID(system.GetCurrentProcessBuildID())},
+ progress.at(static_cast<std::size_t>(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<DirectoryName>();
+ const auto name =
+ Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
+
+ LOG_DEBUG(Service_BCAT, "called, name={}", name);
+
+ backend.SynchronizeDirectory({system.CurrentProcess()->GetTitleID(),
+ GetCurrentBuildID(system.GetCurrentProcessBuildID())},
+ name,
+ progress.at(static_cast<std::size_t>(SyncType::Directory)));
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface(CreateProgressService(SyncType::Directory));
+ }
+
+ void SetPassphrase(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto title_id = rp.PopRaw<u64>();
+
+ const auto passphrase_raw = ctx.ReadBuffer();
+
+ LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
+ Common::HexToString(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);
+ }
+
+ void ClearDeliveryCacheStorage(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto title_id = rp.PopRaw<u64>();
+
+ 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);
+ }
+
+ Core::System& system;
+ Backend& backend;
+
+ std::array<ProgressServiceBackend, static_cast<std::size_t>(SyncType::Count)> progress{
+ ProgressServiceBackend{"Normal"},
+ ProgressServiceBackend{"Directory"},
+ };
};
void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
@@ -37,20 +260,332 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IBcatService>();
+ rb.PushIpcInterface<IBcatService>(system, *backend);
+}
+
+class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> {
+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<DirectoryName>();
+ const auto file_name_raw = rp.PopRaw<FileName>();
+
+ 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<u64>()};
+
+ 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<u64>(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<u64>(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<u64>(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<IDeliveryCacheDirectoryService> {
+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<DirectoryName>();
+ 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<u64>(write_size, files.size());
+ std::vector<DeliveryCacheDirectoryEntry> 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(static_cast<u32>(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(static_cast<u32>(files.size()));
+ }
+
+ FileSys::VirtualDir root;
+ FileSys::VirtualDir current_dir;
+};
+
+class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> {
+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<IDeliveryCacheFileService>(root);
+ }
+
+ void CreateDirectoryService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IDeliveryCacheDirectoryService>(root);
+ }
+
+ void EnumerateDeliveryCacheDirectory(Kernel::HLERequestContext& ctx) {
+ auto size = ctx.GetWriteBufferSize() / sizeof(DirectoryName);
+
+ LOG_DEBUG(Service_BCAT, "called, size={:016X}", size);
+
+ size = std::min<u64>(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(static_cast<u32>(size));
+ }
+
+ FileSys::VirtualDir root;
+ std::vector<DirectoryName> entries;
+ u64 next_read_index = 0;
+};
+
+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<IDeliveryCacheStorageService>(
+ fsc.GetBCATDirectory(system.CurrentProcess()->GetTitleID()));
+}
+
+void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId(
+ Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto title_id = rp.PopRaw<u64>();
+
+ LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IDeliveryCacheStorageService>(fsc.GetBCATDirectory(title_id));
+}
+
+std::unique_ptr<Backend> CreateBackendFromSettings(DirectoryGetter getter) {
+ const auto backend = Settings::values.bcat_backend;
+
+#ifdef YUZU_ENABLE_BOXCAT
+ if (backend == "boxcat")
+ return std::make_unique<Boxcat>(std::move(getter));
+#endif
+
+ return std::make_unique<NullBackend>(std::move(getter));
}
-Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
- : ServiceFramework(name), module(std::move(module)) {}
+Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
+ FileSystem::FileSystemController& fsc_, const char* name)
+ : ServiceFramework(name), fsc{fsc_}, module{std::move(module_)},
+ backend{CreateBackendFromSettings([&fsc_](u64 tid) { return fsc_.GetBCATDirectory(tid); })},
+ system{system_} {}
Module::Interface::~Interface() = default;
-void InstallInterfaces(SM::ServiceManager& service_manager) {
+void InstallInterfaces(Core::System& system) {
auto module = std::make_shared<Module>();
- std::make_shared<BCAT>(module, "bcat:a")->InstallAsService(service_manager);
- std::make_shared<BCAT>(module, "bcat:m")->InstallAsService(service_manager);
- std::make_shared<BCAT>(module, "bcat:u")->InstallAsService(service_manager);
- std::make_shared<BCAT>(module, "bcat:s")->InstallAsService(service_manager);
+ std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:a")
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:m")
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:u")
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<BCAT>(system, 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 f0d63cab0..e4ba23ba0 100644
--- a/src/core/hle/service/bcat/module.h
+++ b/src/core/hle/service/bcat/module.h
@@ -6,23 +6,46 @@
#include "core/hle/service/service.h"
-namespace Service::BCAT {
+namespace Core {
+class System;
+}
+
+namespace Service {
+
+namespace FileSystem {
+class FileSystemController;
+} // namespace FileSystem
+
+namespace BCAT {
+
+class Backend;
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
- explicit Interface(std::shared_ptr<Module> module, const char* name);
+ explicit Interface(Core::System& system_, std::shared_ptr<Module> module_,
+ FileSystem::FileSystemController& fsc_, const char* name);
~Interface() override;
void CreateBcatService(Kernel::HLERequestContext& ctx);
+ void CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx);
+ void CreateDeliveryCacheStorageServiceWithApplicationId(Kernel::HLERequestContext& ctx);
protected:
+ FileSystem::FileSystemController& fsc;
+
std::shared_ptr<Module> module;
+ std::unique_ptr<Backend> backend;
+
+ private:
+ Core::System& system;
};
};
/// 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/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index b2ebf6240..2546d7595 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -66,7 +66,7 @@ enum class FatalType : u32 {
static void GenerateErrorReport(Core::System& system, ResultCode error_code,
const FatalInfo& info) {
- const auto title_id = Core::CurrentProcess()->GetTitleID();
+ const auto title_id = system.CurrentProcess()->GetTitleID();
std::string crash_report = fmt::format(
"Yuzu {}-{} crash report\n"
"Title ID: {:016x}\n"
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 14cd0e322..11e5c56b7 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -241,7 +241,7 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
return FileSys::ERROR_PATH_NOT_FOUND;
}
-FileSystemController::FileSystemController() = default;
+FileSystemController::FileSystemController(Core::System& system_) : system{system_} {}
FileSystemController::~FileSystemController() = default;
@@ -290,7 +290,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess()
return ResultCode(-1);
}
- return romfs_factory->OpenCurrentProcess();
+ return romfs_factory->OpenCurrentProcess(system.CurrentProcess()->GetTitleID());
}
ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
@@ -447,10 +447,10 @@ FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataTy
FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
FileSys::NACP nacp;
- const auto res = Core::System::GetInstance().GetAppLoader().ReadControlData(nacp);
+ const auto res = system.GetAppLoader().ReadControlData(nacp);
if (res != Loader::ResultStatus::Success) {
- FileSys::PatchManager pm{Core::CurrentProcess()->GetTitleID()};
+ FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
auto [nacp_unique, discard] = pm.GetControlMetadata();
if (nacp_unique != nullptr) {
@@ -674,6 +674,15 @@ FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id)
return bis_factory->GetModificationDumpRoot(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)
+ return nullptr;
+
+ return bis_factory->GetBCATDirectory(title_id);
+}
+
void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
if (overwrite) {
bis_factory = nullptr;
@@ -693,10 +702,10 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
if (bis_factory == nullptr) {
bis_factory =
std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
- Core::System::GetInstance().RegisterContentProvider(
- FileSys::ContentProviderUnionSlot::SysNAND, bis_factory->GetSystemNANDContents());
- Core::System::GetInstance().RegisterContentProvider(
- FileSys::ContentProviderUnionSlot::UserNAND, bis_factory->GetUserNANDContents());
+ system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SysNAND,
+ bis_factory->GetSystemNANDContents());
+ system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::UserNAND,
+ bis_factory->GetUserNANDContents());
}
if (save_data_factory == nullptr) {
@@ -705,8 +714,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
if (sdmc_factory == nullptr) {
sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
- Core::System::GetInstance().RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
- sdmc_factory->GetSDMCContents());
+ system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
+ sdmc_factory->GetSDMCContents());
}
}
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 3e0c03ec0..1b0a6a949 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -10,6 +10,10 @@
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
+namespace Core {
+class System;
+}
+
namespace FileSys {
class BISFactory;
class RegisteredCache;
@@ -52,7 +56,7 @@ enum class ImageDirectoryId : u32 {
class FileSystemController {
public:
- FileSystemController();
+ explicit FileSystemController(Core::System& system_);
~FileSystemController();
ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
@@ -110,6 +114,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);
@@ -123,6 +129,8 @@ private:
std::unique_ptr<FileSys::XCI> gamecard;
std::unique_ptr<FileSys::RegisteredCache> gamecard_registered;
std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder;
+
+ Core::System& system;
};
void InstallInterfaces(Core::System& system);
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index eb982ad49..cbd5466c1 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -803,7 +803,7 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
- auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
+ [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
u128 uid = rp.PopRaw<u128>();
LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 42b4ee861..75dd9043b 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -237,7 +237,6 @@ private:
};
Common::UUID uuid;
- bool is_event_created = false;
Kernel::EventPair notification_event;
std::queue<SizedNotificationInfo> notifications;
States states{};
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 8e8263f5b..1f2131ec8 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -11,11 +11,10 @@
namespace Service::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
-constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
+[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
-Controller_DebugPad::Controller_DebugPad(Core::System& system)
- : ControllerBase(system), system(system) {}
+Controller_DebugPad::Controller_DebugPad(Core::System& system) : ControllerBase(system) {}
Controller_DebugPad::~Controller_DebugPad() = default;
void Controller_DebugPad::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 6c4de817e..555b29d76 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -89,6 +89,5 @@ private:
buttons;
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
analogs;
- Core::System& system;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 80da0a0d3..6e990dd00 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -10,8 +10,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
-Controller_Gesture::Controller_Gesture(Core::System& system)
- : ControllerBase(system), system(system) {}
+Controller_Gesture::Controller_Gesture(Core::System& system) : ControllerBase(system) {}
Controller_Gesture::~Controller_Gesture() = default;
void Controller_Gesture::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 396897527..f650b8338 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -59,6 +59,5 @@ private:
std::array<GestureState, 17> gesture_states;
};
SharedMemory shared_memory{};
- Core::System& system;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index e587b2e15..358cb9329 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -12,8 +12,7 @@ namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
constexpr u8 KEYS_PER_BYTE = 8;
-Controller_Keyboard::Controller_Keyboard(Core::System& system)
- : ControllerBase(system), system(system) {}
+Controller_Keyboard::Controller_Keyboard(Core::System& system) : ControllerBase(system) {}
Controller_Keyboard::~Controller_Keyboard() = default;
void Controller_Keyboard::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index ef586f7eb..f3eef5936 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -53,6 +53,5 @@ private:
keyboard_keys;
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
keyboard_mods;
- Core::System& system;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 88f2ca4c1..93d88ea50 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -11,7 +11,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
-Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system), system(system) {}
+Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system) {}
Controller_Mouse::~Controller_Mouse() = default;
void Controller_Mouse::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index df2da6ae3..357ab7107 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -53,6 +53,5 @@ private:
std::unique_ptr<Input::MouseDevice> mouse_device;
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
mouse_button_devices;
- Core::System& system;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 44b668fbf..a2b25a796 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -20,7 +20,7 @@
namespace Service::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
-constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
+[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr u32 BATTERY_FULL = 2;
constexpr u32 MAX_NPAD_ID = 7;
@@ -105,6 +105,8 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.joy_styles.raw = 0; // Zero out
controller.device_type.raw = 0;
switch (controller_type) {
+ case NPadControllerType::None:
+ UNREACHABLE();
case NPadControllerType::Handheld:
controller.joy_styles.handheld.Assign(1);
controller.device_type.handheld.Assign(1);
@@ -165,13 +167,14 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.battery_level[0] = BATTERY_FULL;
controller.battery_level[1] = BATTERY_FULL;
controller.battery_level[2] = BATTERY_FULL;
+ styleset_changed_events[controller_idx].writable->Signal();
}
void Controller_NPad::OnInit() {
auto& kernel = system.Kernel();
for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Automatic, fmt::format("npad:NpadStyleSetChanged_{}", i));
+ kernel, Kernel::ResetType::Manual, fmt::format("npad:NpadStyleSetChanged_{}", i));
}
if (!IsControllerActivated()) {
@@ -238,7 +241,7 @@ void Controller_NPad::OnRelease() {}
void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
const auto controller_idx = NPadIdToIndex(npad_id);
- const auto controller_type = connected_controllers[controller_idx].type;
+ [[maybe_unused]] const auto controller_type = connected_controllers[controller_idx].type;
if (!connected_controllers[controller_idx].is_connected) {
return;
}
@@ -345,6 +348,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_entry.connection_status.raw = 0;
switch (controller_type) {
+ case NPadControllerType::None:
+ UNREACHABLE();
case NPadControllerType::Handheld:
handheld_entry.connection_status.raw = 0;
handheld_entry.connection_status.IsWired.Assign(1);
@@ -433,7 +438,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
supported_npad_id_types.clear();
supported_npad_id_types.resize(length / sizeof(u32));
std::memcpy(supported_npad_id_types.data(), data, length);
- bool had_controller_update = false;
for (std::size_t i = 0; i < connected_controllers.size(); i++) {
auto& controller = connected_controllers[i];
if (!controller.is_connected) {
@@ -452,10 +456,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
controller.type = requested_controller;
InitNewlyAddedControler(i);
}
- had_controller_update = true;
- }
- if (had_controller_update) {
- styleset_changed_events[i].writable->Signal();
}
}
}
@@ -481,7 +481,6 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode)
const std::size_t npad_index = NPadIdToIndex(npad_id);
ASSERT(npad_index < shared_memory_entries.size());
if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) {
- styleset_changed_events[npad_index].writable->Signal();
shared_memory_entries[npad_index].pad_assignment = assignment_mode;
}
}
@@ -507,7 +506,6 @@ Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEven
// TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
// be signalled at least once, and signaled after a new controller is connected?
const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
- styleset_event.writable->Signal();
return styleset_event.readable;
}
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index 9b829341e..9e527d176 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -9,8 +9,7 @@
namespace Service::HID {
-Controller_Stubbed::Controller_Stubbed(Core::System& system)
- : ControllerBase(system), system(system) {}
+Controller_Stubbed::Controller_Stubbed(Core::System& system) : ControllerBase(system) {}
Controller_Stubbed::~Controller_Stubbed() = default;
void Controller_Stubbed::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
index 37d7d8538..4fa83ac85 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -30,6 +30,5 @@ public:
private:
bool smart_update{};
std::size_t common_offset{};
- Core::System& system;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 25912fd69..1c6e55566 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -13,8 +13,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
-Controller_Touchscreen::Controller_Touchscreen(Core::System& system)
- : ControllerBase(system), system(system) {}
+Controller_Touchscreen::Controller_Touchscreen(Core::System& system) : ControllerBase(system) {}
Controller_Touchscreen::~Controller_Touchscreen() = default;
void Controller_Touchscreen::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 3429c84db..a1d97269e 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -69,6 +69,5 @@ private:
TouchScreenSharedMemory shared_memory{};
std::unique_ptr<Input::TouchDevice> touch_device;
s64_le last_touch{};
- Core::System& system;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index 1bce044b4..27511b27b 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -10,7 +10,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
-Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system), system(system) {}
+Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system) {}
Controller_XPad::~Controller_XPad() = default;
void Controller_XPad::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index c445ebec0..ad229787c 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -56,6 +56,5 @@ private:
};
static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
SharedMemory shared_memory{};
- Core::System& system;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 8d76ba746..ba1da4181 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -38,8 +38,10 @@ namespace Service::HID {
// Updating period for each HID device.
// TODO(ogniK): Find actual polling rate of hid
constexpr s64 pad_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 66);
-constexpr s64 accelerometer_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
-constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
+[[maybe_unused]] constexpr s64 accelerometer_update_ticks =
+ static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
+[[maybe_unused]] constexpr s64 gyroscope_update_ticks =
+ static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource(Core::System& system)
@@ -193,7 +195,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
{102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
{103, &Hid::ActivateNpad, "ActivateNpad"},
- {104, nullptr, "DeactivateNpad"},
+ {104, &Hid::DeactivateNpad, "DeactivateNpad"},
{106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
{107, &Hid::DisconnectNpad, "DisconnectNpad"},
{108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
@@ -468,6 +470,17 @@ void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) {
applet_resource->ActivateController(HidController::NPad);
}
+void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ applet_resource->DeactivateController(HidController::NPad);
+}
+
void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id{rp.Pop<u32>()};
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 35b663679..01852e019 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -99,6 +99,7 @@ private:
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
void ActivateNpad(Kernel::HLERequestContext& ctx);
+ void DeactivateNpad(Kernel::HLERequestContext& ctx);
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
void DisconnectNpad(Kernel::HLERequestContext& ctx);
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 3164ca26e..499376bfc 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -163,7 +163,7 @@ public:
return;
}
- if (Core::CurrentProcess()->GetTitleID() != header.title_id) {
+ if (system.CurrentProcess()->GetTitleID() != header.title_id) {
LOG_ERROR(Service_LDR,
"Attempting to load NRR with title ID other than current process. (actual "
"{:016X})!",
@@ -327,7 +327,7 @@ public:
}
// Load NRO as new executable module
- auto* process = Core::CurrentProcess();
+ auto* process = system.CurrentProcess();
auto& vm_manager = process->VMManager();
auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size);
@@ -411,7 +411,7 @@ public:
return;
}
- auto& vm_manager = Core::CurrentProcess()->VMManager();
+ auto& vm_manager = system.CurrentProcess()->VMManager();
const auto& nro_info = iter->second;
// Unmap the mirrored memory
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index a42c22d44..aa886cd3e 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -18,8 +18,8 @@
namespace Service::NFP {
namespace ErrCodes {
-constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
- -1); // TODO(ogniK): Find the actual error code
+[[maybe_unused]] constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
+ -1); // TODO(ogniK): Find the actual error code
constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
} // namespace ErrCodes
@@ -35,7 +35,7 @@ Module::Interface::~Interface() = default;
class IUser final : public ServiceFramework<IUser> {
public:
IUser(Module::Interface& nfp_interface, Core::System& system)
- : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface), system(system) {
+ : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) {
static const FunctionInfo functions[] = {
{0, &IUser::Initialize, "Initialize"},
{1, &IUser::Finalize, "Finalize"},
@@ -183,6 +183,8 @@ private:
case DeviceState::TagRemoved:
device_state = DeviceState::Initialized;
break;
+ default:
+ break;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -324,7 +326,6 @@ private:
Kernel::EventPair deactivate_event;
Kernel::EventPair availability_change_event;
const Module::Interface& nfp_interface;
- Core::System& system;
};
void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
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<IScanRequest> {
public:
explicit IScanRequest() : ServiceFramework("IScanRequest") {
@@ -81,7 +88,7 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(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<u8>(0);
+ rb.Push<u8>(1);
}
void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u8>(0);
+ rb.Push<u8>(1);
}
Core::System& system;
};
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 7dcdb4a07..f64535237 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -324,14 +324,14 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
// Map backing memory for the font data
LOG_DEBUG(Service_NS, "called");
- Core::CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0,
- SHARED_FONT_MEM_SIZE,
- Kernel::MemoryState::Shared);
+ system.CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0,
+ SHARED_FONT_MEM_SIZE,
+ Kernel::MemoryState::Shared);
// Create shared font memory object
auto& kernel = system.Kernel();
impl->shared_font_mem = Kernel::SharedMemory::Create(
- kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
+ kernel, system.CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
"PL_U:shared_font_mem");
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 6bc053f27..07c88465e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -45,6 +45,8 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std:
return GetVARegions(input, output);
case IoctlCommand::IocUnmapBufferCommand:
return UnmapBuffer(input, output);
+ default:
+ break;
}
if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand)
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index ff6b1abae..eb88fee1b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -38,9 +38,10 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::v
return IocCtrlEventUnregister(input, output);
case IoctlCommand::IocCtrlEventSignalCommand:
return IocCtrlEventSignal(input, output);
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented ioctl");
+ return 0;
}
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
}
u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 389ace76f..cc2192e5c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -40,9 +40,10 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input,
return FlushL2(input, output);
case IoctlCommand::IocGetGpuTime:
return GetGpuTime(input, output);
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented ioctl");
+ return 0;
}
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
}
u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 2b8d1bef6..9de0ace22 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -44,6 +44,8 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
return GetWaitbase(input, output);
case IoctlCommand::IocChannelSetTimeoutCommand:
return ChannelSetTimeout(input, output);
+ default:
+ break;
}
if (command.group == NVGPU_IOCTL_MAGIC) {
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index c12a746c8..7c5302017 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::ServiceManager>& 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);