summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt42
-rw-r--r--src/core/file_sys/errors.h1
-rw-r--r--src/core/hle/service/am/applet.h2
-rw-r--r--src/core/hle/service/am/applet_data_broker.h2
-rw-r--r--src/core/hle/service/bcat/backend/backend.cpp30
-rw-r--r--src/core/hle/service/bcat/backend/backend.h54
-rw-r--r--src/core/hle/service/bcat/bcat.cpp42
-rw-r--r--src/core/hle/service/bcat/bcat.h9
-rw-r--r--src/core/hle/service/bcat/bcat_module.cpp606
-rw-r--r--src/core/hle/service/bcat/bcat_module.h46
-rw-r--r--src/core/hle/service/bcat/bcat_result.h15
-rw-r--r--src/core/hle/service/bcat/bcat_service.cpp132
-rw-r--r--src/core/hle/service/bcat/bcat_service.h45
-rw-r--r--src/core/hle/service/bcat/bcat_types.h66
-rw-r--r--src/core/hle/service/bcat/bcat_util.h39
-rw-r--r--src/core/hle/service/bcat/delivery_cache_directory_service.cpp80
-rw-r--r--src/core/hle/service/bcat/delivery_cache_directory_service.h33
-rw-r--r--src/core/hle/service/bcat/delivery_cache_file_service.cpp82
-rw-r--r--src/core/hle/service/bcat/delivery_cache_file_service.h33
-rw-r--r--src/core/hle/service/bcat/delivery_cache_progress_service.cpp41
-rw-r--r--src/core/hle/service/bcat/delivery_cache_progress_service.h35
-rw-r--r--src/core/hle/service/bcat/delivery_cache_storage_service.cpp57
-rw-r--r--src/core/hle/service/bcat/delivery_cache_storage_service.h36
-rw-r--r--src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp34
-rw-r--r--src/core/hle/service/bcat/news/newly_arrived_event_holder.h33
-rw-r--r--src/core/hle/service/bcat/news/news_data_service.cpp25
-rw-r--r--src/core/hle/service/bcat/news/news_data_service.h20
-rw-r--r--src/core/hle/service/bcat/news/news_database_service.cpp53
-rw-r--r--src/core/hle/service/bcat/news/news_database_service.h32
-rw-r--r--src/core/hle/service/bcat/news/news_service.cpp57
-rw-r--r--src/core/hle/service/bcat/news/news_service.h28
-rw-r--r--src/core/hle/service/bcat/news/overwrite_event_holder.cpp33
-rw-r--r--src/core/hle/service/bcat/news/overwrite_event_holder.h33
-rw-r--r--src/core/hle/service/bcat/news/service_creator.cpp64
-rw-r--r--src/core/hle/service/bcat/news/service_creator.h35
-rw-r--r--src/core/hle/service/bcat/service_creator.cpp62
-rw-r--r--src/core/hle/service/bcat/service_creator.h40
-rw-r--r--src/core/hle/service/cmif_serialization.h2
-rw-r--r--src/core/hle/service/cmif_types.h10
-rw-r--r--src/core/hle/service/glue/time/worker.cpp130
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp9
-rw-r--r--src/core/hle/service/hle_ipc.cpp8
-rw-r--r--src/core/hle/service/os/event.cpp (renamed from src/core/hle/service/event.cpp)2
-rw-r--r--src/core/hle/service/os/event.h (renamed from src/core/hle/service/event.h)0
-rw-r--r--src/core/hle/service/os/multi_wait.cpp59
-rw-r--r--src/core/hle/service/os/multi_wait.h36
-rw-r--r--src/core/hle/service/os/multi_wait_holder.cpp25
-rw-r--r--src/core/hle/service/os/multi_wait_holder.h44
-rw-r--r--src/core/hle/service/os/multi_wait_utils.h109
-rw-r--r--src/core/hle/service/os/mutex.cpp (renamed from src/core/hle/service/mutex.cpp)2
-rw-r--r--src/core/hle/service/os/mutex.h (renamed from src/core/hle/service/mutex.h)0
-rw-r--r--src/core/hle/service/server_manager.cpp438
-rw-r--r--src/core/hle/service/server_manager.h59
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/memory/cheat_engine.cpp15
-rw-r--r--src/core/memory/cheat_engine.h2
-rw-r--r--src/core/memory/dmnt_cheat_vm.cpp4
-rw-r--r--src/core/memory/dmnt_cheat_vm.h3
58 files changed, 1934 insertions, 1102 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index eb8f643a2..2d5490968 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -512,10 +512,35 @@ add_library(core STATIC
hle/service/audio/hwopus.h
hle/service/bcat/backend/backend.cpp
hle/service/bcat/backend/backend.h
+ hle/service/bcat/news/newly_arrived_event_holder.cpp
+ hle/service/bcat/news/newly_arrived_event_holder.h
+ hle/service/bcat/news/news_data_service.cpp
+ hle/service/bcat/news/news_data_service.h
+ hle/service/bcat/news/news_database_service.cpp
+ hle/service/bcat/news/news_database_service.h
+ hle/service/bcat/news/news_service.cpp
+ hle/service/bcat/news/news_service.h
+ hle/service/bcat/news/overwrite_event_holder.cpp
+ hle/service/bcat/news/overwrite_event_holder.h
+ hle/service/bcat/news/service_creator.cpp
+ hle/service/bcat/news/service_creator.h
hle/service/bcat/bcat.cpp
hle/service/bcat/bcat.h
- hle/service/bcat/bcat_module.cpp
- hle/service/bcat/bcat_module.h
+ hle/service/bcat/bcat_result.h
+ hle/service/bcat/bcat_service.cpp
+ hle/service/bcat/bcat_service.h
+ hle/service/bcat/bcat_types.h
+ hle/service/bcat/bcat_util.h
+ hle/service/bcat/delivery_cache_directory_service.cpp
+ hle/service/bcat/delivery_cache_directory_service.h
+ hle/service/bcat/delivery_cache_file_service.cpp
+ hle/service/bcat/delivery_cache_file_service.h
+ hle/service/bcat/delivery_cache_progress_service.cpp
+ hle/service/bcat/delivery_cache_progress_service.h
+ hle/service/bcat/delivery_cache_storage_service.cpp
+ hle/service/bcat/delivery_cache_storage_service.h
+ hle/service/bcat/service_creator.cpp
+ hle/service/bcat/service_creator.h
hle/service/bpc/bpc.cpp
hle/service/bpc/bpc.h
hle/service/btdrv/btdrv.cpp
@@ -548,8 +573,6 @@ add_library(core STATIC
hle/service/es/es.h
hle/service/eupld/eupld.cpp
hle/service/eupld/eupld.h
- hle/service/event.cpp
- hle/service/event.h
hle/service/fatal/fatal.cpp
hle/service/fatal/fatal.h
hle/service/fatal/fatal_p.cpp
@@ -676,8 +699,6 @@ add_library(core STATIC
hle/service/mm/mm_u.h
hle/service/mnpp/mnpp_app.cpp
hle/service/mnpp/mnpp_app.h
- hle/service/mutex.cpp
- hle/service/mutex.h
hle/service/ncm/ncm.cpp
hle/service/ncm/ncm.h
hle/service/nfc/common/amiibo_crypto.cpp
@@ -790,6 +811,15 @@ add_library(core STATIC
hle/service/nvnflinger/window.h
hle/service/olsc/olsc.cpp
hle/service/olsc/olsc.h
+ hle/service/os/event.cpp
+ hle/service/os/event.h
+ hle/service/os/multi_wait_holder.cpp
+ hle/service/os/multi_wait_holder.h
+ hle/service/os/multi_wait_utils.h
+ hle/service/os/multi_wait.cpp
+ hle/service/os/multi_wait.h
+ hle/service/os/mutex.cpp
+ hle/service/os/mutex.h
hle/service/pcie/pcie.cpp
hle/service/pcie/pcie.h
hle/service/pctl/pctl.cpp
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
index d4e0eb6f4..b22767bf5 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -91,6 +91,7 @@ constexpr Result ResultWriteNotPermitted{ErrorModule::FS, 6203};
constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325};
constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387};
constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388};
+constexpr Result ResultPermissionDenied{ErrorModule::FS, 6400};
constexpr Result ResultBufferAllocationFailed{ErrorModule::FS, 6705};
} // namespace FileSys
diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h
index bce6f9050..b29ecdfed 100644
--- a/src/core/hle/service/am/applet.h
+++ b/src/core/hle/service/am/applet.h
@@ -9,8 +9,8 @@
#include "common/math_util.h"
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/caps/caps_types.h"
-#include "core/hle/service/event.h"
#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/os/event.h"
#include "core/hle/service/service.h"
#include "core/hle/service/am/am_types.h"
diff --git a/src/core/hle/service/am/applet_data_broker.h b/src/core/hle/service/am/applet_data_broker.h
index 12326fd04..5a1d43c11 100644
--- a/src/core/hle/service/am/applet_data_broker.h
+++ b/src/core/hle/service/am/applet_data_broker.h
@@ -7,8 +7,8 @@
#include <memory>
#include <mutex>
-#include "core/hle/service/event.h"
#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/os/event.h"
union Result;
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp
index 847f76987..1993493a9 100644
--- a/src/core/hle/service/bcat/backend/backend.cpp
+++ b/src/core/hle/service/bcat/backend/backend.cpp
@@ -33,18 +33,18 @@ void ProgressServiceBackend::SetTotalSize(u64 size) {
}
void ProgressServiceBackend::StartConnecting() {
- impl.status = DeliveryCacheProgressImpl::Status::Connecting;
+ impl.status = DeliveryCacheProgressStatus::Connecting;
SignalUpdate();
}
void ProgressServiceBackend::StartProcessingDataList() {
- impl.status = DeliveryCacheProgressImpl::Status::ProcessingDataList;
+ impl.status = DeliveryCacheProgressStatus::ProcessingDataList;
SignalUpdate();
}
void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name,
std::string_view file_name, u64 file_size) {
- impl.status = DeliveryCacheProgressImpl::Status::Downloading;
+ impl.status = DeliveryCacheProgressStatus::Downloading;
impl.current_downloaded_bytes = 0;
impl.current_total_bytes = file_size;
std::memcpy(impl.current_directory.data(), dir_name.data(),
@@ -65,7 +65,7 @@ void ProgressServiceBackend::FinishDownloadingFile() {
}
void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) {
- impl.status = DeliveryCacheProgressImpl::Status::Committing;
+ impl.status = DeliveryCacheProgressStatus::Committing;
impl.current_file.fill(0);
impl.current_downloaded_bytes = 0;
impl.current_total_bytes = 0;
@@ -76,7 +76,7 @@ void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) {
void ProgressServiceBackend::FinishDownload(Result result) {
impl.total_downloaded_bytes = impl.total_bytes;
- impl.status = DeliveryCacheProgressImpl::Status::Done;
+ impl.status = DeliveryCacheProgressStatus::Done;
impl.result = result;
SignalUpdate();
}
@@ -85,15 +85,15 @@ void ProgressServiceBackend::SignalUpdate() {
update_event->Signal();
}
-Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
+BcatBackend::BcatBackend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
-Backend::~Backend() = default;
+BcatBackend::~BcatBackend() = default;
-NullBackend::NullBackend(DirectoryGetter getter) : Backend(std::move(getter)) {}
+NullBcatBackend::NullBcatBackend(DirectoryGetter getter) : BcatBackend(std::move(getter)) {}
-NullBackend::~NullBackend() = default;
+NullBcatBackend::~NullBcatBackend() = default;
-bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
+bool NullBcatBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
title.build_id);
@@ -101,8 +101,8 @@ bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& prog
return true;
}
-bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
- ProgressServiceBackend& progress) {
+bool NullBcatBackend::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);
@@ -110,18 +110,18 @@ bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
return true;
}
-bool NullBackend::Clear(u64 title_id) {
+bool NullBcatBackend::Clear(u64 title_id) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
return true;
}
-void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
+void NullBcatBackend::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) {
+std::optional<std::vector<u8>> NullBcatBackend::GetLaunchParameter(TitleIDVersion title) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
title.build_id);
return std::nullopt;
diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h
index aa36d29d5..3680f6c9c 100644
--- a/src/core/hle/service/bcat/backend/backend.h
+++ b/src/core/hle/service/bcat/backend/backend.h
@@ -10,6 +10,7 @@
#include "common/common_types.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
+#include "core/hle/service/bcat/bcat_types.h"
#include "core/hle/service/kernel_helpers.h"
namespace Core {
@@ -24,44 +25,6 @@ class KReadableEvent;
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;
- Result result = ResultSuccess;
- 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 {
@@ -107,10 +70,10 @@ private:
};
// A class representing an abstract backend for BCAT functionality.
-class Backend {
+class BcatBackend {
public:
- explicit Backend(DirectoryGetter getter);
- virtual ~Backend();
+ explicit BcatBackend(DirectoryGetter getter);
+ virtual ~BcatBackend();
// 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
@@ -135,10 +98,10 @@ protected:
};
// A backend of BCAT that provides no operation.
-class NullBackend : public Backend {
+class NullBcatBackend : public BcatBackend {
public:
- explicit NullBackend(DirectoryGetter getter);
- ~NullBackend() override;
+ explicit NullBcatBackend(DirectoryGetter getter);
+ ~NullBcatBackend() override;
bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
bool SynchronizeDirectory(TitleIDVersion title, std::string name,
@@ -151,6 +114,7 @@ public:
std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
};
-std::unique_ptr<Backend> CreateBackendFromSettings(Core::System& system, DirectoryGetter getter);
+std::unique_ptr<BcatBackend> CreateBackendFromSettings(Core::System& system,
+ DirectoryGetter getter);
} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp
index d0ac17324..ea8b15998 100644
--- a/src/core/hle/service/bcat/bcat.cpp
+++ b/src/core/hle/service/bcat/bcat.cpp
@@ -1,24 +1,38 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "core/hle/service/bcat/backend/backend.h"
#include "core/hle/service/bcat/bcat.h"
+#include "core/hle/service/bcat/news/service_creator.h"
+#include "core/hle/service/bcat/service_creator.h"
+#include "core/hle/service/server_manager.h"
namespace Service::BCAT {
-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"},
- {3, nullptr, "CreateDeliveryCacheProgressService"},
- {4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"},
- };
- // clang-format on
- RegisterHandlers(functions);
+void LoopProcess(Core::System& system) {
+ auto server_manager = std::make_unique<ServerManager>(system);
+
+ server_manager->RegisterNamedService("bcat:a",
+ std::make_shared<IServiceCreator>(system, "bcat:a"));
+ server_manager->RegisterNamedService("bcat:m",
+ std::make_shared<IServiceCreator>(system, "bcat:m"));
+ server_manager->RegisterNamedService("bcat:u",
+ std::make_shared<IServiceCreator>(system, "bcat:u"));
+ server_manager->RegisterNamedService("bcat:s",
+ std::make_shared<IServiceCreator>(system, "bcat:s"));
+
+ server_manager->RegisterNamedService(
+ "news:a", std::make_shared<News::IServiceCreator>(system, 0xffffffff, "news:a"));
+ server_manager->RegisterNamedService(
+ "news:p", std::make_shared<News::IServiceCreator>(system, 0x1, "news:p"));
+ server_manager->RegisterNamedService(
+ "news:c", std::make_shared<News::IServiceCreator>(system, 0x2, "news:c"));
+ server_manager->RegisterNamedService(
+ "news:v", std::make_shared<News::IServiceCreator>(system, 0x4, "news:v"));
+ server_manager->RegisterNamedService(
+ "news:m", std::make_shared<News::IServiceCreator>(system, 0xd, "news:m"));
+
+ ServerManager::RunServer(std::move(server_manager));
}
-BCAT::~BCAT() = default;
} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h
index db9d3c8c5..2aaffc693 100644
--- a/src/core/hle/service/bcat/bcat.h
+++ b/src/core/hle/service/bcat/bcat.h
@@ -3,7 +3,7 @@
#pragma once
-#include "core/hle/service/bcat/bcat_module.h"
+#include "core/hle/service/service.h"
namespace Core {
class System;
@@ -11,11 +11,6 @@ class System;
namespace Service::BCAT {
-class BCAT final : public Module::Interface {
-public:
- explicit BCAT(Core::System& system_, std::shared_ptr<Module> module_,
- FileSystem::FileSystemController& fsc_, const char* name_);
- ~BCAT() override;
-};
+void LoopProcess(Core::System& system);
} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp
deleted file mode 100644
index 76d7bb139..000000000
--- a/src/core/hle/service/bcat/bcat_module.cpp
+++ /dev/null
@@ -1,606 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <cctype>
-#include <mbedtls/md5.h>
-#include "common/hex_util.h"
-#include "common/logging/log.h"
-#include "common/settings.h"
-#include "common/string_util.h"
-#include "core/core.h"
-#include "core/file_sys/vfs/vfs.h"
-#include "core/hle/kernel/k_readable_event.h"
-#include "core/hle/service/bcat/backend/backend.h"
-#include "core/hle/service/bcat/bcat.h"
-#include "core/hle/service/bcat/bcat_module.h"
-#include "core/hle/service/filesystem/filesystem.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/server_manager.h"
-
-namespace Service::BCAT {
-
-constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1};
-constexpr Result ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2};
-constexpr Result ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6};
-constexpr Result 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 Result 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_ret(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(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(HLERequestContext& ctx, DirectoryName name) {
- return VerifyNameValidInternal(ctx, name, '-');
-}
-
-bool VerifyNameValidFile(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:
- explicit IDeliveryCacheProgressService(Core::System& system_, Kernel::KReadableEvent& event_,
- const DeliveryCacheProgressImpl& impl_)
- : ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{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(HLERequestContext& ctx) {
- LOG_DEBUG(Service_BCAT, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(event);
- }
-
- void GetImpl(HLERequestContext& ctx) {
- LOG_DEBUG(Service_BCAT, "called");
-
- ctx.WriteBuffer(impl);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- Kernel::KReadableEvent& event;
- const DeliveryCacheProgressImpl& impl;
-};
-
-class IBcatService final : public ServiceFramework<IBcatService> {
-public:
- explicit IBcatService(Core::System& system_, Backend& backend_)
- : ServiceFramework{system_, "IBcatService"}, backend{backend_},
- progress{{
- ProgressServiceBackend{system_, "Normal"},
- ProgressServiceBackend{system_, "Directory"},
- }} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {10100, &IBcatService::RequestSyncDeliveryCache, "RequestSyncDeliveryCache"},
- {10101, &IBcatService::RequestSyncDeliveryCacheWithDirectoryName, "RequestSyncDeliveryCacheWithDirectoryName"},
- {10200, nullptr, "CancelSyncDeliveryCacheRequest"},
- {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"},
- {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"},
- {20300, nullptr, "GetDeliveryCacheStorageUpdateNotifier"},
- {20301, nullptr, "RequestSuspendDeliveryTask"},
- {20400, nullptr, "RegisterSystemApplicationDeliveryTask"},
- {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
- {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
- {30100, &IBcatService::SetPassphrase, "SetPassphrase"},
- {30101, nullptr, "Unknown30101"},
- {30102, nullptr, "Unknown30102"},
- {30200, nullptr, "RegisterBackgroundDeliveryTask"},
- {30201, nullptr, "UnregisterBackgroundDeliveryTask"},
- {30202, nullptr, "BlockDeliveryTask"},
- {30203, nullptr, "UnblockDeliveryTask"},
- {30210, nullptr, "SetDeliveryTaskTimer"},
- {30300, nullptr, "RegisterSystemApplicationDeliveryTasks"},
- {90100, nullptr, "EnumerateBackgroundDeliveryTask"},
- {90101, nullptr, "Unknown90101"},
- {90200, nullptr, "GetDeliveryList"},
- {90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"},
- {90202, nullptr, "ClearDeliveryTaskSubscriptionStatus"},
- {90300, nullptr, "GetPushNotificationLog"},
- {90301, nullptr, "Unknown90301"},
- };
- // clang-format on
- RegisterHandlers(functions);
- }
-
-private:
- enum class SyncType {
- Normal,
- Directory,
- Count,
- };
-
- std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) {
- auto& progress_backend{GetProgressBackend(type)};
- return std::make_shared<IDeliveryCacheProgressService>(system, progress_backend.GetEvent(),
- progress_backend.GetImpl());
- }
-
- void RequestSyncDeliveryCache(HLERequestContext& ctx) {
- LOG_DEBUG(Service_BCAT, "called");
-
- backend.Synchronize({system.GetApplicationProcessProgramID(),
- GetCurrentBuildID(system.GetApplicationProcessBuildID())},
- GetProgressBackend(SyncType::Normal));
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface(CreateProgressService(SyncType::Normal));
- }
-
- void RequestSyncDeliveryCacheWithDirectoryName(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.GetApplicationProcessProgramID(),
- GetCurrentBuildID(system.GetApplicationProcessBuildID())},
- name, GetProgressBackend(SyncType::Directory));
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface(CreateProgressService(SyncType::Directory));
- }
-
- void SetPassphrase(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(ResultSuccess);
- }
-
- void ClearDeliveryCacheStorage(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(ResultSuccess);
- }
-
- ProgressServiceBackend& GetProgressBackend(SyncType type) {
- return progress.at(static_cast<size_t>(type));
- }
-
- const ProgressServiceBackend& GetProgressBackend(SyncType type) const {
- return progress.at(static_cast<size_t>(type));
- }
-
- Backend& backend;
- std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
-};
-
-void Module::Interface::CreateBcatService(HLERequestContext& ctx) {
- LOG_DEBUG(Service_BCAT, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IBcatService>(system, *backend);
-}
-
-class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> {
-public:
- explicit IDeliveryCacheFileService(Core::System& system_, FileSys::VirtualDir root_)
- : ServiceFramework{system_, "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(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(ResultSuccess);
- }
-
- void Read(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(ResultSuccess);
- rb.Push<u64>(buffer.size());
- }
-
- void GetSize(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(ResultSuccess);
- rb.Push<u64>(current_file->GetSize());
- }
-
- void GetDigest(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(ResultSuccess);
- rb.PushRaw(DigestFile(current_file));
- }
-
- FileSys::VirtualDir root;
- FileSys::VirtualFile current_file;
-};
-
-class IDeliveryCacheDirectoryService final
- : public ServiceFramework<IDeliveryCacheDirectoryService> {
-public:
- explicit IDeliveryCacheDirectoryService(Core::System& system_, FileSys::VirtualDir root_)
- : ServiceFramework{system_, "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(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(ResultSuccess);
- }
-
- void Read(HLERequestContext& ctx) {
- auto write_size = ctx.GetWriteBufferNumElements<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(ResultSuccess);
- rb.Push(static_cast<u32>(write_size * sizeof(DeliveryCacheDirectoryEntry)));
- }
-
- void GetCount(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(ResultSuccess);
- rb.Push(static_cast<u32>(files.size()));
- }
-
- FileSys::VirtualDir root;
- FileSys::VirtualDir current_dir;
-};
-
-class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> {
-public:
- explicit IDeliveryCacheStorageService(Core::System& system_, FileSys::VirtualDir root_)
- : ServiceFramework{system_, "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(HLERequestContext& ctx) {
- LOG_DEBUG(Service_BCAT, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IDeliveryCacheFileService>(system, root);
- }
-
- void CreateDirectoryService(HLERequestContext& ctx) {
- LOG_DEBUG(Service_BCAT, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IDeliveryCacheDirectoryService>(system, root);
- }
-
- void EnumerateDeliveryCacheDirectory(HLERequestContext& ctx) {
- auto size = ctx.GetWriteBufferNumElements<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(ResultSuccess);
- rb.Push(static_cast<u32>(size));
- }
-
- FileSys::VirtualDir root;
- std::vector<DirectoryName> entries;
- u64 next_read_index = 0;
-};
-
-void Module::Interface::CreateDeliveryCacheStorageService(HLERequestContext& ctx) {
- LOG_DEBUG(Service_BCAT, "called");
-
- const auto title_id = system.GetApplicationProcessProgramID();
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
-}
-
-void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId(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(ResultSuccess);
- rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
-}
-
-std::unique_ptr<Backend> CreateBackendFromSettings([[maybe_unused]] Core::System& system,
- DirectoryGetter getter) {
- return std::make_unique<NullBackend>(std::move(getter));
-}
-
-Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
- FileSystem::FileSystemController& fsc_, const char* name)
- : ServiceFramework{system_, name}, fsc{fsc_}, module{std::move(module_)},
- backend{CreateBackendFromSettings(system_,
- [&fsc_](u64 tid) { return fsc_.GetBCATDirectory(tid); })} {}
-
-Module::Interface::~Interface() = default;
-
-void LoopProcess(Core::System& system) {
- auto server_manager = std::make_unique<ServerManager>(system);
- auto module = std::make_shared<Module>();
-
- server_manager->RegisterNamedService(
- "bcat:a",
- std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:a"));
- server_manager->RegisterNamedService(
- "bcat:m",
- std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:m"));
- server_manager->RegisterNamedService(
- "bcat:u",
- std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:u"));
- server_manager->RegisterNamedService(
- "bcat:s",
- std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:s"));
- ServerManager::RunServer(std::move(server_manager));
-}
-
-} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat_module.h b/src/core/hle/service/bcat/bcat_module.h
deleted file mode 100644
index 87576288b..000000000
--- a/src/core/hle/service/bcat/bcat_module.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-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(Core::System& system_, std::shared_ptr<Module> module_,
- FileSystem::FileSystemController& fsc_, const char* name);
- ~Interface() override;
-
- void CreateBcatService(HLERequestContext& ctx);
- void CreateDeliveryCacheStorageService(HLERequestContext& ctx);
- void CreateDeliveryCacheStorageServiceWithApplicationId(HLERequestContext& ctx);
-
- protected:
- FileSystem::FileSystemController& fsc;
-
- std::shared_ptr<Module> module;
- std::unique_ptr<Backend> backend;
- };
-};
-
-void LoopProcess(Core::System& system);
-
-} // namespace BCAT
-
-} // namespace Service
diff --git a/src/core/hle/service/bcat/bcat_result.h b/src/core/hle/service/bcat/bcat_result.h
new file mode 100644
index 000000000..edf8a6564
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_result.h
@@ -0,0 +1,15 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::BCAT {
+
+constexpr Result ResultInvalidArgument{ErrorModule::BCAT, 1};
+constexpr Result ResultFailedOpenEntity{ErrorModule::BCAT, 2};
+constexpr Result ResultEntityAlreadyOpen{ErrorModule::BCAT, 6};
+constexpr Result ResultNoOpenEntry{ErrorModule::BCAT, 7};
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat_service.cpp b/src/core/hle/service/bcat/bcat_service.cpp
new file mode 100644
index 000000000..63b1072d2
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_service.cpp
@@ -0,0 +1,132 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/hex_util.h"
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/file_sys/errors.h"
+#include "core/hle/service/bcat/backend/backend.h"
+#include "core/hle/service/bcat/bcat_result.h"
+#include "core/hle/service/bcat/bcat_service.h"
+#include "core/hle/service/bcat/bcat_util.h"
+#include "core/hle/service/bcat/delivery_cache_progress_service.h"
+#include "core/hle/service/bcat/delivery_cache_storage_service.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::BCAT {
+
+static u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
+ u64 out{};
+ std::memcpy(&out, id.data(), sizeof(u64));
+ return out;
+}
+
+IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_)
+ : ServiceFramework{system_, "IBcatService"}, backend{backend_},
+ progress{{
+ ProgressServiceBackend{system_, "Normal"},
+ ProgressServiceBackend{system_, "Directory"},
+ }} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {10100, D<&IBcatService::RequestSyncDeliveryCache>, "RequestSyncDeliveryCache"},
+ {10101, D<&IBcatService::RequestSyncDeliveryCacheWithDirectoryName>, "RequestSyncDeliveryCacheWithDirectoryName"},
+ {10200, nullptr, "CancelSyncDeliveryCacheRequest"},
+ {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"},
+ {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"},
+ {20300, nullptr, "GetDeliveryCacheStorageUpdateNotifier"},
+ {20301, nullptr, "RequestSuspendDeliveryTask"},
+ {20400, nullptr, "RegisterSystemApplicationDeliveryTask"},
+ {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
+ {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
+ {30100, D<&IBcatService::SetPassphrase>, "SetPassphrase"},
+ {30101, nullptr, "Unknown30101"},
+ {30102, nullptr, "Unknown30102"},
+ {30200, nullptr, "RegisterBackgroundDeliveryTask"},
+ {30201, nullptr, "UnregisterBackgroundDeliveryTask"},
+ {30202, nullptr, "BlockDeliveryTask"},
+ {30203, nullptr, "UnblockDeliveryTask"},
+ {30210, nullptr, "SetDeliveryTaskTimer"},
+ {30300, D<&IBcatService::RegisterSystemApplicationDeliveryTasks>, "RegisterSystemApplicationDeliveryTasks"},
+ {90100, nullptr, "EnumerateBackgroundDeliveryTask"},
+ {90101, nullptr, "Unknown90101"},
+ {90200, nullptr, "GetDeliveryList"},
+ {90201, D<&IBcatService::ClearDeliveryCacheStorage>, "ClearDeliveryCacheStorage"},
+ {90202, nullptr, "ClearDeliveryTaskSubscriptionStatus"},
+ {90300, nullptr, "GetPushNotificationLog"},
+ {90301, nullptr, "Unknown90301"},
+ };
+ // clang-format on
+ RegisterHandlers(functions);
+}
+
+IBcatService::~IBcatService() = default;
+
+Result IBcatService::RequestSyncDeliveryCache(
+ OutInterface<IDeliveryCacheProgressService> out_interface) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ auto& progress_backend{GetProgressBackend(SyncType::Normal)};
+ backend.Synchronize({system.GetApplicationProcessProgramID(),
+ GetCurrentBuildID(system.GetApplicationProcessBuildID())},
+ GetProgressBackend(SyncType::Normal));
+
+ *out_interface = std::make_shared<IDeliveryCacheProgressService>(
+ system, progress_backend.GetEvent(), progress_backend.GetImpl());
+ R_SUCCEED();
+}
+
+Result IBcatService::RequestSyncDeliveryCacheWithDirectoryName(
+ const DirectoryName& name_raw, OutInterface<IDeliveryCacheProgressService> out_interface) {
+ const auto name = Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
+
+ LOG_DEBUG(Service_BCAT, "called, name={}", name);
+
+ auto& progress_backend{GetProgressBackend(SyncType::Directory)};
+ backend.SynchronizeDirectory({system.GetApplicationProcessProgramID(),
+ GetCurrentBuildID(system.GetApplicationProcessBuildID())},
+ name, progress_backend);
+
+ *out_interface = std::make_shared<IDeliveryCacheProgressService>(
+ system, progress_backend.GetEvent(), progress_backend.GetImpl());
+ R_SUCCEED();
+}
+
+Result IBcatService::SetPassphrase(u64 application_id,
+ InBuffer<BufferAttr_HipcPointer> passphrase_buffer) {
+ LOG_DEBUG(Service_BCAT, "called, application_id={:016X}, passphrase={}", application_id,
+ Common::HexToString(passphrase_buffer));
+
+ R_UNLESS(application_id != 0, ResultInvalidArgument);
+ R_UNLESS(passphrase_buffer.size() <= 0x40, ResultInvalidArgument);
+
+ Passphrase passphrase{};
+ std::memcpy(passphrase.data(), passphrase_buffer.data(),
+ std::min(passphrase.size(), passphrase_buffer.size()));
+
+ backend.SetPassphrase(application_id, passphrase);
+ R_SUCCEED();
+}
+
+Result IBcatService::RegisterSystemApplicationDeliveryTasks() {
+ LOG_WARNING(Service_BCAT, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IBcatService::ClearDeliveryCacheStorage(u64 application_id) {
+ LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", application_id);
+
+ R_UNLESS(application_id != 0, ResultInvalidArgument);
+ R_UNLESS(backend.Clear(application_id), FileSys::ResultPermissionDenied);
+ R_SUCCEED();
+}
+
+ProgressServiceBackend& IBcatService::GetProgressBackend(SyncType type) {
+ return progress.at(static_cast<size_t>(type));
+}
+
+const ProgressServiceBackend& IBcatService::GetProgressBackend(SyncType type) const {
+ return progress.at(static_cast<size_t>(type));
+}
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat_service.h b/src/core/hle/service/bcat/bcat_service.h
new file mode 100644
index 000000000..dda5a2d5f
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_service.h
@@ -0,0 +1,45 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/bcat/backend/backend.h"
+#include "core/hle/service/bcat/bcat_types.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::BCAT {
+class BcatBackend;
+class IDeliveryCacheStorageService;
+class IDeliveryCacheProgressService;
+
+class IBcatService final : public ServiceFramework<IBcatService> {
+public:
+ explicit IBcatService(Core::System& system_, BcatBackend& backend_);
+ ~IBcatService() override;
+
+private:
+ Result RequestSyncDeliveryCache(OutInterface<IDeliveryCacheProgressService> out_interface);
+
+ Result RequestSyncDeliveryCacheWithDirectoryName(
+ const DirectoryName& name, OutInterface<IDeliveryCacheProgressService> out_interface);
+
+ Result SetPassphrase(u64 application_id, InBuffer<BufferAttr_HipcPointer> passphrase_buffer);
+
+ Result RegisterSystemApplicationDeliveryTasks();
+
+ Result ClearDeliveryCacheStorage(u64 application_id);
+
+private:
+ ProgressServiceBackend& GetProgressBackend(SyncType type);
+ const ProgressServiceBackend& GetProgressBackend(SyncType type) const;
+
+ BcatBackend& backend;
+ std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
+};
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat_types.h b/src/core/hle/service/bcat/bcat_types.h
new file mode 100644
index 000000000..b35dab7c5
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_types.h
@@ -0,0 +1,66 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+#include <functional>
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
+#include "core/hle/result.h"
+
+namespace Service::BCAT {
+
+using DirectoryName = std::array<char, 0x20>;
+using FileName = std::array<char, 0x20>;
+using BcatDigest = std::array<u8, 0x10>;
+using Passphrase = std::array<u8, 0x20>;
+using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>;
+
+enum class SyncType {
+ Normal,
+ Directory,
+ Count,
+};
+
+enum class DeliveryCacheProgressStatus : s32 {
+ None = 0x0,
+ Queued = 0x1,
+ Connecting = 0x2,
+ ProcessingDataList = 0x3,
+ Downloading = 0x4,
+ Committing = 0x5,
+ Done = 0x9,
+};
+
+struct DeliveryCacheDirectoryEntry {
+ FileName name;
+ u64 size;
+ BcatDigest digest;
+};
+
+struct TitleIDVersion {
+ u64 title_id;
+ u64 build_id;
+};
+
+struct DeliveryCacheProgressImpl {
+ DeliveryCacheProgressStatus status;
+ Result result;
+ 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_NOINIT(
+ 0x198); ///< Appears to be unused in official code, possibly reserved for future use.
+};
+static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200,
+ "DeliveryCacheProgressImpl has incorrect size.");
+static_assert(std::is_trivial_v<DeliveryCacheProgressImpl>,
+ "DeliveryCacheProgressImpl type must be trivially copyable.");
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat_util.h b/src/core/hle/service/bcat/bcat_util.h
new file mode 100644
index 000000000..6bf2657ee
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_util.h
@@ -0,0 +1,39 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+#include <cctype>
+#include <mbedtls/md5.h>
+
+#include "core/hle/service/bcat/bcat_result.h"
+#include "core/hle/service/bcat/bcat_types.h"
+
+namespace Service::BCAT {
+
+// 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.
+constexpr Result VerifyNameValidInternal(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!");
+ return ResultInvalidArgument;
+ }
+
+ return ResultSuccess;
+}
+
+constexpr Result VerifyNameValidDir(DirectoryName name) {
+ return VerifyNameValidInternal(name, '-');
+}
+
+constexpr Result VerifyNameValidFile(FileName name) {
+ return VerifyNameValidInternal(name, '.');
+}
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_directory_service.cpp b/src/core/hle/service/bcat/delivery_cache_directory_service.cpp
new file mode 100644
index 000000000..01f08a2fc
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_directory_service.cpp
@@ -0,0 +1,80 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "common/string_util.h"
+#include "core/file_sys/vfs/vfs_types.h"
+#include "core/hle/service/bcat/bcat_result.h"
+#include "core/hle/service/bcat/bcat_util.h"
+#include "core/hle/service/bcat/delivery_cache_directory_service.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::BCAT {
+
+// 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.
+static BcatDigest DigestFile(const FileSys::VirtualFile& file) {
+ BcatDigest out{};
+ const auto bytes = file->ReadAllBytes();
+ mbedtls_md5_ret(bytes.data(), bytes.size(), out.data());
+ return out;
+}
+
+IDeliveryCacheDirectoryService::IDeliveryCacheDirectoryService(Core::System& system_,
+ FileSys::VirtualDir root_)
+ : ServiceFramework{system_, "IDeliveryCacheDirectoryService"}, root(std::move(root_)) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IDeliveryCacheDirectoryService::Open>, "Open"},
+ {1, D<&IDeliveryCacheDirectoryService::Read>, "Read"},
+ {2, D<&IDeliveryCacheDirectoryService::GetCount>, "GetCount"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IDeliveryCacheDirectoryService::~IDeliveryCacheDirectoryService() = default;
+
+Result IDeliveryCacheDirectoryService::Open(const DirectoryName& dir_name_raw) {
+ const auto dir_name =
+ Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size());
+
+ LOG_DEBUG(Service_BCAT, "called, dir_name={}", dir_name);
+
+ R_TRY(VerifyNameValidDir(dir_name_raw));
+ R_UNLESS(current_dir == nullptr, ResultEntityAlreadyOpen);
+
+ const auto dir = root->GetSubdirectory(dir_name);
+ R_UNLESS(dir != nullptr, ResultFailedOpenEntity);
+
+ R_SUCCEED();
+}
+
+Result IDeliveryCacheDirectoryService::Read(
+ Out<s32> out_count, OutArray<DeliveryCacheDirectoryEntry, BufferAttr_HipcMapAlias> out_buffer) {
+ LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", out_buffer.size());
+
+ R_UNLESS(current_dir != nullptr, ResultNoOpenEntry);
+
+ const auto files = current_dir->GetFiles();
+ *out_count = static_cast<s32>(std::min(files.size(), out_buffer.size()));
+ std::transform(files.begin(), files.begin() + *out_count, out_buffer.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)};
+ });
+ R_SUCCEED();
+}
+
+Result IDeliveryCacheDirectoryService::GetCount(Out<s32> out_count) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ R_UNLESS(current_dir != nullptr, ResultNoOpenEntry);
+
+ *out_count = static_cast<s32>(current_dir->GetFiles().size());
+ R_SUCCEED();
+}
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_directory_service.h b/src/core/hle/service/bcat/delivery_cache_directory_service.h
new file mode 100644
index 000000000..b902c6495
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_directory_service.h
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/file_sys/vfs/vfs.h"
+#include "core/hle/service/bcat/bcat_types.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::BCAT {
+
+class IDeliveryCacheDirectoryService final
+ : public ServiceFramework<IDeliveryCacheDirectoryService> {
+public:
+ explicit IDeliveryCacheDirectoryService(Core::System& system_, FileSys::VirtualDir root_);
+ ~IDeliveryCacheDirectoryService() override;
+
+private:
+ Result Open(const DirectoryName& dir_name_raw);
+ Result Read(Out<s32> out_count,
+ OutArray<DeliveryCacheDirectoryEntry, BufferAttr_HipcMapAlias> out_buffer);
+ Result GetCount(Out<s32> out_count);
+
+ FileSys::VirtualDir root;
+ FileSys::VirtualDir current_dir;
+};
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_file_service.cpp b/src/core/hle/service/bcat/delivery_cache_file_service.cpp
new file mode 100644
index 000000000..b75fac4bf
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_file_service.cpp
@@ -0,0 +1,82 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "common/string_util.h"
+#include "core/hle/service/bcat/bcat_result.h"
+#include "core/hle/service/bcat/bcat_util.h"
+#include "core/hle/service/bcat/delivery_cache_file_service.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::BCAT {
+
+IDeliveryCacheFileService::IDeliveryCacheFileService(Core::System& system_,
+ FileSys::VirtualDir root_)
+ : ServiceFramework{system_, "IDeliveryCacheFileService"}, root(std::move(root_)) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IDeliveryCacheFileService::Open>, "Open"},
+ {1, D<&IDeliveryCacheFileService::Read>, "Read"},
+ {2, D<&IDeliveryCacheFileService::GetSize>, "GetSize"},
+ {3, D<&IDeliveryCacheFileService::GetDigest>, "GetDigest"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IDeliveryCacheFileService::~IDeliveryCacheFileService() = default;
+
+Result IDeliveryCacheFileService::Open(const DirectoryName& dir_name_raw,
+ const FileName& file_name_raw) {
+ 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);
+
+ R_TRY(VerifyNameValidDir(dir_name_raw));
+ R_TRY(VerifyNameValidDir(file_name_raw));
+ R_UNLESS(current_file == nullptr, ResultEntityAlreadyOpen);
+
+ const auto dir = root->GetSubdirectory(dir_name);
+ R_UNLESS(dir != nullptr, ResultFailedOpenEntity);
+
+ current_file = dir->GetFile(file_name);
+ R_UNLESS(current_file != nullptr, ResultFailedOpenEntity);
+
+ R_SUCCEED();
+}
+
+Result IDeliveryCacheFileService::Read(Out<u64> out_buffer_size, u64 offset,
+ OutBuffer<BufferAttr_HipcMapAlias> out_buffer) {
+ LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, out_buffer.size());
+
+ R_UNLESS(current_file != nullptr, ResultNoOpenEntry);
+
+ *out_buffer_size = std::min<u64>(current_file->GetSize() - offset, out_buffer.size());
+ const auto buffer = current_file->ReadBytes(*out_buffer_size, offset);
+ memcpy(out_buffer.data(), buffer.data(), buffer.size());
+
+ R_SUCCEED();
+}
+
+Result IDeliveryCacheFileService::GetSize(Out<u64> out_size) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ R_UNLESS(current_file != nullptr, ResultNoOpenEntry);
+
+ *out_size = current_file->GetSize();
+ R_SUCCEED();
+}
+
+Result IDeliveryCacheFileService::GetDigest(Out<BcatDigest> out_digest) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ R_UNLESS(current_file != nullptr, ResultNoOpenEntry);
+
+ //*out_digest = DigestFile(current_file);
+ R_SUCCEED();
+}
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_file_service.h b/src/core/hle/service/bcat/delivery_cache_file_service.h
new file mode 100644
index 000000000..e1012e687
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_file_service.h
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/file_sys/vfs/vfs.h"
+#include "core/hle/service/bcat/bcat_types.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::BCAT {
+
+class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> {
+public:
+ explicit IDeliveryCacheFileService(Core::System& system_, FileSys::VirtualDir root_);
+ ~IDeliveryCacheFileService() override;
+
+private:
+ Result Open(const DirectoryName& dir_name_raw, const FileName& file_name_raw);
+ Result Read(Out<u64> out_buffer_size, u64 offset,
+ OutBuffer<BufferAttr_HipcMapAlias> out_buffer);
+ Result GetSize(Out<u64> out_size);
+ Result GetDigest(Out<BcatDigest> out_digest);
+
+ FileSys::VirtualDir root;
+ FileSys::VirtualFile current_file;
+};
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_progress_service.cpp b/src/core/hle/service/bcat/delivery_cache_progress_service.cpp
new file mode 100644
index 000000000..79e7e0d95
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_progress_service.cpp
@@ -0,0 +1,41 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/bcat/bcat_types.h"
+#include "core/hle/service/bcat/delivery_cache_progress_service.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::BCAT {
+
+IDeliveryCacheProgressService::IDeliveryCacheProgressService(Core::System& system_,
+ Kernel::KReadableEvent& event_,
+ const DeliveryCacheProgressImpl& impl_)
+ : ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{event_}, impl{impl_} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IDeliveryCacheProgressService::GetEvent>, "Get"},
+ {1, D<&IDeliveryCacheProgressService::GetImpl>, "Get"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IDeliveryCacheProgressService::~IDeliveryCacheProgressService() = default;
+
+Result IDeliveryCacheProgressService::GetEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ *out_event = &event;
+ R_SUCCEED();
+}
+
+Result IDeliveryCacheProgressService::GetImpl(
+ OutLargeData<DeliveryCacheProgressImpl, BufferAttr_HipcPointer> out_impl) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ *out_impl = impl;
+ R_SUCCEED();
+}
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_progress_service.h b/src/core/hle/service/bcat/delivery_cache_progress_service.h
new file mode 100644
index 000000000..f81a13980
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_progress_service.h
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Service::BCAT {
+struct DeliveryCacheProgressImpl;
+
+class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> {
+public:
+ explicit IDeliveryCacheProgressService(Core::System& system_, Kernel::KReadableEvent& event_,
+ const DeliveryCacheProgressImpl& impl_);
+ ~IDeliveryCacheProgressService() override;
+
+private:
+ Result GetEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result GetImpl(OutLargeData<DeliveryCacheProgressImpl, BufferAttr_HipcPointer> out_impl);
+
+ Kernel::KReadableEvent& event;
+ const DeliveryCacheProgressImpl& impl;
+};
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_storage_service.cpp b/src/core/hle/service/bcat/delivery_cache_storage_service.cpp
new file mode 100644
index 000000000..4c79d71f4
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_storage_service.cpp
@@ -0,0 +1,57 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/bcat/bcat_result.h"
+#include "core/hle/service/bcat/delivery_cache_directory_service.h"
+#include "core/hle/service/bcat/delivery_cache_file_service.h"
+#include "core/hle/service/bcat/delivery_cache_storage_service.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::BCAT {
+
+IDeliveryCacheStorageService::IDeliveryCacheStorageService(Core::System& system_,
+ FileSys::VirtualDir root_)
+ : ServiceFramework{system_, "IDeliveryCacheStorageService"}, root(std::move(root_)) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IDeliveryCacheStorageService::CreateFileService>, "CreateFileService"},
+ {1, D<&IDeliveryCacheStorageService::CreateDirectoryService>, "CreateDirectoryService"},
+ {10, D<&IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory>, "EnumerateDeliveryCacheDirectory"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IDeliveryCacheStorageService::~IDeliveryCacheStorageService() = default;
+
+Result IDeliveryCacheStorageService::CreateFileService(
+ OutInterface<IDeliveryCacheFileService> out_interface) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ *out_interface = std::make_shared<IDeliveryCacheFileService>(system, root);
+ R_SUCCEED();
+}
+
+Result IDeliveryCacheStorageService::CreateDirectoryService(
+ OutInterface<IDeliveryCacheDirectoryService> out_interface) {
+ LOG_DEBUG(Service_BCAT, "called");
+
+ *out_interface = std::make_shared<IDeliveryCacheDirectoryService>(system, root);
+ R_SUCCEED();
+}
+
+Result IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory(
+ Out<s32> out_directory_count,
+ OutArray<DirectoryName, BufferAttr_HipcMapAlias> out_directories) {
+ LOG_DEBUG(Service_BCAT, "called, size={:016X}", out_directories.size());
+
+ *out_directory_count =
+ static_cast<s32>(std::min(out_directories.size(), entries.size() - next_read_index));
+ memcpy(out_directories.data(), entries.data() + next_read_index,
+ *out_directory_count * sizeof(DirectoryName));
+ next_read_index += *out_directory_count;
+ R_SUCCEED();
+}
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_storage_service.h b/src/core/hle/service/bcat/delivery_cache_storage_service.h
new file mode 100644
index 000000000..3b8dfb1a3
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_storage_service.h
@@ -0,0 +1,36 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/file_sys/vfs/vfs.h"
+#include "core/hle/service/bcat/bcat_types.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::BCAT {
+class IDeliveryCacheFileService;
+class IDeliveryCacheDirectoryService;
+
+class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> {
+public:
+ explicit IDeliveryCacheStorageService(Core::System& system_, FileSys::VirtualDir root_);
+ ~IDeliveryCacheStorageService() override;
+
+private:
+ Result CreateFileService(OutInterface<IDeliveryCacheFileService> out_interface);
+ Result CreateDirectoryService(OutInterface<IDeliveryCacheDirectoryService> out_interface);
+ Result EnumerateDeliveryCacheDirectory(
+ Out<s32> out_directory_count,
+ OutArray<DirectoryName, BufferAttr_HipcMapAlias> out_directories);
+
+ FileSys::VirtualDir root;
+ std::vector<DirectoryName> entries;
+ std::size_t next_read_index = 0;
+};
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp b/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp
new file mode 100644
index 000000000..ed393f7a2
--- /dev/null
+++ b/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp
@@ -0,0 +1,34 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/bcat/news/newly_arrived_event_holder.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::News {
+
+INewlyArrivedEventHolder::INewlyArrivedEventHolder(Core::System& system_)
+ : ServiceFramework{system_, "INewlyArrivedEventHolder"}, service_context{
+ system_,
+ "INewlyArrivedEventHolder"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&INewlyArrivedEventHolder::Get>, "Get"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ arrived_event = service_context.CreateEvent("INewlyArrivedEventHolder::ArrivedEvent");
+}
+
+INewlyArrivedEventHolder::~INewlyArrivedEventHolder() {
+ service_context.CloseEvent(arrived_event);
+}
+
+Result INewlyArrivedEventHolder::Get(OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_INFO(Service_BCAT, "called");
+
+ *out_event = &arrived_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/newly_arrived_event_holder.h b/src/core/hle/service/bcat/news/newly_arrived_event_holder.h
new file mode 100644
index 000000000..6cc9ae099
--- /dev/null
+++ b/src/core/hle/service/bcat/news/newly_arrived_event_holder.h
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Service::News {
+
+class INewlyArrivedEventHolder final : public ServiceFramework<INewlyArrivedEventHolder> {
+public:
+ explicit INewlyArrivedEventHolder(Core::System& system_);
+ ~INewlyArrivedEventHolder() override;
+
+private:
+ Result Get(OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ Kernel::KEvent* arrived_event;
+ KernelHelpers::ServiceContext service_context;
+};
+
+} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_data_service.cpp b/src/core/hle/service/bcat/news/news_data_service.cpp
new file mode 100644
index 000000000..08103c9c3
--- /dev/null
+++ b/src/core/hle/service/bcat/news/news_data_service.cpp
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/bcat/news/news_data_service.h"
+
+namespace Service::News {
+
+INewsDataService::INewsDataService(Core::System& system_)
+ : ServiceFramework{system_, "INewsDataService"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "Open"},
+ {1, nullptr, "OpenWithNewsRecordV1"},
+ {2, nullptr, "Read"},
+ {3, nullptr, "GetSize"},
+ {1001, nullptr, "OpenWithNewsRecord"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+INewsDataService::~INewsDataService() = default;
+
+} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_data_service.h b/src/core/hle/service/bcat/news/news_data_service.h
new file mode 100644
index 000000000..12082ada4
--- /dev/null
+++ b/src/core/hle/service/bcat/news/news_data_service.h
@@ -0,0 +1,20 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::News {
+
+class INewsDataService final : public ServiceFramework<INewsDataService> {
+public:
+ explicit INewsDataService(Core::System& system_);
+ ~INewsDataService() override;
+};
+
+} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_database_service.cpp b/src/core/hle/service/bcat/news/news_database_service.cpp
new file mode 100644
index 000000000..b94ef0636
--- /dev/null
+++ b/src/core/hle/service/bcat/news/news_database_service.cpp
@@ -0,0 +1,53 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/bcat/news/news_database_service.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::News {
+
+INewsDatabaseService::INewsDatabaseService(Core::System& system_)
+ : ServiceFramework{system_, "INewsDatabaseService"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetListV1"},
+ {1, D<&INewsDatabaseService::Count>, "Count"},
+ {2, nullptr, "CountWithKey"},
+ {3, nullptr, "UpdateIntegerValue"},
+ {4, D<&INewsDatabaseService::UpdateIntegerValueWithAddition>, "UpdateIntegerValueWithAddition"},
+ {5, nullptr, "UpdateStringValue"},
+ {1000, D<&INewsDatabaseService::GetList>, "GetList"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+INewsDatabaseService::~INewsDatabaseService() = default;
+
+Result INewsDatabaseService::Count(Out<s32> out_count,
+ InBuffer<BufferAttr_HipcPointer> buffer_data) {
+ LOG_WARNING(Service_BCAT, "(STUBBED) called, buffer_size={}", buffer_data.size());
+ *out_count = 0;
+ R_SUCCEED();
+}
+
+Result INewsDatabaseService::UpdateIntegerValueWithAddition(
+ u32 value, InBuffer<BufferAttr_HipcPointer> buffer_data_1,
+ InBuffer<BufferAttr_HipcPointer> buffer_data_2) {
+ LOG_WARNING(Service_BCAT, "(STUBBED) called, value={}, buffer_size_1={}, buffer_data_2={}",
+ value, buffer_data_1.size(), buffer_data_2.size());
+ R_SUCCEED();
+}
+
+Result INewsDatabaseService::GetList(Out<s32> out_count, u32 value,
+ OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data,
+ InBuffer<BufferAttr_HipcPointer> buffer_data_1,
+ InBuffer<BufferAttr_HipcPointer> buffer_data_2) {
+ LOG_WARNING(Service_BCAT, "(STUBBED) called, value={}, buffer_size_1={}, buffer_data_2={}",
+ value, buffer_data_1.size(), buffer_data_2.size());
+ *out_count = 0;
+ R_SUCCEED();
+}
+
+} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_database_service.h b/src/core/hle/service/bcat/news/news_database_service.h
new file mode 100644
index 000000000..860b7074c
--- /dev/null
+++ b/src/core/hle/service/bcat/news/news_database_service.h
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::News {
+
+class INewsDatabaseService final : public ServiceFramework<INewsDatabaseService> {
+public:
+ explicit INewsDatabaseService(Core::System& system_);
+ ~INewsDatabaseService() override;
+
+private:
+ Result Count(Out<s32> out_count, InBuffer<BufferAttr_HipcPointer> buffer_data);
+
+ Result UpdateIntegerValueWithAddition(u32 value, InBuffer<BufferAttr_HipcPointer> buffer_data_1,
+ InBuffer<BufferAttr_HipcPointer> buffer_data_2);
+
+ Result GetList(Out<s32> out_count, u32 value,
+ OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data,
+ InBuffer<BufferAttr_HipcPointer> buffer_data_1,
+ InBuffer<BufferAttr_HipcPointer> buffer_data_2);
+};
+
+} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_service.cpp b/src/core/hle/service/bcat/news/news_service.cpp
new file mode 100644
index 000000000..bc6c2afd2
--- /dev/null
+++ b/src/core/hle/service/bcat/news/news_service.cpp
@@ -0,0 +1,57 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/bcat/news/news_service.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::News {
+
+INewsService::INewsService(Core::System& system_) : ServiceFramework{system_, "INewsService"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {10100, nullptr, "PostLocalNews"},
+ {20100, nullptr, "SetPassphrase"},
+ {30100, D<&INewsService::GetSubscriptionStatus>, "GetSubscriptionStatus"},
+ {30101, nullptr, "GetTopicList"},
+ {30110, nullptr, "Unknown30110"},
+ {30200, D<&INewsService::IsSystemUpdateRequired>, "IsSystemUpdateRequired"},
+ {30201, nullptr, "Unknown30201"},
+ {30210, nullptr, "Unknown30210"},
+ {30300, nullptr, "RequestImmediateReception"},
+ {30400, nullptr, "DecodeArchiveFile"},
+ {30500, nullptr, "Unknown30500"},
+ {30900, nullptr, "Unknown30900"},
+ {30901, nullptr, "Unknown30901"},
+ {30902, nullptr, "Unknown30902"},
+ {40100, nullptr, "SetSubscriptionStatus"},
+ {40101, D<&INewsService::RequestAutoSubscription>, "RequestAutoSubscription"},
+ {40200, nullptr, "ClearStorage"},
+ {40201, nullptr, "ClearSubscriptionStatusAll"},
+ {90100, nullptr, "GetNewsDatabaseDump"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+INewsService::~INewsService() = default;
+
+Result INewsService::GetSubscriptionStatus(Out<u32> out_status,
+ InBuffer<BufferAttr_HipcPointer> buffer_data) {
+ LOG_WARNING(Service_BCAT, "(STUBBED) called, buffer_size={}", buffer_data.size());
+ *out_status = 0;
+ R_SUCCEED();
+}
+
+Result INewsService::IsSystemUpdateRequired(Out<bool> out_is_system_update_required) {
+ LOG_WARNING(Service_BCAT, "(STUBBED) called");
+ *out_is_system_update_required = false;
+ R_SUCCEED();
+}
+
+Result INewsService::RequestAutoSubscription(u64 value) {
+ LOG_WARNING(Service_BCAT, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_service.h b/src/core/hle/service/bcat/news/news_service.h
new file mode 100644
index 000000000..f1716a302
--- /dev/null
+++ b/src/core/hle/service/bcat/news/news_service.h
@@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::News {
+
+class INewsService final : public ServiceFramework<INewsService> {
+public:
+ explicit INewsService(Core::System& system_);
+ ~INewsService() override;
+
+private:
+ Result GetSubscriptionStatus(Out<u32> out_status, InBuffer<BufferAttr_HipcPointer> buffer_data);
+
+ Result IsSystemUpdateRequired(Out<bool> out_is_system_update_required);
+
+ Result RequestAutoSubscription(u64 value);
+};
+
+} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/overwrite_event_holder.cpp b/src/core/hle/service/bcat/news/overwrite_event_holder.cpp
new file mode 100644
index 000000000..1712971e4
--- /dev/null
+++ b/src/core/hle/service/bcat/news/overwrite_event_holder.cpp
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/bcat/news/overwrite_event_holder.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::News {
+
+IOverwriteEventHolder::IOverwriteEventHolder(Core::System& system_)
+ : ServiceFramework{system_, "IOverwriteEventHolder"}, service_context{system_,
+ "IOverwriteEventHolder"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IOverwriteEventHolder::Get>, "Get"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ overwrite_event = service_context.CreateEvent("IOverwriteEventHolder::OverwriteEvent");
+}
+
+IOverwriteEventHolder::~IOverwriteEventHolder() {
+ service_context.CloseEvent(overwrite_event);
+}
+
+Result IOverwriteEventHolder::Get(OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_INFO(Service_BCAT, "called");
+
+ *out_event = &overwrite_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/overwrite_event_holder.h b/src/core/hle/service/bcat/news/overwrite_event_holder.h
new file mode 100644
index 000000000..cdc87d782
--- /dev/null
+++ b/src/core/hle/service/bcat/news/overwrite_event_holder.h
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Service::News {
+
+class IOverwriteEventHolder final : public ServiceFramework<IOverwriteEventHolder> {
+public:
+ explicit IOverwriteEventHolder(Core::System& system_);
+ ~IOverwriteEventHolder() override;
+
+private:
+ Result Get(OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ Kernel::KEvent* overwrite_event;
+ KernelHelpers::ServiceContext service_context;
+};
+
+} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/service_creator.cpp b/src/core/hle/service/bcat/news/service_creator.cpp
new file mode 100644
index 000000000..a1b22c004
--- /dev/null
+++ b/src/core/hle/service/bcat/news/service_creator.cpp
@@ -0,0 +1,64 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/bcat/news/newly_arrived_event_holder.h"
+#include "core/hle/service/bcat/news/news_data_service.h"
+#include "core/hle/service/bcat/news/news_database_service.h"
+#include "core/hle/service/bcat/news/news_service.h"
+#include "core/hle/service/bcat/news/overwrite_event_holder.h"
+#include "core/hle/service/bcat/news/service_creator.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::News {
+
+IServiceCreator::IServiceCreator(Core::System& system_, u32 permissions_, const char* name_)
+ : ServiceFramework{system_, name_}, permissions{permissions_} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IServiceCreator::CreateNewsService>, "CreateNewsService"},
+ {1, D<&IServiceCreator::CreateNewlyArrivedEventHolder>, "CreateNewlyArrivedEventHolder"},
+ {2, D<&IServiceCreator::CreateNewsDataService>, "CreateNewsDataService"},
+ {3, D<&IServiceCreator::CreateNewsDatabaseService>, "CreateNewsDatabaseService"},
+ {4, D<&IServiceCreator::CreateOverwriteEventHolder>, "CreateOverwriteEventHolder"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IServiceCreator::~IServiceCreator() = default;
+
+Result IServiceCreator::CreateNewsService(OutInterface<INewsService> out_interface) {
+ LOG_INFO(Service_BCAT, "called");
+ *out_interface = std::make_shared<INewsService>(system);
+ R_SUCCEED();
+}
+
+Result IServiceCreator::CreateNewlyArrivedEventHolder(
+ OutInterface<INewlyArrivedEventHolder> out_interface) {
+ LOG_INFO(Service_BCAT, "called");
+ *out_interface = std::make_shared<INewlyArrivedEventHolder>(system);
+ R_SUCCEED();
+}
+
+Result IServiceCreator::CreateNewsDataService(OutInterface<INewsDataService> out_interface) {
+ LOG_INFO(Service_BCAT, "called");
+ *out_interface = std::make_shared<INewsDataService>(system);
+ R_SUCCEED();
+}
+
+Result IServiceCreator::CreateNewsDatabaseService(
+ OutInterface<INewsDatabaseService> out_interface) {
+ LOG_INFO(Service_BCAT, "called");
+ *out_interface = std::make_shared<INewsDatabaseService>(system);
+ R_SUCCEED();
+}
+
+Result IServiceCreator::CreateOverwriteEventHolder(
+ OutInterface<IOverwriteEventHolder> out_interface) {
+ LOG_INFO(Service_BCAT, "called");
+ *out_interface = std::make_shared<IOverwriteEventHolder>(system);
+ R_SUCCEED();
+}
+
+} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/service_creator.h b/src/core/hle/service/bcat/news/service_creator.h
new file mode 100644
index 000000000..5a62e7c1a
--- /dev/null
+++ b/src/core/hle/service/bcat/news/service_creator.h
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::News {
+class INewsService;
+class INewlyArrivedEventHolder;
+class INewsDataService;
+class INewsDatabaseService;
+class IOverwriteEventHolder;
+
+class IServiceCreator final : public ServiceFramework<IServiceCreator> {
+public:
+ explicit IServiceCreator(Core::System& system_, u32 permissions_, const char* name_);
+ ~IServiceCreator() override;
+
+private:
+ Result CreateNewsService(OutInterface<INewsService> out_interface);
+ Result CreateNewlyArrivedEventHolder(OutInterface<INewlyArrivedEventHolder> out_interface);
+ Result CreateNewsDataService(OutInterface<INewsDataService> out_interface);
+ Result CreateNewsDatabaseService(OutInterface<INewsDatabaseService> out_interface);
+ Result CreateOverwriteEventHolder(OutInterface<IOverwriteEventHolder> out_interface);
+
+ u32 permissions;
+};
+
+} // namespace Service::News
diff --git a/src/core/hle/service/bcat/service_creator.cpp b/src/core/hle/service/bcat/service_creator.cpp
new file mode 100644
index 000000000..ca339e5a6
--- /dev/null
+++ b/src/core/hle/service/bcat/service_creator.cpp
@@ -0,0 +1,62 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/bcat/bcat_service.h"
+#include "core/hle/service/bcat/delivery_cache_storage_service.h"
+#include "core/hle/service/bcat/service_creator.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/filesystem/filesystem.h"
+
+namespace Service::BCAT {
+
+std::unique_ptr<BcatBackend> CreateBackendFromSettings([[maybe_unused]] Core::System& system,
+ DirectoryGetter getter) {
+ return std::make_unique<NullBcatBackend>(std::move(getter));
+}
+
+IServiceCreator::IServiceCreator(Core::System& system_, const char* name_)
+ : ServiceFramework{system_, name_}, fsc{system.GetFileSystemController()} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IServiceCreator::CreateBcatService>, "CreateBcatService"},
+ {1, D<&IServiceCreator::CreateDeliveryCacheStorageService>, "CreateDeliveryCacheStorageService"},
+ {2, D<&IServiceCreator::CreateDeliveryCacheStorageServiceWithApplicationId>, "CreateDeliveryCacheStorageServiceWithApplicationId"},
+ {3, nullptr, "CreateDeliveryCacheProgressService"},
+ {4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+
+ backend =
+ CreateBackendFromSettings(system_, [this](u64 tid) { return fsc.GetBCATDirectory(tid); });
+}
+
+IServiceCreator::~IServiceCreator() = default;
+
+Result IServiceCreator::CreateBcatService(ClientProcessId process_id,
+ OutInterface<IBcatService> out_interface) {
+ LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
+ *out_interface = std::make_shared<IBcatService>(system, *backend);
+ R_SUCCEED();
+}
+
+Result IServiceCreator::CreateDeliveryCacheStorageService(
+ ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
+ LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
+
+ const auto title_id = system.GetApplicationProcessProgramID();
+ *out_interface =
+ std::make_shared<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
+ R_SUCCEED();
+}
+
+Result IServiceCreator::CreateDeliveryCacheStorageServiceWithApplicationId(
+ u64 application_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
+ LOG_DEBUG(Service_BCAT, "called, application_id={:016X}", application_id);
+ *out_interface = std::make_shared<IDeliveryCacheStorageService>(
+ system, fsc.GetBCATDirectory(application_id));
+ R_SUCCEED();
+}
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/service_creator.h b/src/core/hle/service/bcat/service_creator.h
new file mode 100644
index 000000000..50e663324
--- /dev/null
+++ b/src/core/hle/service/bcat/service_creator.h
@@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::FileSystem {
+class FileSystemController;
+}
+
+namespace Service::BCAT {
+class BcatBackend;
+class IBcatService;
+class IDeliveryCacheStorageService;
+
+class IServiceCreator final : public ServiceFramework<IServiceCreator> {
+public:
+ explicit IServiceCreator(Core::System& system_, const char* name_);
+ ~IServiceCreator() override;
+
+private:
+ Result CreateBcatService(ClientProcessId process_id, OutInterface<IBcatService> out_interface);
+
+ Result CreateDeliveryCacheStorageService(
+ ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface);
+
+ Result CreateDeliveryCacheStorageServiceWithApplicationId(
+ u64 application_id, OutInterface<IDeliveryCacheStorageService> out_interface);
+
+ std::unique_ptr<BcatBackend> backend;
+ Service::FileSystem::FileSystemController& fsc;
+};
+
+} // namespace Service::BCAT
diff --git a/src/core/hle/service/cmif_serialization.h b/src/core/hle/service/cmif_serialization.h
index e985fe317..f24682c34 100644
--- a/src/core/hle/service/cmif_serialization.h
+++ b/src/core/hle/service/cmif_serialization.h
@@ -280,7 +280,7 @@ void ReadInArgument(bool is_domain, CallArguments& args, const u8* raw_data, HLE
u32 value{};
std::memcpy(&value, raw_data + ArgOffset, ArgSize);
- std::get<ArgIndex>(args) = ctx.GetDomainHandler<ArgType::Type>(value - 1);
+ std::get<ArgIndex>(args) = ctx.GetDomainHandler<typename ArgType::element_type>(value - 1);
return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) {
diff --git a/src/core/hle/service/cmif_types.h b/src/core/hle/service/cmif_types.h
index 325304d5c..dad358b87 100644
--- a/src/core/hle/service/cmif_types.h
+++ b/src/core/hle/service/cmif_types.h
@@ -65,6 +65,14 @@ struct ClientProcessId {
};
struct ProcessId {
+ explicit ProcessId() : pid() {}
+ explicit ProcessId(u64 p) : pid(p) {}
+ /* implicit */ ProcessId(const ClientProcessId& c) : pid(c.pid) {}
+
+ bool operator==(const ProcessId& rhs) const {
+ return pid == rhs.pid;
+ }
+
explicit operator bool() const {
return pid != 0;
}
@@ -291,4 +299,4 @@ private:
};
// clang-format on
-} // namespace Service \ No newline at end of file
+} // namespace Service
diff --git a/src/core/hle/service/glue/time/worker.cpp b/src/core/hle/service/glue/time/worker.cpp
index f44f3077e..8787f2dcd 100644
--- a/src/core/hle/service/glue/time/worker.cpp
+++ b/src/core/hle/service/glue/time/worker.cpp
@@ -7,6 +7,7 @@
#include "core/hle/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
#include "core/hle/service/glue/time/worker.h"
+#include "core/hle/service/os/multi_wait_utils.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/service_manager.h"
#include "core/hle/service/psc/time/static.h"
@@ -143,82 +144,46 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
Common::SetCurrentThreadName("TimeWorker");
Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
- enum class EventType {
- Exit = 0,
- IpmModuleService_GetEvent = 1,
- PowerStateChange = 2,
- SignalAlarms = 3,
- UpdateLocalSystemClock = 4,
- UpdateNetworkSystemClock = 5,
- UpdateEphemeralSystemClock = 6,
- UpdateSteadyClock = 7,
- UpdateFileTimestamp = 8,
- AutoCorrect = 9,
- Max = 10,
- };
-
- s32 num_objs{};
- std::array<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{};
- std::array<EventType, static_cast<u32>(EventType::Max)> wait_indices{};
-
- const auto AddWaiter{
- [&](Kernel::KSynchronizationObject* synchronization_object, EventType type) {
- // Open a new reference to the object.
- synchronization_object->Open();
-
- // Insert into the list.
- wait_indices[num_objs] = type;
- wait_objs[num_objs++] = synchronization_object;
- }};
-
while (!stop_token.stop_requested()) {
- SCOPE_EXIT({
- for (s32 i = 0; i < num_objs; i++) {
- wait_objs[i]->Close();
- }
- });
+ enum class EventType : s32 {
+ Exit = 0,
+ PowerStateChange = 1,
+ SignalAlarms = 2,
+ UpdateLocalSystemClock = 3,
+ UpdateNetworkSystemClock = 4,
+ UpdateEphemeralSystemClock = 5,
+ UpdateSteadyClock = 6,
+ UpdateFileTimestamp = 7,
+ AutoCorrect = 8,
+ };
+
+ s32 index{};
- num_objs = {};
- wait_objs = {};
if (m_pm_state_change_handler.m_priority != 0) {
- AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
- // TODO
- // AddWaiter(gIPmModuleService::GetEvent(), 1);
- AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange);
+ // TODO: gIPmModuleService::GetEvent() 1
+ index = WaitAny(m_system.Kernel(),
+ &m_event->GetReadableEvent(), // 0
+ &m_alarm_worker.GetEvent() // 1
+ );
} else {
- AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
- // TODO
- // AddWaiter(gIPmModuleService::GetEvent(), 1);
- AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange);
- AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms);
- AddWaiter(m_local_clock_event, EventType::UpdateLocalSystemClock);
- AddWaiter(m_network_clock_event, EventType::UpdateNetworkSystemClock);
- AddWaiter(m_ephemeral_clock_event, EventType::UpdateEphemeralSystemClock);
- AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock);
- AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp);
- AddWaiter(m_standard_user_auto_correct_clock_event, EventType::AutoCorrect);
+ // TODO: gIPmModuleService::GetEvent() 1
+ index = WaitAny(m_system.Kernel(),
+ &m_event->GetReadableEvent(), // 0
+ &m_alarm_worker.GetEvent(), // 1
+ &m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
+ m_local_clock_event, // 3
+ m_network_clock_event, // 4
+ m_ephemeral_clock_event, // 5
+ &m_timer_steady_clock->GetReadableEvent(), // 6
+ &m_timer_file_system->GetReadableEvent(), // 7
+ m_standard_user_auto_correct_clock_event // 8
+ );
}
- s32 out_index{-1};
- Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(),
- num_objs, -1);
- ASSERT(out_index >= 0 && out_index < num_objs);
-
- if (stop_token.stop_requested()) {
- return;
- }
-
- switch (wait_indices[out_index]) {
+ switch (static_cast<EventType>(index)) {
case EventType::Exit:
return;
- case EventType::IpmModuleService_GetEvent:
- // TODO
- // IPmModuleService::GetEvent()
- // clear the event
- // Handle power state change event
- break;
-
case EventType::PowerStateChange:
m_alarm_worker.GetEvent().Clear();
if (m_pm_state_change_handler.m_priority <= 1) {
@@ -235,19 +200,19 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
m_local_clock_event->Clear();
Service::PSC::Time::SystemClockContext context{};
- auto res = m_local_clock->GetSystemClockContext(&context);
- ASSERT(res == ResultSuccess);
+ R_ASSERT(m_local_clock->GetSystemClockContext(&context));
m_set_sys->SetUserSystemClockContext(context);
-
m_file_timestamp_worker.SetFilesystemPosixTime();
- } break;
+ break;
+ }
case EventType::UpdateNetworkSystemClock: {
m_network_clock_event->Clear();
+
Service::PSC::Time::SystemClockContext context{};
- auto res = m_network_clock->GetSystemClockContext(&context);
- ASSERT(res == ResultSuccess);
+ R_ASSERT(m_network_clock->GetSystemClockContext(&context));
+
m_set_sys->SetNetworkSystemClockContext(context);
s64 time{};
@@ -267,7 +232,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
}
m_file_timestamp_worker.SetFilesystemPosixTime();
- } break;
+ break;
+ }
case EventType::UpdateEphemeralSystemClock: {
m_ephemeral_clock_event->Clear();
@@ -295,7 +261,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
if (!g_ig_report_ephemeral_clock_context_set) {
g_ig_report_ephemeral_clock_context_set = true;
}
- } break;
+ break;
+ }
case EventType::UpdateSteadyClock:
m_timer_steady_clock->Clear();
@@ -314,21 +281,20 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
m_standard_user_auto_correct_clock_event->Clear();
bool automatic_correction{};
- auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
- &automatic_correction);
- ASSERT(res == ResultSuccess);
+ R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
+ &automatic_correction));
Service::PSC::Time::SteadyClockTimePoint time_point{};
- res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point);
- ASSERT(res == ResultSuccess);
+ R_ASSERT(
+ m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
- } break;
+ break;
+ }
default:
UNREACHABLE();
- break;
}
}
}
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
index 22471e9e2..7126a1dcd 100644
--- a/src/core/hle/service/hid/hid_system_server.cpp
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -508,13 +508,8 @@ void IHidSystemServer::RegisterAppletResourceUserId(HLERequestContext& ctx) {
Result result = GetResourceManager()->RegisterAppletResourceUserId(
parameters.applet_resource_user_id, parameters.enable_input);
- if (result.IsSuccess()) {
- // result = GetResourceManager()->GetNpad()->RegisterAppletResourceUserId(
- // parameters.applet_resource_user_id);
- }
-
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IHidSystemServer::UnregisterAppletResourceUserId(HLERequestContext& ctx) {
@@ -524,8 +519,6 @@ void IHidSystemServer::UnregisterAppletResourceUserId(HLERequestContext& ctx) {
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
GetResourceManager()->UnregisterAppletResourceUserId(applet_resource_user_id);
- // GetResourceManager()->GetNpad()->UnregisterAppletResourceUserId(applet_resource_user_id);
- // GetResourceManager()->GetPalma()->UnregisterAppletResourceUserId(applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp
index 50e1ed756..e0367e774 100644
--- a/src/core/hle/service/hle_ipc.cpp
+++ b/src/core/hle/service/hle_ipc.cpp
@@ -299,8 +299,12 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer() {
if (GetManager()->IsDomain()) {
current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
for (auto& object : outgoing_domain_objects) {
- GetManager()->AppendDomainHandler(std::move(object));
- cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount());
+ if (object) {
+ GetManager()->AppendDomainHandler(std::move(object));
+ cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount());
+ } else {
+ cmd_buf[current_offset++] = 0;
+ }
}
}
diff --git a/src/core/hle/service/event.cpp b/src/core/hle/service/os/event.cpp
index 375660d72..ec52c17fd 100644
--- a/src/core/hle/service/event.cpp
+++ b/src/core/hle/service/os/event.cpp
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_event.h"
-#include "core/hle/service/event.h"
#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/os/event.h"
namespace Service {
diff --git a/src/core/hle/service/event.h b/src/core/hle/service/os/event.h
index cdbc4635a..cdbc4635a 100644
--- a/src/core/hle/service/event.h
+++ b/src/core/hle/service/os/event.h
diff --git a/src/core/hle/service/os/multi_wait.cpp b/src/core/hle/service/os/multi_wait.cpp
new file mode 100644
index 000000000..7b80d28be
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait.cpp
@@ -0,0 +1,59 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/k_hardware_timer.h"
+#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/svc_common.h"
+#include "core/hle/service/os/multi_wait.h"
+
+namespace Service {
+
+MultiWait::MultiWait() = default;
+MultiWait::~MultiWait() = default;
+
+MultiWaitHolder* MultiWait::WaitAny(Kernel::KernelCore& kernel) {
+ return this->TimedWaitImpl(kernel, -1);
+}
+
+MultiWaitHolder* MultiWait::TryWaitAny(Kernel::KernelCore& kernel) {
+ return this->TimedWaitImpl(kernel, 0);
+}
+
+MultiWaitHolder* MultiWait::TimedWaitAny(Kernel::KernelCore& kernel, s64 timeout_ns) {
+ return this->TimedWaitImpl(kernel, kernel.HardwareTimer().GetTick() + timeout_ns);
+}
+
+MultiWaitHolder* MultiWait::TimedWaitImpl(Kernel::KernelCore& kernel, s64 timeout_tick) {
+ std::array<MultiWaitHolder*, Kernel::Svc::ArgumentHandleCountMax> holders{};
+ std::array<Kernel::KSynchronizationObject*, Kernel::Svc::ArgumentHandleCountMax> objects{};
+
+ s32 out_index = -1;
+ s32 num_objects = 0;
+
+ for (auto it = m_wait_list.begin(); it != m_wait_list.end(); it++) {
+ ASSERT(num_objects < Kernel::Svc::ArgumentHandleCountMax);
+ holders[num_objects] = std::addressof(*it);
+ objects[num_objects] = it->GetNativeHandle();
+ num_objects++;
+ }
+
+ Kernel::KSynchronizationObject::Wait(kernel, std::addressof(out_index), objects.data(),
+ num_objects, timeout_tick);
+
+ if (out_index == -1) {
+ return nullptr;
+ } else {
+ return holders[out_index];
+ }
+}
+
+void MultiWait::MoveAll(MultiWait* other) {
+ while (!other->m_wait_list.empty()) {
+ MultiWaitHolder& holder = other->m_wait_list.front();
+ holder.UnlinkFromMultiWait();
+ holder.LinkToMultiWait(this);
+ }
+}
+
+} // namespace Service
diff --git a/src/core/hle/service/os/multi_wait.h b/src/core/hle/service/os/multi_wait.h
new file mode 100644
index 000000000..340c611b5
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait.h
@@ -0,0 +1,36 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/os/multi_wait_holder.h"
+
+namespace Kernel {
+class KernelCore;
+}
+
+namespace Service {
+
+class MultiWait final {
+public:
+ explicit MultiWait();
+ ~MultiWait();
+
+public:
+ MultiWaitHolder* WaitAny(Kernel::KernelCore& kernel);
+ MultiWaitHolder* TryWaitAny(Kernel::KernelCore& kernel);
+ MultiWaitHolder* TimedWaitAny(Kernel::KernelCore& kernel, s64 timeout_ns);
+ // TODO: SdkReplyAndReceive?
+
+ void MoveAll(MultiWait* other);
+
+private:
+ MultiWaitHolder* TimedWaitImpl(Kernel::KernelCore& kernel, s64 timeout_tick);
+
+private:
+ friend class MultiWaitHolder;
+ using ListType = Common::IntrusiveListMemberTraits<&MultiWaitHolder::m_list_node>::ListType;
+ ListType m_wait_list{};
+};
+
+} // namespace Service
diff --git a/src/core/hle/service/os/multi_wait_holder.cpp b/src/core/hle/service/os/multi_wait_holder.cpp
new file mode 100644
index 000000000..01efa045b
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait_holder.cpp
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/os/multi_wait.h"
+#include "core/hle/service/os/multi_wait_holder.h"
+
+namespace Service {
+
+void MultiWaitHolder::LinkToMultiWait(MultiWait* multi_wait) {
+ if (m_multi_wait != nullptr) {
+ UNREACHABLE();
+ }
+
+ m_multi_wait = multi_wait;
+ m_multi_wait->m_wait_list.push_back(*this);
+}
+
+void MultiWaitHolder::UnlinkFromMultiWait() {
+ if (m_multi_wait) {
+ m_multi_wait->m_wait_list.erase(m_multi_wait->m_wait_list.iterator_to(*this));
+ m_multi_wait = nullptr;
+ }
+}
+
+} // namespace Service
diff --git a/src/core/hle/service/os/multi_wait_holder.h b/src/core/hle/service/os/multi_wait_holder.h
new file mode 100644
index 000000000..646395a3f
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait_holder.h
@@ -0,0 +1,44 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/intrusive_list.h"
+
+namespace Kernel {
+class KSynchronizationObject;
+} // namespace Kernel
+
+namespace Service {
+
+class MultiWait;
+
+class MultiWaitHolder {
+public:
+ explicit MultiWaitHolder(Kernel::KSynchronizationObject* native_handle)
+ : m_native_handle(native_handle) {}
+
+ void LinkToMultiWait(MultiWait* multi_wait);
+ void UnlinkFromMultiWait();
+
+ void SetUserData(uintptr_t user_data) {
+ m_user_data = user_data;
+ }
+
+ uintptr_t GetUserData() const {
+ return m_user_data;
+ }
+
+ Kernel::KSynchronizationObject* GetNativeHandle() const {
+ return m_native_handle;
+ }
+
+private:
+ friend class MultiWait;
+ Common::IntrusiveListNode m_list_node{};
+ MultiWait* m_multi_wait{};
+ Kernel::KSynchronizationObject* m_native_handle{};
+ uintptr_t m_user_data{};
+};
+
+} // namespace Service
diff --git a/src/core/hle/service/os/multi_wait_utils.h b/src/core/hle/service/os/multi_wait_utils.h
new file mode 100644
index 000000000..96d3a10f3
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait_utils.h
@@ -0,0 +1,109 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/os/multi_wait.h"
+
+namespace Service {
+
+namespace impl {
+
+class AutoMultiWaitHolder {
+private:
+ MultiWaitHolder m_holder;
+
+public:
+ template <typename T>
+ explicit AutoMultiWaitHolder(MultiWait* multi_wait, T&& arg) : m_holder(arg) {
+ m_holder.LinkToMultiWait(multi_wait);
+ }
+
+ ~AutoMultiWaitHolder() {
+ m_holder.UnlinkFromMultiWait();
+ }
+
+ std::pair<MultiWaitHolder*, int> ConvertResult(const std::pair<MultiWaitHolder*, int> result,
+ int index) {
+ if (result.first == std::addressof(m_holder)) {
+ return std::make_pair(static_cast<MultiWaitHolder*>(nullptr), index);
+ } else {
+ return result;
+ }
+ }
+};
+
+using WaitAnyFunction = decltype(&MultiWait::WaitAny);
+
+inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
+ MultiWait* multi_wait, WaitAnyFunction func,
+ int) {
+ return std::pair<MultiWaitHolder*, int>((multi_wait->*func)(kernel), -1);
+}
+
+template <typename T, typename... Args>
+inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
+ MultiWait* multi_wait, WaitAnyFunction func,
+ int index, T&& x, Args&&... args) {
+ AutoMultiWaitHolder holder(multi_wait, std::forward<T>(x));
+ return holder.ConvertResult(
+ WaitAnyImpl(kernel, multi_wait, func, index + 1, std::forward<Args>(args)...), index);
+}
+
+template <typename... Args>
+inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
+ MultiWait* multi_wait, WaitAnyFunction func,
+ Args&&... args) {
+ return WaitAnyImpl(kernel, multi_wait, func, 0, std::forward<Args>(args)...);
+}
+
+template <typename... Args>
+inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
+ WaitAnyFunction func, Args&&... args) {
+ MultiWait temp_multi_wait;
+ return WaitAnyImpl(kernel, std::addressof(temp_multi_wait), func, 0,
+ std::forward<Args>(args)...);
+}
+
+class NotBoolButInt {
+public:
+ constexpr NotBoolButInt(int v) : m_value(v) {}
+ constexpr operator int() const {
+ return m_value;
+ }
+ explicit operator bool() const = delete;
+
+private:
+ int m_value;
+};
+
+} // namespace impl
+
+template <typename... Args>
+ requires(sizeof...(Args) > 0)
+inline std::pair<MultiWaitHolder*, int> WaitAny(Kernel::KernelCore& kernel, MultiWait* multi_wait,
+ Args&&... args) {
+ return impl::WaitAnyImpl(kernel, &MultiWait::WaitAny, multi_wait, std::forward<Args>(args)...);
+}
+
+template <typename... Args>
+ requires(sizeof...(Args) > 0)
+inline int WaitAny(Kernel::KernelCore& kernel, Args&&... args) {
+ return impl::WaitAnyImpl(kernel, &MultiWait::WaitAny, std::forward<Args>(args)...).second;
+}
+
+template <typename... Args>
+ requires(sizeof...(Args) > 0)
+inline std::pair<MultiWaitHolder*, int> TryWaitAny(Kernel::KernelCore& kernel,
+ MultiWait* multi_wait, Args&&... args) {
+ return impl::WaitAnyImpl(kernel, &MultiWait::TryWaitAny, multi_wait,
+ std::forward<Args>(args)...);
+}
+
+template <typename... Args>
+ requires(sizeof...(Args) > 0)
+inline impl::NotBoolButInt TryWaitAny(Kernel::KernelCore& kernel, Args&&... args) {
+ return impl::WaitAnyImpl(kernel, &MultiWait::TryWaitAny, std::forward<Args>(args)...).second;
+}
+
+} // namespace Service
diff --git a/src/core/hle/service/mutex.cpp b/src/core/hle/service/os/mutex.cpp
index b0ff71d1b..6009f4866 100644
--- a/src/core/hle/service/mutex.cpp
+++ b/src/core/hle/service/os/mutex.cpp
@@ -4,7 +4,7 @@
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_synchronization_object.h"
-#include "core/hle/service/mutex.h"
+#include "core/hle/service/os/mutex.h"
namespace Service {
diff --git a/src/core/hle/service/mutex.h b/src/core/hle/service/os/mutex.h
index 95ac9b117..95ac9b117 100644
--- a/src/core/hle/service/mutex.h
+++ b/src/core/hle/service/os/mutex.h
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index 8ef49387d..8c7f94c8c 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -20,50 +20,91 @@
namespace Service {
-constexpr size_t MaximumWaitObjects = 0x40;
-
-enum HandleType {
+enum class UserDataTag {
Port,
Session,
DeferEvent,
- Event,
};
-ServerManager::ServerManager(Core::System& system) : m_system{system}, m_serve_mutex{system} {
+class Port : public MultiWaitHolder, public Common::IntrusiveListBaseNode<Port> {
+public:
+ explicit Port(Kernel::KServerPort* server_port, SessionRequestHandlerFactory&& handler_factory)
+ : MultiWaitHolder(server_port), m_handler_factory(std::move(handler_factory)) {
+ this->SetUserData(static_cast<uintptr_t>(UserDataTag::Port));
+ }
+
+ ~Port() {
+ this->GetNativeHandle()->Close();
+ }
+
+ SessionRequestHandlerPtr CreateHandler() {
+ return m_handler_factory();
+ }
+
+private:
+ const SessionRequestHandlerFactory m_handler_factory;
+};
+
+class Session : public MultiWaitHolder, public Common::IntrusiveListBaseNode<Session> {
+public:
+ explicit Session(Kernel::KServerSession* server_session,
+ std::shared_ptr<SessionRequestManager>&& manager)
+ : MultiWaitHolder(server_session), m_manager(std::move(manager)) {
+ this->SetUserData(static_cast<uintptr_t>(UserDataTag::Session));
+ }
+
+ ~Session() {
+ this->GetNativeHandle()->Close();
+ }
+
+ std::shared_ptr<SessionRequestManager>& GetManager() {
+ return m_manager;
+ }
+
+ std::shared_ptr<HLERequestContext>& GetContext() {
+ return m_context;
+ }
+
+private:
+ std::shared_ptr<SessionRequestManager> m_manager;
+ std::shared_ptr<HLERequestContext> m_context;
+};
+
+ServerManager::ServerManager(Core::System& system) : m_system{system}, m_selection_mutex{system} {
// Initialize event.
- m_event = Kernel::KEvent::Create(system.Kernel());
- m_event->Initialize(nullptr);
+ m_wakeup_event = Kernel::KEvent::Create(system.Kernel());
+ m_wakeup_event->Initialize(nullptr);
// Register event.
- Kernel::KEvent::Register(system.Kernel(), m_event);
+ Kernel::KEvent::Register(system.Kernel(), m_wakeup_event);
+
+ // Link to holder.
+ m_wakeup_holder.emplace(std::addressof(m_wakeup_event->GetReadableEvent()));
+ m_wakeup_holder->LinkToMultiWait(std::addressof(m_deferred_list));
}
ServerManager::~ServerManager() {
// Signal stop.
m_stop_source.request_stop();
- m_event->Signal();
+ m_wakeup_event->Signal();
// Wait for processing to stop.
m_stopped.Wait();
m_threads.clear();
- // Clean up server ports.
- for (const auto& [port, handler] : m_ports) {
- port->Close();
+ // Clean up ports.
+ for (auto it = m_servers.begin(); it != m_servers.end(); it = m_servers.erase(it)) {
+ delete std::addressof(*it);
}
// Clean up sessions.
- for (const auto& [session, manager] : m_sessions) {
- session->Close();
- }
-
- for (const auto& request : m_deferrals) {
- request.session->Close();
+ for (auto it = m_sessions.begin(); it != m_sessions.end(); it = m_sessions.erase(it)) {
+ delete std::addressof(*it);
}
- // Close event.
- m_event->GetReadableEvent().Close();
- m_event->Close();
+ // Close wakeup event.
+ m_wakeup_event->GetReadableEvent().Close();
+ m_wakeup_event->Close();
if (m_deferral_event) {
m_deferral_event->GetReadableEvent().Close();
@@ -75,19 +116,19 @@ void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
server_manager->m_system.RunServer(std::move(server_manager));
}
-Result ServerManager::RegisterSession(Kernel::KServerSession* session,
+Result ServerManager::RegisterSession(Kernel::KServerSession* server_session,
std::shared_ptr<SessionRequestManager> manager) {
- ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
-
// We are taking ownership of the server session, so don't open it.
+ auto* session = new Session(server_session, std::move(manager));
+
// Begin tracking the server session.
{
- std::scoped_lock ll{m_list_mutex};
- m_sessions.emplace(session, std::move(manager));
+ std::scoped_lock ll{m_deferred_list_mutex};
+ m_sessions.push_back(*session);
}
- // Signal the wakeup event.
- m_event->Signal();
+ // Register to wait on the session.
+ this->LinkToDeferredList(session);
R_SUCCEED();
}
@@ -95,21 +136,22 @@ Result ServerManager::RegisterSession(Kernel::KServerSession* session,
Result ServerManager::RegisterNamedService(const std::string& service_name,
SessionRequestHandlerFactory&& handler_factory,
u32 max_sessions) {
- ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
-
// Add the new server to sm: and get the moved server port.
Kernel::KServerPort* server_port{};
R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name,
max_sessions, handler_factory));
+ // We are taking ownership of the server port, so don't open it.
+ auto* server = new Port(server_port, std::move(handler_factory));
+
// Begin tracking the server port.
{
- std::scoped_lock ll{m_list_mutex};
- m_ports.emplace(server_port, std::move(handler_factory));
+ std::scoped_lock ll{m_deferred_list_mutex};
+ m_servers.push_back(*server);
}
- // Signal the wakeup event.
- m_event->Signal();
+ // Register to wait on the server port.
+ this->LinkToDeferredList(server);
R_SUCCEED();
}
@@ -127,8 +169,6 @@ Result ServerManager::RegisterNamedService(const std::string& service_name,
Result ServerManager::ManageNamedPort(const std::string& service_name,
SessionRequestHandlerFactory&& handler_factory,
u32 max_sessions) {
- ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
-
// Create a new port.
auto* port = Kernel::KPort::Create(m_system.Kernel());
port->Initialize(max_sessions, false, 0);
@@ -149,12 +189,18 @@ Result ServerManager::ManageNamedPort(const std::string& service_name,
// Open a new reference to the server port.
port->GetServerPort().Open();
- // Begin tracking the server port.
+ // Transfer ownership into a new port object.
+ auto* server = new Port(std::addressof(port->GetServerPort()), std::move(handler_factory));
+
+ // Begin tracking the port.
{
- std::scoped_lock ll{m_list_mutex};
- m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler_factory));
+ std::scoped_lock ll{m_deferred_list_mutex};
+ m_servers.push_back(*server);
}
+ // Register to wait on the port.
+ this->LinkToDeferredList(server);
+
// We succeeded.
R_SUCCEED();
}
@@ -173,6 +219,11 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
// Set the output.
*out_event = m_deferral_event;
+ // Register to wait on the event.
+ m_deferral_holder.emplace(std::addressof(m_deferral_event->GetReadableEvent()));
+ m_deferral_holder->SetUserData(static_cast<uintptr_t>(UserDataTag::DeferEvent));
+ this->LinkToDeferredList(std::addressof(*m_deferral_holder));
+
// We succeeded.
R_SUCCEED();
}
@@ -191,270 +242,185 @@ Result ServerManager::LoopProcess() {
R_RETURN(this->LoopProcessImpl());
}
-Result ServerManager::LoopProcessImpl() {
- while (!m_stop_source.stop_requested()) {
- R_TRY(this->WaitAndProcessImpl());
+void ServerManager::LinkToDeferredList(MultiWaitHolder* holder) {
+ // Link.
+ {
+ std::scoped_lock lk{m_deferred_list_mutex};
+ holder->LinkToMultiWait(std::addressof(m_deferred_list));
}
- R_SUCCEED();
+ // Signal the wakeup event.
+ m_wakeup_event->Signal();
}
-Result ServerManager::WaitAndProcessImpl() {
- Kernel::KScopedAutoObject<Kernel::KSynchronizationObject> wait_obj;
- HandleType wait_type{};
+void ServerManager::LinkDeferred() {
+ std::scoped_lock lk{m_deferred_list_mutex};
+ m_multi_wait.MoveAll(std::addressof(m_deferred_list));
+}
+MultiWaitHolder* ServerManager::WaitSignaled() {
// Ensure we are the only thread waiting for this server.
- std::unique_lock sl{m_serve_mutex};
+ std::scoped_lock lk{m_selection_mutex};
- // If we're done, return before we start waiting.
- R_SUCCEED_IF(m_stop_source.stop_requested());
+ while (true) {
+ this->LinkDeferred();
- // Wait for a tracked object to become signaled.
- {
- s32 num_objs{};
- std::array<HandleType, MaximumWaitObjects> wait_types{};
- std::array<Kernel::KSynchronizationObject*, MaximumWaitObjects> wait_objs{};
-
- const auto AddWaiter{
- [&](Kernel::KSynchronizationObject* synchronization_object, HandleType type) {
- // Open a new reference to the object.
- synchronization_object->Open();
-
- // Insert into the list.
- wait_types[num_objs] = type;
- wait_objs[num_objs++] = synchronization_object;
- }};
-
- {
- std::scoped_lock ll{m_list_mutex};
-
- // Add all of our ports.
- for (const auto& [port, handler] : m_ports) {
- AddWaiter(port, HandleType::Port);
- }
-
- // Add all of our sessions.
- for (const auto& [session, manager] : m_sessions) {
- AddWaiter(session, HandleType::Session);
- }
+ // If we're done, return before we start waiting.
+ if (m_stop_source.stop_requested()) {
+ return nullptr;
}
- // Add the deferral wakeup event.
- if (m_deferral_event != nullptr) {
- AddWaiter(std::addressof(m_deferral_event->GetReadableEvent()), HandleType::DeferEvent);
+ auto* selected = m_multi_wait.WaitAny(m_system.Kernel());
+ if (selected == std::addressof(*m_wakeup_holder)) {
+ // Clear and restart if we were woken up.
+ m_wakeup_event->Clear();
+ } else {
+ // Unlink and handle the event.
+ selected->UnlinkFromMultiWait();
+ return selected;
}
+ }
+}
- // Add the wakeup event.
- AddWaiter(std::addressof(m_event->GetReadableEvent()), HandleType::Event);
-
- // Clean up extra references on exit.
- SCOPE_EXIT({
- for (s32 i = 0; i < num_objs; i++) {
- wait_objs[i]->Close();
- }
- });
-
- // Wait for a signal.
- s32 out_index{-1};
- R_TRY_CATCH(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index,
- wait_objs.data(), num_objs, -1)) {
- R_CATCH(Kernel::ResultSessionClosed) {
- // On session closed, index is updated and we don't want to return an error.
- }
- }
- R_END_TRY_CATCH;
- ASSERT(out_index >= 0 && out_index < num_objs);
+Result ServerManager::Process(MultiWaitHolder* holder) {
+ switch (static_cast<UserDataTag>(holder->GetUserData())) {
+ case UserDataTag::Session:
+ R_RETURN(this->OnSessionEvent(static_cast<Session*>(holder)));
+ case UserDataTag::Port:
+ R_RETURN(this->OnPortEvent(static_cast<Port*>(holder)));
+ case UserDataTag::DeferEvent:
+ R_RETURN(this->OnDeferralEvent());
+ default:
+ UNREACHABLE();
+ }
+}
- // Set the output index.
- wait_obj = wait_objs[out_index];
- wait_type = wait_types[out_index];
+bool ServerManager::WaitAndProcessImpl() {
+ if (auto* signaled_holder = this->WaitSignaled(); signaled_holder != nullptr) {
+ R_ASSERT(this->Process(signaled_holder));
+ return true;
+ } else {
+ return false;
}
+}
- // Process what we just received, temporarily removing the object so it is
- // not processed concurrently by another thread.
- {
- switch (wait_type) {
- case HandleType::Port: {
- // Port signaled.
- auto* port = wait_obj->DynamicCast<Kernel::KServerPort*>();
- SessionRequestHandlerFactory handler_factory;
-
- // Remove from tracking.
- {
- std::scoped_lock ll{m_list_mutex};
- ASSERT(m_ports.contains(port));
- m_ports.at(port).swap(handler_factory);
- m_ports.erase(port);
- }
-
- // Allow other threads to serve.
- sl.unlock();
-
- // Finish.
- R_RETURN(this->OnPortEvent(port, std::move(handler_factory)));
- }
- case HandleType::Session: {
- // Session signaled.
- auto* session = wait_obj->DynamicCast<Kernel::KServerSession*>();
- std::shared_ptr<SessionRequestManager> manager;
-
- // Remove from tracking.
- {
- std::scoped_lock ll{m_list_mutex};
- ASSERT(m_sessions.contains(session));
- m_sessions.at(session).swap(manager);
- m_sessions.erase(session);
- }
-
- // Allow other threads to serve.
- sl.unlock();
-
- // Finish.
- R_RETURN(this->OnSessionEvent(session, std::move(manager)));
- }
- case HandleType::DeferEvent: {
- // Clear event.
- ASSERT(R_SUCCEEDED(m_deferral_event->Clear()));
-
- // Drain the list of deferrals while we process.
- std::list<RequestState> deferrals;
- {
- std::scoped_lock ll{m_list_mutex};
- m_deferrals.swap(deferrals);
- }
-
- // Allow other threads to serve.
- sl.unlock();
-
- // Finish.
- R_RETURN(this->OnDeferralEvent(std::move(deferrals)));
- }
- case HandleType::Event: {
- // Clear event and finish.
- R_RETURN(m_event->Clear());
- }
- default: {
- UNREACHABLE();
- }
- }
+Result ServerManager::LoopProcessImpl() {
+ while (!m_stop_source.stop_requested()) {
+ this->WaitAndProcessImpl();
}
+
+ R_SUCCEED();
}
-Result ServerManager::OnPortEvent(Kernel::KServerPort* port,
- SessionRequestHandlerFactory&& handler_factory) {
+Result ServerManager::OnPortEvent(Port* server) {
// Accept a new server session.
- Kernel::KServerSession* session = port->AcceptSession();
- ASSERT(session != nullptr);
+ auto* server_port = static_cast<Kernel::KServerPort*>(server->GetNativeHandle());
+ Kernel::KServerSession* server_session = server_port->AcceptSession();
+ ASSERT(server_session != nullptr);
// Create the session manager and install the handler.
auto manager = std::make_shared<SessionRequestManager>(m_system.Kernel(), *this);
- manager->SetSessionHandler(handler_factory());
+ manager->SetSessionHandler(server->CreateHandler());
- // Track the server session.
- {
- std::scoped_lock ll{m_list_mutex};
- m_ports.emplace(port, std::move(handler_factory));
- m_sessions.emplace(session, std::move(manager));
- }
+ // Create and register the new session.
+ this->RegisterSession(server_session, std::move(manager));
- // Signal the wakeup event.
- m_event->Signal();
+ // Resume tracking the port.
+ this->LinkToDeferredList(server);
// We succeeded.
R_SUCCEED();
}
-Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
- std::shared_ptr<SessionRequestManager>&& manager) {
- Result rc{ResultSuccess};
+Result ServerManager::OnSessionEvent(Session* session) {
+ Result res = ResultSuccess;
// Try to receive a message.
- std::shared_ptr<HLERequestContext> context;
- rc = session->ReceiveRequestHLE(&context, manager);
+ auto* server_session = static_cast<Kernel::KServerSession*>(session->GetNativeHandle());
+ res = server_session->ReceiveRequestHLE(&session->GetContext(), session->GetManager());
// If the session has been closed, we're done.
- if (rc == Kernel::ResultSessionClosed) {
- // Close the session.
- session->Close();
-
- // Finish.
+ if (res == Kernel::ResultSessionClosed) {
+ this->DestroySession(session);
R_SUCCEED();
}
- ASSERT(R_SUCCEEDED(rc));
- RequestState request{
- .session = session,
- .context = std::move(context),
- .manager = std::move(manager),
- };
+ R_ASSERT(res);
// Complete the sync request with deferral handling.
- R_RETURN(this->CompleteSyncRequest(std::move(request)));
+ R_RETURN(this->CompleteSyncRequest(session));
}
-Result ServerManager::CompleteSyncRequest(RequestState&& request) {
- Result rc{ResultSuccess};
- Result service_rc{ResultSuccess};
+Result ServerManager::CompleteSyncRequest(Session* session) {
+ Result res = ResultSuccess;
+ Result service_res = ResultSuccess;
// Mark the request as not deferred.
- request.context->SetIsDeferred(false);
+ session->GetContext()->SetIsDeferred(false);
// Complete the request. We have exclusive access to this session.
- service_rc = request.manager->CompleteSyncRequest(request.session, *request.context);
+ auto* server_session = static_cast<Kernel::KServerSession*>(session->GetNativeHandle());
+ service_res =
+ session->GetManager()->CompleteSyncRequest(server_session, *session->GetContext());
// If we've been deferred, we're done.
- if (request.context->GetIsDeferred()) {
- // Insert into deferral list.
- std::scoped_lock ll{m_list_mutex};
- m_deferrals.emplace_back(std::move(request));
+ if (session->GetContext()->GetIsDeferred()) {
+ // Insert into deferred session list.
+ std::scoped_lock ll{m_deferred_list_mutex};
+ m_deferred_sessions.push_back(session);
// Finish.
R_SUCCEED();
}
// Send the reply.
- rc = request.session->SendReplyHLE();
+ res = server_session->SendReplyHLE();
// If the session has been closed, we're done.
- if (rc == Kernel::ResultSessionClosed || service_rc == IPC::ResultSessionClosed) {
- // Close the session.
- request.session->Close();
-
- // Finish.
+ if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) {
+ this->DestroySession(session);
R_SUCCEED();
}
- ASSERT(R_SUCCEEDED(rc));
- ASSERT(R_SUCCEEDED(service_rc));
-
- // Reinsert the session.
- {
- std::scoped_lock ll{m_list_mutex};
- m_sessions.emplace(request.session, std::move(request.manager));
- }
+ R_ASSERT(res);
+ R_ASSERT(service_res);
- // Signal the wakeup event.
- m_event->Signal();
+ // We succeeded, so we can process future messages on this session.
+ this->LinkToDeferredList(session);
- // We succeeded.
R_SUCCEED();
}
-Result ServerManager::OnDeferralEvent(std::list<RequestState>&& deferrals) {
- ON_RESULT_FAILURE {
- std::scoped_lock ll{m_list_mutex};
- m_deferrals.splice(m_deferrals.end(), deferrals);
- };
+Result ServerManager::OnDeferralEvent() {
+ // Clear event before grabbing the list.
+ m_deferral_event->Clear();
- while (!deferrals.empty()) {
- RequestState request = deferrals.front();
- deferrals.pop_front();
+ // Get and clear list.
+ const auto deferrals = [&] {
+ std::scoped_lock lk{m_deferred_list_mutex};
+ return std::move(m_deferred_sessions);
+ }();
- // Try again to complete the request.
- R_TRY(this->CompleteSyncRequest(std::move(request)));
+ // Relink deferral event.
+ this->LinkToDeferredList(std::addressof(*m_deferral_holder));
+
+ // For each session, try again to complete the request.
+ for (auto* session : deferrals) {
+ R_ASSERT(this->CompleteSyncRequest(session));
}
R_SUCCEED();
}
+void ServerManager::DestroySession(Session* session) {
+ // Unlink.
+ {
+ std::scoped_lock lk{m_deferred_list_mutex};
+ m_sessions.erase(m_sessions.iterator_to(*session));
+ }
+
+ // Free the session.
+ delete session;
+}
+
} // namespace Service
diff --git a/src/core/hle/service/server_manager.h b/src/core/hle/service/server_manager.h
index c4bc07262..5173ce46e 100644
--- a/src/core/hle/service/server_manager.h
+++ b/src/core/hle/service/server_manager.h
@@ -3,18 +3,17 @@
#pragma once
-#include <functional>
#include <list>
-#include <map>
#include <mutex>
-#include <string_view>
+#include <optional>
#include <vector>
#include "common/polyfill_thread.h"
#include "common/thread.h"
#include "core/hle/result.h"
#include "core/hle/service/hle_ipc.h"
-#include "core/hle/service/mutex.h"
+#include "core/hle/service/os/multi_wait.h"
+#include "core/hle/service/os/mutex.h"
namespace Core {
class System;
@@ -24,11 +23,13 @@ namespace Kernel {
class KEvent;
class KServerPort;
class KServerSession;
-class KSynchronizationObject;
} // namespace Kernel
namespace Service {
+class Port;
+class Session;
+
class ServerManager {
public:
explicit ServerManager(Core::System& system);
@@ -52,34 +53,40 @@ public:
static void RunServer(std::unique_ptr<ServerManager>&& server);
private:
- struct RequestState;
-
+ void LinkToDeferredList(MultiWaitHolder* holder);
+ void LinkDeferred();
+ MultiWaitHolder* WaitSignaled();
+ Result Process(MultiWaitHolder* holder);
+ bool WaitAndProcessImpl();
Result LoopProcessImpl();
- Result WaitAndProcessImpl();
- Result OnPortEvent(Kernel::KServerPort* port, SessionRequestHandlerFactory&& handler_factory);
- Result OnSessionEvent(Kernel::KServerSession* session,
- std::shared_ptr<SessionRequestManager>&& manager);
- Result OnDeferralEvent(std::list<RequestState>&& deferrals);
- Result CompleteSyncRequest(RequestState&& state);
+
+ Result OnPortEvent(Port* port);
+ Result OnSessionEvent(Session* session);
+ Result OnDeferralEvent();
+ Result CompleteSyncRequest(Session* session);
+
+private:
+ void DestroySession(Session* session);
private:
Core::System& m_system;
- Mutex m_serve_mutex;
- std::mutex m_list_mutex;
+ Mutex m_selection_mutex;
- // Guest state tracking
- std::map<Kernel::KServerPort*, SessionRequestHandlerFactory> m_ports{};
- std::map<Kernel::KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions{};
- Kernel::KEvent* m_event{};
+ // Events
+ Kernel::KEvent* m_wakeup_event{};
Kernel::KEvent* m_deferral_event{};
- // Deferral tracking
- struct RequestState {
- Kernel::KServerSession* session;
- std::shared_ptr<HLERequestContext> context;
- std::shared_ptr<SessionRequestManager> manager;
- };
- std::list<RequestState> m_deferrals{};
+ // Deferred wait list
+ std::mutex m_deferred_list_mutex{};
+ MultiWait m_deferred_list{};
+
+ // Guest state tracking
+ MultiWait m_multi_wait{};
+ Common::IntrusiveListBaseTraits<Port>::ListType m_servers{};
+ Common::IntrusiveListBaseTraits<Session>::ListType m_sessions{};
+ std::list<Session*> m_deferred_sessions{};
+ std::optional<MultiWaitHolder> m_wakeup_holder{};
+ std::optional<MultiWaitHolder> m_deferral_holder{};
// Host state tracking
Common::Event m_stopped{};
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 06cbad268..f68c3c686 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -15,7 +15,7 @@
#include "core/hle/service/aoc/aoc_u.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/audio/audio.h"
-#include "core/hle/service/bcat/bcat_module.h"
+#include "core/hle/service/bcat/bcat.h"
#include "core/hle/service/bpc/bpc.h"
#include "core/hle/service/btdrv/btdrv.h"
#include "core/hle/service/btm/btm.h"
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 470521f44..b84b57d92 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -11,6 +11,7 @@
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_process_page_table.h"
+#include "core/hle/kernel/svc_types.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/sm/sm.h"
#include "core/memory.h"
@@ -87,6 +88,20 @@ u64 StandardVmCallbacks::HidKeysDown() {
return static_cast<u64>(press_state & HID::NpadButton::All);
}
+void StandardVmCallbacks::PauseProcess() {
+ if (system.ApplicationProcess()->IsSuspended()) {
+ return;
+ }
+ system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Paused);
+}
+
+void StandardVmCallbacks::ResumeProcess() {
+ if (!system.ApplicationProcess()->IsSuspended()) {
+ return;
+ }
+ system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Runnable);
+}
+
void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value);
}
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
index 619cabaa2..f52f2be7c 100644
--- a/src/core/memory/cheat_engine.h
+++ b/src/core/memory/cheat_engine.h
@@ -30,6 +30,8 @@ public:
void MemoryReadUnsafe(VAddr address, void* data, u64 size) override;
void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override;
u64 HidKeysDown() override;
+ void PauseProcess() override;
+ void ResumeProcess() override;
void DebugLog(u8 id, u64 value) override;
void CommandLog(std::string_view data) override;
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp
index 8bc81e72d..f7097d01d 100644
--- a/src/core/memory/dmnt_cheat_vm.cpp
+++ b/src/core/memory/dmnt_cheat_vm.cpp
@@ -1205,9 +1205,9 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
}
} else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) {
- // TODO: Pause cheat process
+ callbacks->PauseProcess();
} else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) {
- // TODO: Resume cheat process
+ callbacks->ResumeProcess();
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
// Read value from memory.
u64 log_value = 0;
diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h
index fed6a24ad..1c1ed1259 100644
--- a/src/core/memory/dmnt_cheat_vm.h
+++ b/src/core/memory/dmnt_cheat_vm.h
@@ -271,6 +271,9 @@ public:
virtual u64 HidKeysDown() = 0;
+ virtual void PauseProcess() = 0;
+ virtual void ResumeProcess() = 0;
+
virtual void DebugLog(u8 id, u64 value) = 0;
virtual void CommandLog(std::string_view data) = 0;
};