summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/CMakeLists.txt17
-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.cpp30
-rw-r--r--src/core/hle/service/bcat/bcat.h9
-rw-r--r--src/core/hle/service/bcat/bcat_interface.cpp61
-rw-r--r--src/core/hle/service/bcat/bcat_interface.h40
-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.h20
-rw-r--r--src/core/hle/service/bcat/bcat_service.cpp131
-rw-r--r--src/core/hle/service/bcat/bcat_service.h45
-rw-r--r--src/core/hle/service/bcat/bcat_types.h60
-rw-r--r--src/core/hle/service/bcat/bcat_util.h39
-rw-r--r--src/core/hle/service/bcat/delivery_cache_directory_service.cpp81
-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.cpp81
-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
22 files changed, 850 insertions, 735 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index eb8f643a2..703e6050f 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -514,8 +514,21 @@ add_library(core STATIC
514 hle/service/bcat/backend/backend.h 514 hle/service/bcat/backend/backend.h
515 hle/service/bcat/bcat.cpp 515 hle/service/bcat/bcat.cpp
516 hle/service/bcat/bcat.h 516 hle/service/bcat/bcat.h
517 hle/service/bcat/bcat_module.cpp 517 hle/service/bcat/bcat_interface.cpp
518 hle/service/bcat/bcat_module.h 518 hle/service/bcat/bcat_interface.h
519 hle/service/bcat/bcat_result.h
520 hle/service/bcat/bcat_service.cpp
521 hle/service/bcat/bcat_service.h
522 hle/service/bcat/bcat_types.h
523 hle/service/bcat/bcat_util.h
524 hle/service/bcat/delivery_cache_directory_service.cpp
525 hle/service/bcat/delivery_cache_directory_service.h
526 hle/service/bcat/delivery_cache_file_service.cpp
527 hle/service/bcat/delivery_cache_file_service.h
528 hle/service/bcat/delivery_cache_progress_service.cpp
529 hle/service/bcat/delivery_cache_progress_service.h
530 hle/service/bcat/delivery_cache_storage_service.cpp
531 hle/service/bcat/delivery_cache_storage_service.h
519 hle/service/bpc/bpc.cpp 532 hle/service/bpc/bpc.cpp
520 hle/service/bpc/bpc.h 533 hle/service/bpc/bpc.h
521 hle/service/btdrv/btdrv.cpp 534 hle/service/btdrv/btdrv.cpp
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) {
33} 33}
34 34
35void ProgressServiceBackend::StartConnecting() { 35void ProgressServiceBackend::StartConnecting() {
36 impl.status = DeliveryCacheProgressImpl::Status::Connecting; 36 impl.status = DeliveryCacheProgressStatus::Connecting;
37 SignalUpdate(); 37 SignalUpdate();
38} 38}
39 39
40void ProgressServiceBackend::StartProcessingDataList() { 40void ProgressServiceBackend::StartProcessingDataList() {
41 impl.status = DeliveryCacheProgressImpl::Status::ProcessingDataList; 41 impl.status = DeliveryCacheProgressStatus::ProcessingDataList;
42 SignalUpdate(); 42 SignalUpdate();
43} 43}
44 44
45void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name, 45void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name,
46 std::string_view file_name, u64 file_size) { 46 std::string_view file_name, u64 file_size) {
47 impl.status = DeliveryCacheProgressImpl::Status::Downloading; 47 impl.status = DeliveryCacheProgressStatus::Downloading;
48 impl.current_downloaded_bytes = 0; 48 impl.current_downloaded_bytes = 0;
49 impl.current_total_bytes = file_size; 49 impl.current_total_bytes = file_size;
50 std::memcpy(impl.current_directory.data(), dir_name.data(), 50 std::memcpy(impl.current_directory.data(), dir_name.data(),
@@ -65,7 +65,7 @@ void ProgressServiceBackend::FinishDownloadingFile() {
65} 65}
66 66
67void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) { 67void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) {
68 impl.status = DeliveryCacheProgressImpl::Status::Committing; 68 impl.status = DeliveryCacheProgressStatus::Committing;
69 impl.current_file.fill(0); 69 impl.current_file.fill(0);
70 impl.current_downloaded_bytes = 0; 70 impl.current_downloaded_bytes = 0;
71 impl.current_total_bytes = 0; 71 impl.current_total_bytes = 0;
@@ -76,7 +76,7 @@ void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) {
76 76
77void ProgressServiceBackend::FinishDownload(Result result) { 77void ProgressServiceBackend::FinishDownload(Result result) {
78 impl.total_downloaded_bytes = impl.total_bytes; 78 impl.total_downloaded_bytes = impl.total_bytes;
79 impl.status = DeliveryCacheProgressImpl::Status::Done; 79 impl.status = DeliveryCacheProgressStatus::Done;
80 impl.result = result; 80 impl.result = result;
81 SignalUpdate(); 81 SignalUpdate();
82} 82}
@@ -85,15 +85,15 @@ void ProgressServiceBackend::SignalUpdate() {
85 update_event->Signal(); 85 update_event->Signal();
86} 86}
87 87
88Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {} 88BcatBackend::BcatBackend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
89 89
90Backend::~Backend() = default; 90BcatBackend::~BcatBackend() = default;
91 91
92NullBackend::NullBackend(DirectoryGetter getter) : Backend(std::move(getter)) {} 92NullBcatBackend::NullBcatBackend(DirectoryGetter getter) : BcatBackend(std::move(getter)) {}
93 93
94NullBackend::~NullBackend() = default; 94NullBcatBackend::~NullBcatBackend() = default;
95 95
96bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) { 96bool NullBcatBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
97 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, 97 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
98 title.build_id); 98 title.build_id);
99 99
@@ -101,8 +101,8 @@ bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& prog
101 return true; 101 return true;
102} 102}
103 103
104bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name, 104bool NullBcatBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
105 ProgressServiceBackend& progress) { 105 ProgressServiceBackend& progress) {
106 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id, 106 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id,
107 title.build_id, name); 107 title.build_id, name);
108 108
@@ -110,18 +110,18 @@ bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
110 return true; 110 return true;
111} 111}
112 112
113bool NullBackend::Clear(u64 title_id) { 113bool NullBcatBackend::Clear(u64 title_id) {
114 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); 114 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
115 115
116 return true; 116 return true;
117} 117}
118 118
119void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) { 119void NullBcatBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
120 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, 120 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
121 Common::HexToString(passphrase)); 121 Common::HexToString(passphrase));
122} 122}
123 123
124std::optional<std::vector<u8>> NullBackend::GetLaunchParameter(TitleIDVersion title) { 124std::optional<std::vector<u8>> NullBcatBackend::GetLaunchParameter(TitleIDVersion title) {
125 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, 125 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
126 title.build_id); 126 title.build_id);
127 return std::nullopt; 127 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 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/file_sys/vfs/vfs_types.h" 11#include "core/file_sys/vfs/vfs_types.h"
12#include "core/hle/result.h" 12#include "core/hle/result.h"
13#include "core/hle/service/bcat/bcat_types.h"
13#include "core/hle/service/kernel_helpers.h" 14#include "core/hle/service/kernel_helpers.h"
14 15
15namespace Core { 16namespace Core {
@@ -24,44 +25,6 @@ class KReadableEvent;
24 25
25namespace Service::BCAT { 26namespace Service::BCAT {
26 27
27struct DeliveryCacheProgressImpl;
28
29using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>;
30using Passphrase = std::array<u8, 0x20>;
31
32struct TitleIDVersion {
33 u64 title_id;
34 u64 build_id;
35};
36
37using DirectoryName = std::array<char, 0x20>;
38using FileName = std::array<char, 0x20>;
39
40struct DeliveryCacheProgressImpl {
41 enum class Status : s32 {
42 None = 0x0,
43 Queued = 0x1,
44 Connecting = 0x2,
45 ProcessingDataList = 0x3,
46 Downloading = 0x4,
47 Committing = 0x5,
48 Done = 0x9,
49 };
50
51 Status status;
52 Result result = ResultSuccess;
53 DirectoryName current_directory;
54 FileName current_file;
55 s64 current_downloaded_bytes; ///< Bytes downloaded on current file.
56 s64 current_total_bytes; ///< Bytes total on current file.
57 s64 total_downloaded_bytes; ///< Bytes downloaded on overall download.
58 s64 total_bytes; ///< Bytes total on overall download.
59 INSERT_PADDING_BYTES(
60 0x198); ///< Appears to be unused in official code, possibly reserved for future use.
61};
62static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200,
63 "DeliveryCacheProgressImpl has incorrect size.");
64
65// A class to manage the signalling to the game about BCAT download progress. 28// A class to manage the signalling to the game about BCAT download progress.
66// Some of this class is implemented in module.cpp to avoid exposing the implementation structure. 29// Some of this class is implemented in module.cpp to avoid exposing the implementation structure.
67class ProgressServiceBackend { 30class ProgressServiceBackend {
@@ -107,10 +70,10 @@ private:
107}; 70};
108 71
109// A class representing an abstract backend for BCAT functionality. 72// A class representing an abstract backend for BCAT functionality.
110class Backend { 73class BcatBackend {
111public: 74public:
112 explicit Backend(DirectoryGetter getter); 75 explicit BcatBackend(DirectoryGetter getter);
113 virtual ~Backend(); 76 virtual ~BcatBackend();
114 77
115 // Called when the backend is needed to synchronize the data for the game with title ID and 78 // Called when the backend is needed to synchronize the data for the game with title ID and
116 // version in title. A ProgressServiceBackend object is provided to alert the application of 79 // version in title. A ProgressServiceBackend object is provided to alert the application of
@@ -135,10 +98,10 @@ protected:
135}; 98};
136 99
137// A backend of BCAT that provides no operation. 100// A backend of BCAT that provides no operation.
138class NullBackend : public Backend { 101class NullBcatBackend : public BcatBackend {
139public: 102public:
140 explicit NullBackend(DirectoryGetter getter); 103 explicit NullBcatBackend(DirectoryGetter getter);
141 ~NullBackend() override; 104 ~NullBcatBackend() override;
142 105
143 bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override; 106 bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
144 bool SynchronizeDirectory(TitleIDVersion title, std::string name, 107 bool SynchronizeDirectory(TitleIDVersion title, std::string name,
@@ -151,6 +114,7 @@ public:
151 std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override; 114 std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
152}; 115};
153 116
154std::unique_ptr<Backend> CreateBackendFromSettings(Core::System& system, DirectoryGetter getter); 117std::unique_ptr<BcatBackend> CreateBackendFromSettings(Core::System& system,
118 DirectoryGetter getter);
155 119
156} // namespace Service::BCAT 120} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp
index d0ac17324..31e9d8662 100644
--- a/src/core/hle/service/bcat/bcat.cpp
+++ b/src/core/hle/service/bcat/bcat.cpp
@@ -1,24 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/bcat/backend/backend.h"
4#include "core/hle/service/bcat/bcat.h" 5#include "core/hle/service/bcat/bcat.h"
6#include "core/hle/service/bcat/bcat_interface.h"
7#include "core/hle/service/server_manager.h"
5 8
6namespace Service::BCAT { 9namespace Service::BCAT {
7 10
8BCAT::BCAT(Core::System& system_, std::shared_ptr<Module> module_, 11void LoopProcess(Core::System& system) {
9 FileSystem::FileSystemController& fsc_, const char* name_) 12 auto server_manager = std::make_unique<ServerManager>(system);
10 : Interface(system_, std::move(module_), fsc_, name_) { 13
11 // clang-format off 14 server_manager->RegisterNamedService("bcat:a",
12 static const FunctionInfo functions[] = { 15 std::make_shared<BcatInterface>(system, "bcat:a"));
13 {0, &BCAT::CreateBcatService, "CreateBcatService"}, 16 server_manager->RegisterNamedService("bcat:m",
14 {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"}, 17 std::make_shared<BcatInterface>(system, "bcat:m"));
15 {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"}, 18 server_manager->RegisterNamedService("bcat:u",
16 {3, nullptr, "CreateDeliveryCacheProgressService"}, 19 std::make_shared<BcatInterface>(system, "bcat:u"));
17 {4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"}, 20 server_manager->RegisterNamedService("bcat:s",
18 }; 21 std::make_shared<BcatInterface>(system, "bcat:s"));
19 // clang-format on 22
20 RegisterHandlers(functions); 23 ServerManager::RunServer(std::move(server_manager));
21} 24}
22 25
23BCAT::~BCAT() = default;
24} // namespace Service::BCAT 26} // 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 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/bcat/bcat_module.h" 6#include "core/hle/service/service.h"
7 7
8namespace Core { 8namespace Core {
9class System; 9class System;
@@ -11,11 +11,6 @@ class System;
11 11
12namespace Service::BCAT { 12namespace Service::BCAT {
13 13
14class BCAT final : public Module::Interface { 14void LoopProcess(Core::System& system);
15public:
16 explicit BCAT(Core::System& system_, std::shared_ptr<Module> module_,
17 FileSystem::FileSystemController& fsc_, const char* name_);
18 ~BCAT() override;
19};
20 15
21} // namespace Service::BCAT 16} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat_interface.cpp b/src/core/hle/service/bcat/bcat_interface.cpp
new file mode 100644
index 000000000..2d9a0efcc
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_interface.cpp
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/bcat/bcat_interface.h"
5#include "core/hle/service/bcat/bcat_service.h"
6#include "core/hle/service/bcat/delivery_cache_storage_service.h"
7#include "core/hle/service/cmif_serialization.h"
8#include "core/hle/service/filesystem/filesystem.h"
9
10namespace Service::BCAT {
11
12std::unique_ptr<BcatBackend> CreateBackendFromSettings([[maybe_unused]] Core::System& system,
13 DirectoryGetter getter) {
14 return std::make_unique<NullBcatBackend>(std::move(getter));
15}
16
17BcatInterface::BcatInterface(Core::System& system_, const char* name_)
18 : ServiceFramework{system_, name_}, fsc{system.GetFileSystemController()} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, C<&BcatInterface::CreateBcatService>, "CreateBcatService"},
22 {1, C<&BcatInterface::CreateDeliveryCacheStorageService>, "CreateDeliveryCacheStorageService"},
23 {2, C<&BcatInterface::CreateDeliveryCacheStorageServiceWithApplicationId>, "CreateDeliveryCacheStorageServiceWithApplicationId"},
24 {3, nullptr, "CreateDeliveryCacheProgressService"},
25 {4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"},
26 };
27 // clang-format on
28
29 RegisterHandlers(functions);
30
31 backend =
32 CreateBackendFromSettings(system_, [this](u64 tid) { return fsc.GetBCATDirectory(tid); });
33}
34
35BcatInterface::~BcatInterface() = default;
36
37Result BcatInterface::CreateBcatService(OutInterface<IBcatService> out_interface) {
38 LOG_INFO(Service_BCAT, "called");
39 *out_interface = std::make_shared<IBcatService>(system, *backend);
40 R_SUCCEED();
41}
42
43Result BcatInterface::CreateDeliveryCacheStorageService(
44 OutInterface<IDeliveryCacheStorageService> out_interface) {
45 LOG_INFO(Service_BCAT, "called");
46
47 const auto title_id = system.GetApplicationProcessProgramID();
48 *out_interface =
49 std::make_shared<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
50 R_SUCCEED();
51}
52
53Result BcatInterface::CreateDeliveryCacheStorageServiceWithApplicationId(
54 u64 title_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
55 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
56 *out_interface =
57 std::make_shared<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
58 R_SUCCEED();
59}
60
61} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat_interface.h b/src/core/hle/service/bcat/bcat_interface.h
new file mode 100644
index 000000000..9282b57bb
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_interface.h
@@ -0,0 +1,40 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::FileSystem {
14class FileSystemController;
15}
16
17namespace Service::BCAT {
18class BcatBackend;
19class IBcatService;
20class IDeliveryCacheStorageService;
21
22class BcatInterface final : public ServiceFramework<BcatInterface> {
23public:
24 explicit BcatInterface(Core::System& system_, const char* name_);
25 ~BcatInterface() override;
26
27private:
28 Result CreateBcatService(OutInterface<IBcatService> out_interface);
29
30 Result CreateDeliveryCacheStorageService(
31 OutInterface<IDeliveryCacheStorageService> out_interface);
32
33 Result CreateDeliveryCacheStorageServiceWithApplicationId(
34 u64 title_id, OutInterface<IDeliveryCacheStorageService> out_interface);
35
36 std::unique_ptr<BcatBackend> backend;
37 Service::FileSystem::FileSystemController& fsc;
38};
39
40} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cctype>
5#include <mbedtls/md5.h>
6#include "common/hex_util.h"
7#include "common/logging/log.h"
8#include "common/settings.h"
9#include "common/string_util.h"
10#include "core/core.h"
11#include "core/file_sys/vfs/vfs.h"
12#include "core/hle/kernel/k_readable_event.h"
13#include "core/hle/service/bcat/backend/backend.h"
14#include "core/hle/service/bcat/bcat.h"
15#include "core/hle/service/bcat/bcat_module.h"
16#include "core/hle/service/filesystem/filesystem.h"
17#include "core/hle/service/ipc_helpers.h"
18#include "core/hle/service/server_manager.h"
19
20namespace Service::BCAT {
21
22constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1};
23constexpr Result ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2};
24constexpr Result ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6};
25constexpr Result ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7};
26
27// The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files
28// and if any of them have a non-zero result it just forwards that result. This is the FS error code
29// for permission denied, which is the closest approximation of this scenario.
30constexpr Result ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400};
31
32using BCATDigest = std::array<u8, 0x10>;
33
34namespace {
35
36u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
37 u64 out{};
38 std::memcpy(&out, id.data(), sizeof(u64));
39 return out;
40}
41
42// The digest is only used to determine if a file is unique compared to others of the same name.
43// Since the algorithm isn't ever checked in game, MD5 is safe.
44BCATDigest DigestFile(const FileSys::VirtualFile& file) {
45 BCATDigest out{};
46 const auto bytes = file->ReadAllBytes();
47 mbedtls_md5_ret(bytes.data(), bytes.size(), out.data());
48 return out;
49}
50
51// For a name to be valid it must be non-empty, must have a null terminating character as the final
52// char, can only contain numbers, letters, underscores and a hyphen if directory and a period if
53// file.
54bool VerifyNameValidInternal(HLERequestContext& ctx, std::array<char, 0x20> name, char match_char) {
55 const auto null_chars = std::count(name.begin(), name.end(), 0);
56 const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) {
57 return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0';
58 });
59 if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') {
60 LOG_ERROR(Service_BCAT, "Name passed was invalid!");
61 IPC::ResponseBuilder rb{ctx, 2};
62 rb.Push(ERROR_INVALID_ARGUMENT);
63 return false;
64 }
65
66 return true;
67}
68
69bool VerifyNameValidDir(HLERequestContext& ctx, DirectoryName name) {
70 return VerifyNameValidInternal(ctx, name, '-');
71}
72
73bool VerifyNameValidFile(HLERequestContext& ctx, FileName name) {
74 return VerifyNameValidInternal(ctx, name, '.');
75}
76
77} // Anonymous namespace
78
79struct DeliveryCacheDirectoryEntry {
80 FileName name;
81 u64 size;
82 BCATDigest digest;
83};
84
85class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> {
86public:
87 explicit IDeliveryCacheProgressService(Core::System& system_, Kernel::KReadableEvent& event_,
88 const DeliveryCacheProgressImpl& impl_)
89 : ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{event_}, impl{impl_} {
90 // clang-format off
91 static const FunctionInfo functions[] = {
92 {0, &IDeliveryCacheProgressService::GetEvent, "GetEvent"},
93 {1, &IDeliveryCacheProgressService::GetImpl, "GetImpl"},
94 };
95 // clang-format on
96
97 RegisterHandlers(functions);
98 }
99
100private:
101 void GetEvent(HLERequestContext& ctx) {
102 LOG_DEBUG(Service_BCAT, "called");
103
104 IPC::ResponseBuilder rb{ctx, 2, 1};
105 rb.Push(ResultSuccess);
106 rb.PushCopyObjects(event);
107 }
108
109 void GetImpl(HLERequestContext& ctx) {
110 LOG_DEBUG(Service_BCAT, "called");
111
112 ctx.WriteBuffer(impl);
113
114 IPC::ResponseBuilder rb{ctx, 2};
115 rb.Push(ResultSuccess);
116 }
117
118 Kernel::KReadableEvent& event;
119 const DeliveryCacheProgressImpl& impl;
120};
121
122class IBcatService final : public ServiceFramework<IBcatService> {
123public:
124 explicit IBcatService(Core::System& system_, Backend& backend_)
125 : ServiceFramework{system_, "IBcatService"}, backend{backend_},
126 progress{{
127 ProgressServiceBackend{system_, "Normal"},
128 ProgressServiceBackend{system_, "Directory"},
129 }} {
130 // clang-format off
131 static const FunctionInfo functions[] = {
132 {10100, &IBcatService::RequestSyncDeliveryCache, "RequestSyncDeliveryCache"},
133 {10101, &IBcatService::RequestSyncDeliveryCacheWithDirectoryName, "RequestSyncDeliveryCacheWithDirectoryName"},
134 {10200, nullptr, "CancelSyncDeliveryCacheRequest"},
135 {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"},
136 {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"},
137 {20300, nullptr, "GetDeliveryCacheStorageUpdateNotifier"},
138 {20301, nullptr, "RequestSuspendDeliveryTask"},
139 {20400, nullptr, "RegisterSystemApplicationDeliveryTask"},
140 {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
141 {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
142 {30100, &IBcatService::SetPassphrase, "SetPassphrase"},
143 {30101, nullptr, "Unknown30101"},
144 {30102, nullptr, "Unknown30102"},
145 {30200, nullptr, "RegisterBackgroundDeliveryTask"},
146 {30201, nullptr, "UnregisterBackgroundDeliveryTask"},
147 {30202, nullptr, "BlockDeliveryTask"},
148 {30203, nullptr, "UnblockDeliveryTask"},
149 {30210, nullptr, "SetDeliveryTaskTimer"},
150 {30300, nullptr, "RegisterSystemApplicationDeliveryTasks"},
151 {90100, nullptr, "EnumerateBackgroundDeliveryTask"},
152 {90101, nullptr, "Unknown90101"},
153 {90200, nullptr, "GetDeliveryList"},
154 {90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"},
155 {90202, nullptr, "ClearDeliveryTaskSubscriptionStatus"},
156 {90300, nullptr, "GetPushNotificationLog"},
157 {90301, nullptr, "Unknown90301"},
158 };
159 // clang-format on
160 RegisterHandlers(functions);
161 }
162
163private:
164 enum class SyncType {
165 Normal,
166 Directory,
167 Count,
168 };
169
170 std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) {
171 auto& progress_backend{GetProgressBackend(type)};
172 return std::make_shared<IDeliveryCacheProgressService>(system, progress_backend.GetEvent(),
173 progress_backend.GetImpl());
174 }
175
176 void RequestSyncDeliveryCache(HLERequestContext& ctx) {
177 LOG_DEBUG(Service_BCAT, "called");
178
179 backend.Synchronize({system.GetApplicationProcessProgramID(),
180 GetCurrentBuildID(system.GetApplicationProcessBuildID())},
181 GetProgressBackend(SyncType::Normal));
182
183 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
184 rb.Push(ResultSuccess);
185 rb.PushIpcInterface(CreateProgressService(SyncType::Normal));
186 }
187
188 void RequestSyncDeliveryCacheWithDirectoryName(HLERequestContext& ctx) {
189 IPC::RequestParser rp{ctx};
190 const auto name_raw = rp.PopRaw<DirectoryName>();
191 const auto name =
192 Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
193
194 LOG_DEBUG(Service_BCAT, "called, name={}", name);
195
196 backend.SynchronizeDirectory({system.GetApplicationProcessProgramID(),
197 GetCurrentBuildID(system.GetApplicationProcessBuildID())},
198 name, GetProgressBackend(SyncType::Directory));
199
200 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
201 rb.Push(ResultSuccess);
202 rb.PushIpcInterface(CreateProgressService(SyncType::Directory));
203 }
204
205 void SetPassphrase(HLERequestContext& ctx) {
206 IPC::RequestParser rp{ctx};
207 const auto title_id = rp.PopRaw<u64>();
208
209 const auto passphrase_raw = ctx.ReadBuffer();
210
211 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
212 Common::HexToString(passphrase_raw));
213
214 if (title_id == 0) {
215 LOG_ERROR(Service_BCAT, "Invalid title ID!");
216 IPC::ResponseBuilder rb{ctx, 2};
217 rb.Push(ERROR_INVALID_ARGUMENT);
218 }
219
220 if (passphrase_raw.size() > 0x40) {
221 LOG_ERROR(Service_BCAT, "Passphrase too large!");
222 IPC::ResponseBuilder rb{ctx, 2};
223 rb.Push(ERROR_INVALID_ARGUMENT);
224 return;
225 }
226
227 Passphrase passphrase{};
228 std::memcpy(passphrase.data(), passphrase_raw.data(),
229 std::min(passphrase.size(), passphrase_raw.size()));
230
231 backend.SetPassphrase(title_id, passphrase);
232
233 IPC::ResponseBuilder rb{ctx, 2};
234 rb.Push(ResultSuccess);
235 }
236
237 void ClearDeliveryCacheStorage(HLERequestContext& ctx) {
238 IPC::RequestParser rp{ctx};
239 const auto title_id = rp.PopRaw<u64>();
240
241 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
242
243 if (title_id == 0) {
244 LOG_ERROR(Service_BCAT, "Invalid title ID!");
245 IPC::ResponseBuilder rb{ctx, 2};
246 rb.Push(ERROR_INVALID_ARGUMENT);
247 return;
248 }
249
250 if (!backend.Clear(title_id)) {
251 LOG_ERROR(Service_BCAT, "Could not clear the directory successfully!");
252 IPC::ResponseBuilder rb{ctx, 2};
253 rb.Push(ERROR_FAILED_CLEAR_CACHE);
254 return;
255 }
256
257 IPC::ResponseBuilder rb{ctx, 2};
258 rb.Push(ResultSuccess);
259 }
260
261 ProgressServiceBackend& GetProgressBackend(SyncType type) {
262 return progress.at(static_cast<size_t>(type));
263 }
264
265 const ProgressServiceBackend& GetProgressBackend(SyncType type) const {
266 return progress.at(static_cast<size_t>(type));
267 }
268
269 Backend& backend;
270 std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
271};
272
273void Module::Interface::CreateBcatService(HLERequestContext& ctx) {
274 LOG_DEBUG(Service_BCAT, "called");
275
276 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
277 rb.Push(ResultSuccess);
278 rb.PushIpcInterface<IBcatService>(system, *backend);
279}
280
281class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> {
282public:
283 explicit IDeliveryCacheFileService(Core::System& system_, FileSys::VirtualDir root_)
284 : ServiceFramework{system_, "IDeliveryCacheFileService"}, root(std::move(root_)) {
285 // clang-format off
286 static const FunctionInfo functions[] = {
287 {0, &IDeliveryCacheFileService::Open, "Open"},
288 {1, &IDeliveryCacheFileService::Read, "Read"},
289 {2, &IDeliveryCacheFileService::GetSize, "GetSize"},
290 {3, &IDeliveryCacheFileService::GetDigest, "GetDigest"},
291 };
292 // clang-format on
293
294 RegisterHandlers(functions);
295 }
296
297private:
298 void Open(HLERequestContext& ctx) {
299 IPC::RequestParser rp{ctx};
300 const auto dir_name_raw = rp.PopRaw<DirectoryName>();
301 const auto file_name_raw = rp.PopRaw<FileName>();
302
303 const auto dir_name =
304 Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size());
305 const auto file_name =
306 Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size());
307
308 LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name);
309
310 if (!VerifyNameValidDir(ctx, dir_name_raw) || !VerifyNameValidFile(ctx, file_name_raw))
311 return;
312
313 if (current_file != nullptr) {
314 LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!");
315 IPC::ResponseBuilder rb{ctx, 2};
316 rb.Push(ERROR_ENTITY_ALREADY_OPEN);
317 return;
318 }
319
320 const auto dir = root->GetSubdirectory(dir_name);
321
322 if (dir == nullptr) {
323 LOG_ERROR(Service_BCAT, "The directory of name={} couldn't be opened!", dir_name);
324 IPC::ResponseBuilder rb{ctx, 2};
325 rb.Push(ERROR_FAILED_OPEN_ENTITY);
326 return;
327 }
328
329 current_file = dir->GetFile(file_name);
330
331 if (current_file == nullptr) {
332 LOG_ERROR(Service_BCAT, "The file of name={} couldn't be opened!", file_name);
333 IPC::ResponseBuilder rb{ctx, 2};
334 rb.Push(ERROR_FAILED_OPEN_ENTITY);
335 return;
336 }
337
338 IPC::ResponseBuilder rb{ctx, 2};
339 rb.Push(ResultSuccess);
340 }
341
342 void Read(HLERequestContext& ctx) {
343 IPC::RequestParser rp{ctx};
344 const auto offset{rp.PopRaw<u64>()};
345
346 auto size = ctx.GetWriteBufferSize();
347
348 LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, size);
349
350 if (current_file == nullptr) {
351 LOG_ERROR(Service_BCAT, "There is no file currently open!");
352 IPC::ResponseBuilder rb{ctx, 2};
353 rb.Push(ERROR_NO_OPEN_ENTITY);
354 }
355
356 size = std::min<u64>(current_file->GetSize() - offset, size);
357 const auto buffer = current_file->ReadBytes(size, offset);
358 ctx.WriteBuffer(buffer);
359
360 IPC::ResponseBuilder rb{ctx, 4};
361 rb.Push(ResultSuccess);
362 rb.Push<u64>(buffer.size());
363 }
364
365 void GetSize(HLERequestContext& ctx) {
366 LOG_DEBUG(Service_BCAT, "called");
367
368 if (current_file == nullptr) {
369 LOG_ERROR(Service_BCAT, "There is no file currently open!");
370 IPC::ResponseBuilder rb{ctx, 2};
371 rb.Push(ERROR_NO_OPEN_ENTITY);
372 }
373
374 IPC::ResponseBuilder rb{ctx, 4};
375 rb.Push(ResultSuccess);
376 rb.Push<u64>(current_file->GetSize());
377 }
378
379 void GetDigest(HLERequestContext& ctx) {
380 LOG_DEBUG(Service_BCAT, "called");
381
382 if (current_file == nullptr) {
383 LOG_ERROR(Service_BCAT, "There is no file currently open!");
384 IPC::ResponseBuilder rb{ctx, 2};
385 rb.Push(ERROR_NO_OPEN_ENTITY);
386 }
387
388 IPC::ResponseBuilder rb{ctx, 6};
389 rb.Push(ResultSuccess);
390 rb.PushRaw(DigestFile(current_file));
391 }
392
393 FileSys::VirtualDir root;
394 FileSys::VirtualFile current_file;
395};
396
397class IDeliveryCacheDirectoryService final
398 : public ServiceFramework<IDeliveryCacheDirectoryService> {
399public:
400 explicit IDeliveryCacheDirectoryService(Core::System& system_, FileSys::VirtualDir root_)
401 : ServiceFramework{system_, "IDeliveryCacheDirectoryService"}, root(std::move(root_)) {
402 // clang-format off
403 static const FunctionInfo functions[] = {
404 {0, &IDeliveryCacheDirectoryService::Open, "Open"},
405 {1, &IDeliveryCacheDirectoryService::Read, "Read"},
406 {2, &IDeliveryCacheDirectoryService::GetCount, "GetCount"},
407 };
408 // clang-format on
409
410 RegisterHandlers(functions);
411 }
412
413private:
414 void Open(HLERequestContext& ctx) {
415 IPC::RequestParser rp{ctx};
416 const auto name_raw = rp.PopRaw<DirectoryName>();
417 const auto name =
418 Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
419
420 LOG_DEBUG(Service_BCAT, "called, name={}", name);
421
422 if (!VerifyNameValidDir(ctx, name_raw))
423 return;
424
425 if (current_dir != nullptr) {
426 LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!");
427 IPC::ResponseBuilder rb{ctx, 2};
428 rb.Push(ERROR_ENTITY_ALREADY_OPEN);
429 return;
430 }
431
432 current_dir = root->GetSubdirectory(name);
433
434 if (current_dir == nullptr) {
435 LOG_ERROR(Service_BCAT, "Failed to open the directory name={}!", name);
436 IPC::ResponseBuilder rb{ctx, 2};
437 rb.Push(ERROR_FAILED_OPEN_ENTITY);
438 return;
439 }
440
441 IPC::ResponseBuilder rb{ctx, 2};
442 rb.Push(ResultSuccess);
443 }
444
445 void Read(HLERequestContext& ctx) {
446 auto write_size = ctx.GetWriteBufferNumElements<DeliveryCacheDirectoryEntry>();
447
448 LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size);
449
450 if (current_dir == nullptr) {
451 LOG_ERROR(Service_BCAT, "There is no open directory!");
452 IPC::ResponseBuilder rb{ctx, 2};
453 rb.Push(ERROR_NO_OPEN_ENTITY);
454 return;
455 }
456
457 const auto files = current_dir->GetFiles();
458 write_size = std::min<u64>(write_size, files.size());
459 std::vector<DeliveryCacheDirectoryEntry> entries(write_size);
460 std::transform(
461 files.begin(), files.begin() + write_size, entries.begin(), [](const auto& file) {
462 FileName name{};
463 std::memcpy(name.data(), file->GetName().data(),
464 std::min(file->GetName().size(), name.size()));
465 return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)};
466 });
467
468 ctx.WriteBuffer(entries);
469
470 IPC::ResponseBuilder rb{ctx, 3};
471 rb.Push(ResultSuccess);
472 rb.Push(static_cast<u32>(write_size * sizeof(DeliveryCacheDirectoryEntry)));
473 }
474
475 void GetCount(HLERequestContext& ctx) {
476 LOG_DEBUG(Service_BCAT, "called");
477
478 if (current_dir == nullptr) {
479 LOG_ERROR(Service_BCAT, "There is no open directory!");
480 IPC::ResponseBuilder rb{ctx, 2};
481 rb.Push(ERROR_NO_OPEN_ENTITY);
482 return;
483 }
484
485 const auto files = current_dir->GetFiles();
486
487 IPC::ResponseBuilder rb{ctx, 3};
488 rb.Push(ResultSuccess);
489 rb.Push(static_cast<u32>(files.size()));
490 }
491
492 FileSys::VirtualDir root;
493 FileSys::VirtualDir current_dir;
494};
495
496class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> {
497public:
498 explicit IDeliveryCacheStorageService(Core::System& system_, FileSys::VirtualDir root_)
499 : ServiceFramework{system_, "IDeliveryCacheStorageService"}, root(std::move(root_)) {
500 // clang-format off
501 static const FunctionInfo functions[] = {
502 {0, &IDeliveryCacheStorageService::CreateFileService, "CreateFileService"},
503 {1, &IDeliveryCacheStorageService::CreateDirectoryService, "CreateDirectoryService"},
504 {10, &IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory, "EnumerateDeliveryCacheDirectory"},
505 };
506 // clang-format on
507
508 RegisterHandlers(functions);
509
510 for (const auto& subdir : root->GetSubdirectories()) {
511 DirectoryName name{};
512 std::memcpy(name.data(), subdir->GetName().data(),
513 std::min(sizeof(DirectoryName) - 1, subdir->GetName().size()));
514 entries.push_back(name);
515 }
516 }
517
518private:
519 void CreateFileService(HLERequestContext& ctx) {
520 LOG_DEBUG(Service_BCAT, "called");
521
522 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
523 rb.Push(ResultSuccess);
524 rb.PushIpcInterface<IDeliveryCacheFileService>(system, root);
525 }
526
527 void CreateDirectoryService(HLERequestContext& ctx) {
528 LOG_DEBUG(Service_BCAT, "called");
529
530 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
531 rb.Push(ResultSuccess);
532 rb.PushIpcInterface<IDeliveryCacheDirectoryService>(system, root);
533 }
534
535 void EnumerateDeliveryCacheDirectory(HLERequestContext& ctx) {
536 auto size = ctx.GetWriteBufferNumElements<DirectoryName>();
537
538 LOG_DEBUG(Service_BCAT, "called, size={:016X}", size);
539
540 size = std::min<u64>(size, entries.size() - next_read_index);
541 ctx.WriteBuffer(entries.data() + next_read_index, size * sizeof(DirectoryName));
542 next_read_index += size;
543
544 IPC::ResponseBuilder rb{ctx, 3};
545 rb.Push(ResultSuccess);
546 rb.Push(static_cast<u32>(size));
547 }
548
549 FileSys::VirtualDir root;
550 std::vector<DirectoryName> entries;
551 u64 next_read_index = 0;
552};
553
554void Module::Interface::CreateDeliveryCacheStorageService(HLERequestContext& ctx) {
555 LOG_DEBUG(Service_BCAT, "called");
556
557 const auto title_id = system.GetApplicationProcessProgramID();
558 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
559 rb.Push(ResultSuccess);
560 rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
561}
562
563void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId(HLERequestContext& ctx) {
564 IPC::RequestParser rp{ctx};
565 const auto title_id = rp.PopRaw<u64>();
566
567 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
568
569 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
570 rb.Push(ResultSuccess);
571 rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
572}
573
574std::unique_ptr<Backend> CreateBackendFromSettings([[maybe_unused]] Core::System& system,
575 DirectoryGetter getter) {
576 return std::make_unique<NullBackend>(std::move(getter));
577}
578
579Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
580 FileSystem::FileSystemController& fsc_, const char* name)
581 : ServiceFramework{system_, name}, fsc{fsc_}, module{std::move(module_)},
582 backend{CreateBackendFromSettings(system_,
583 [&fsc_](u64 tid) { return fsc_.GetBCATDirectory(tid); })} {}
584
585Module::Interface::~Interface() = default;
586
587void LoopProcess(Core::System& system) {
588 auto server_manager = std::make_unique<ServerManager>(system);
589 auto module = std::make_shared<Module>();
590
591 server_manager->RegisterNamedService(
592 "bcat:a",
593 std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:a"));
594 server_manager->RegisterNamedService(
595 "bcat:m",
596 std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:m"));
597 server_manager->RegisterNamedService(
598 "bcat:u",
599 std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:u"));
600 server_manager->RegisterNamedService(
601 "bcat:s",
602 std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:s"));
603 ServerManager::RunServer(std::move(server_manager));
604}
605
606} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Core {
9class System;
10}
11
12namespace Service {
13
14namespace FileSystem {
15class FileSystemController;
16} // namespace FileSystem
17
18namespace BCAT {
19
20class Backend;
21
22class Module final {
23public:
24 class Interface : public ServiceFramework<Interface> {
25 public:
26 explicit Interface(Core::System& system_, std::shared_ptr<Module> module_,
27 FileSystem::FileSystemController& fsc_, const char* name);
28 ~Interface() override;
29
30 void CreateBcatService(HLERequestContext& ctx);
31 void CreateDeliveryCacheStorageService(HLERequestContext& ctx);
32 void CreateDeliveryCacheStorageServiceWithApplicationId(HLERequestContext& ctx);
33
34 protected:
35 FileSystem::FileSystemController& fsc;
36
37 std::shared_ptr<Module> module;
38 std::unique_ptr<Backend> backend;
39 };
40};
41
42void LoopProcess(Core::System& system);
43
44} // namespace BCAT
45
46} // 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..d711924b1
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_result.h
@@ -0,0 +1,20 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::BCAT {
9
10constexpr Result ResultInvalidArgument{ErrorModule::BCAT, 1};
11constexpr Result ResultFailedOpenEntity{ErrorModule::BCAT, 2};
12constexpr Result ResultEntityAlreadyOpen{ErrorModule::BCAT, 6};
13constexpr Result ResultNoOpenEntry{ErrorModule::BCAT, 7};
14
15// The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the
16// files and if any of them have a non-zero result it just forwards that result. This is the FS
17// error code for permission denied, which is the closest approximation of this scenario.
18constexpr Result ResultFailedClearCache{ErrorModule::FS, 6400};
19
20} // 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..2eacec3ae
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_service.cpp
@@ -0,0 +1,131 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/hex_util.h"
5#include "common/string_util.h"
6#include "core/core.h"
7#include "core/hle/service/bcat/backend/backend.h"
8#include "core/hle/service/bcat/bcat_result.h"
9#include "core/hle/service/bcat/bcat_service.h"
10#include "core/hle/service/bcat/bcat_util.h"
11#include "core/hle/service/bcat/delivery_cache_progress_service.h"
12#include "core/hle/service/bcat/delivery_cache_storage_service.h"
13#include "core/hle/service/cmif_serialization.h"
14
15namespace Service::BCAT {
16
17u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
18 u64 out{};
19 std::memcpy(&out, id.data(), sizeof(u64));
20 return out;
21}
22
23IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_)
24 : ServiceFramework{system_, "IBcatService"}, backend{backend_},
25 progress{{
26 ProgressServiceBackend{system_, "Normal"},
27 ProgressServiceBackend{system_, "Directory"},
28 }} {
29 // clang-format off
30 static const FunctionInfo functions[] = {
31 {10100, C<&IBcatService::RequestSyncDeliveryCache>, "RequestSyncDeliveryCache"},
32 {10101, C<&IBcatService::RequestSyncDeliveryCacheWithDirectoryName>, "RequestSyncDeliveryCacheWithDirectoryName"},
33 {10200, nullptr, "CancelSyncDeliveryCacheRequest"},
34 {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"},
35 {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"},
36 {20300, nullptr, "GetDeliveryCacheStorageUpdateNotifier"},
37 {20301, nullptr, "RequestSuspendDeliveryTask"},
38 {20400, nullptr, "RegisterSystemApplicationDeliveryTask"},
39 {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
40 {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
41 {30100, C<&IBcatService::SetPassphrase>, "SetPassphrase"},
42 {30101, nullptr, "Unknown30101"},
43 {30102, nullptr, "Unknown30102"},
44 {30200, nullptr, "RegisterBackgroundDeliveryTask"},
45 {30201, nullptr, "UnregisterBackgroundDeliveryTask"},
46 {30202, nullptr, "BlockDeliveryTask"},
47 {30203, nullptr, "UnblockDeliveryTask"},
48 {30210, nullptr, "SetDeliveryTaskTimer"},
49 {30300, C<&IBcatService::RegisterSystemApplicationDeliveryTasks>, "RegisterSystemApplicationDeliveryTasks"},
50 {90100, nullptr, "EnumerateBackgroundDeliveryTask"},
51 {90101, nullptr, "Unknown90101"},
52 {90200, nullptr, "GetDeliveryList"},
53 {90201, C<&IBcatService::ClearDeliveryCacheStorage>, "ClearDeliveryCacheStorage"},
54 {90202, nullptr, "ClearDeliveryTaskSubscriptionStatus"},
55 {90300, nullptr, "GetPushNotificationLog"},
56 {90301, nullptr, "Unknown90301"},
57 };
58 // clang-format on
59 RegisterHandlers(functions);
60}
61
62IBcatService::~IBcatService() = default;
63
64Result IBcatService::RequestSyncDeliveryCache(
65 OutInterface<IDeliveryCacheProgressService> out_interface) {
66 LOG_DEBUG(Service_BCAT, "called");
67
68 auto& progress_backend{GetProgressBackend(SyncType::Normal)};
69 backend.Synchronize({system.GetApplicationProcessProgramID(),
70 GetCurrentBuildID(system.GetApplicationProcessBuildID())},
71 GetProgressBackend(SyncType::Normal));
72
73 *out_interface = std::make_shared<IDeliveryCacheProgressService>(
74 system, progress_backend.GetEvent(), progress_backend.GetImpl());
75 R_SUCCEED();
76}
77
78Result IBcatService::RequestSyncDeliveryCacheWithDirectoryName(
79 DirectoryName name_raw, OutInterface<IDeliveryCacheProgressService> out_interface) {
80 const auto name = Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
81
82 LOG_DEBUG(Service_BCAT, "called, name={}", name);
83
84 auto& progress_backend{GetProgressBackend(SyncType::Directory)};
85 backend.SynchronizeDirectory({system.GetApplicationProcessProgramID(),
86 GetCurrentBuildID(system.GetApplicationProcessBuildID())},
87 name, progress_backend);
88
89 *out_interface = std::make_shared<IDeliveryCacheProgressService>(
90 system, progress_backend.GetEvent(), progress_backend.GetImpl());
91 R_SUCCEED();
92}
93
94Result IBcatService::SetPassphrase(u64 title_id,
95 InBuffer<BufferAttr_HipcPointer> passphrase_buffer) {
96 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
97 Common::HexToString(passphrase_buffer));
98
99 R_UNLESS(title_id != 0, ResultInvalidArgument);
100 R_UNLESS(passphrase_buffer.size() <= 0x40, ResultInvalidArgument);
101
102 Passphrase passphrase{};
103 std::memcpy(passphrase.data(), passphrase_buffer.data(),
104 std::min(passphrase.size(), passphrase_buffer.size()));
105
106 backend.SetPassphrase(title_id, passphrase);
107 R_SUCCEED();
108}
109
110Result IBcatService::RegisterSystemApplicationDeliveryTasks() {
111 LOG_WARNING(Service_BCAT, "(STUBBED) called");
112 R_SUCCEED();
113}
114
115Result IBcatService::ClearDeliveryCacheStorage(u64 title_id) {
116 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
117
118 R_UNLESS(title_id != 0, ResultInvalidArgument);
119 R_UNLESS(backend.Clear(title_id), ResultFailedClearCache);
120 R_SUCCEED();
121}
122
123ProgressServiceBackend& IBcatService::GetProgressBackend(SyncType type) {
124 return progress.at(static_cast<size_t>(type));
125}
126
127const ProgressServiceBackend& IBcatService::GetProgressBackend(SyncType type) const {
128 return progress.at(static_cast<size_t>(type));
129}
130
131} // 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..3e1ed98c3
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_service.h
@@ -0,0 +1,45 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/bcat/backend/backend.h"
7#include "core/hle/service/bcat/bcat_types.h"
8#include "core/hle/service/cmif_types.h"
9#include "core/hle/service/service.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::BCAT {
16class BcatBackend;
17class IDeliveryCacheStorageService;
18class IDeliveryCacheProgressService;
19
20class IBcatService final : public ServiceFramework<IBcatService> {
21public:
22 explicit IBcatService(Core::System& system_, BcatBackend& backend_);
23 ~IBcatService() override;
24
25private:
26 Result RequestSyncDeliveryCache(OutInterface<IDeliveryCacheProgressService> out_interface);
27
28 Result RequestSyncDeliveryCacheWithDirectoryName(
29 DirectoryName name, OutInterface<IDeliveryCacheProgressService> out_interface);
30
31 Result SetPassphrase(u64 title_id, InBuffer<BufferAttr_HipcPointer> passphrase_buffer);
32
33 Result RegisterSystemApplicationDeliveryTasks();
34
35 Result ClearDeliveryCacheStorage(u64 title_id);
36
37private:
38 ProgressServiceBackend& GetProgressBackend(SyncType type);
39 const ProgressServiceBackend& GetProgressBackend(SyncType type) const;
40
41 BcatBackend& backend;
42 std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
43};
44
45} // 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..a56f9248f
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_types.h
@@ -0,0 +1,60 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8#include "core/file_sys/vfs/vfs_types.h"
9
10namespace Service::BCAT {
11
12using DirectoryName = std::array<char, 0x20>;
13using FileName = std::array<char, 0x20>;
14using BcatDigest = std::array<u8, 0x10>;
15using Passphrase = std::array<u8, 0x20>;
16using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>;
17
18enum class SyncType {
19 Normal,
20 Directory,
21 Count,
22};
23
24enum class DeliveryCacheProgressStatus : s32 {
25 None = 0x0,
26 Queued = 0x1,
27 Connecting = 0x2,
28 ProcessingDataList = 0x3,
29 Downloading = 0x4,
30 Committing = 0x5,
31 Done = 0x9,
32};
33
34struct DeliveryCacheDirectoryEntry {
35 FileName name;
36 u64 size;
37 BcatDigest digest;
38};
39
40struct TitleIDVersion {
41 u64 title_id;
42 u64 build_id;
43};
44
45struct DeliveryCacheProgressImpl {
46 DeliveryCacheProgressStatus status;
47 Result result = ResultSuccess;
48 DirectoryName current_directory;
49 FileName current_file;
50 s64 current_downloaded_bytes; ///< Bytes downloaded on current file.
51 s64 current_total_bytes; ///< Bytes total on current file.
52 s64 total_downloaded_bytes; ///< Bytes downloaded on overall download.
53 s64 total_bytes; ///< Bytes total on overall download.
54 INSERT_PADDING_BYTES(
55 0x198); ///< Appears to be unused in official code, possibly reserved for future use.
56};
57static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200,
58 "DeliveryCacheProgressImpl has incorrect size.");
59
60} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <cctype>
8#include <mbedtls/md5.h>
9
10#include "core/hle/service/bcat/bcat_result.h"
11#include "core/hle/service/bcat/bcat_types.h"
12
13namespace Service::BCAT {
14
15// For a name to be valid it must be non-empty, must have a null terminating character as the final
16// char, can only contain numbers, letters, underscores and a hyphen if directory and a period if
17// file.
18constexpr Result VerifyNameValidInternal(std::array<char, 0x20> name, char match_char) {
19 const auto null_chars = std::count(name.begin(), name.end(), 0);
20 const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) {
21 return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0';
22 });
23 if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') {
24 LOG_ERROR(Service_BCAT, "Name passed was invalid!");
25 return ResultInvalidArgument;
26 }
27
28 return ResultSuccess;
29}
30
31constexpr Result VerifyNameValidDir(DirectoryName name) {
32 return VerifyNameValidInternal(name, '-');
33}
34
35constexpr Result VerifyNameValidFile(FileName name) {
36 return VerifyNameValidInternal(name, '.');
37}
38
39} // 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..8bda1168a
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_directory_service.cpp
@@ -0,0 +1,81 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/string_util.h"
5#include "core/file_sys/vfs/vfs_types.h"
6#include "core/hle/service/bcat/bcat_result.h"
7#include "core/hle/service/bcat/bcat_util.h"
8#include "core/hle/service/bcat/delivery_cache_directory_service.h"
9#include "core/hle/service/cmif_serialization.h"
10
11namespace Service::BCAT {
12
13// The digest is only used to determine if a file is unique compared to others of the same name.
14// Since the algorithm isn't ever checked in game, MD5 is safe.
15BcatDigest DigestFile(const FileSys::VirtualFile& file) {
16 BcatDigest out{};
17 const auto bytes = file->ReadAllBytes();
18 mbedtls_md5_ret(bytes.data(), bytes.size(), out.data());
19 return out;
20}
21
22IDeliveryCacheDirectoryService::IDeliveryCacheDirectoryService(Core::System& system_,
23 FileSys::VirtualDir root_)
24 : ServiceFramework{system_, "IDeliveryCacheDirectoryService"}, root(std::move(root_)) {
25 // clang-format off
26 static const FunctionInfo functions[] = {
27 {0, C<&IDeliveryCacheDirectoryService::Open>, "Open"},
28 {1, C<&IDeliveryCacheDirectoryService::Read>, "Read"},
29 {2, C<&IDeliveryCacheDirectoryService::GetCount>, "GetCount"},
30 };
31 // clang-format on
32
33 RegisterHandlers(functions);
34}
35
36IDeliveryCacheDirectoryService::~IDeliveryCacheDirectoryService() = default;
37
38Result IDeliveryCacheDirectoryService::Open(DirectoryName dir_name_raw) {
39 const auto dir_name =
40 Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size());
41
42 LOG_DEBUG(Service_BCAT, "called, dir_name={}", dir_name);
43
44 // R_TRY(VerifyNameValidDir(dir_name_raw));
45 R_UNLESS(current_dir == nullptr, ResultEntityAlreadyOpen);
46
47 const auto dir = root->GetSubdirectory(dir_name);
48 R_UNLESS(dir != nullptr, ResultFailedOpenEntity);
49
50 R_SUCCEED();
51}
52
53Result IDeliveryCacheDirectoryService::Read(
54 Out<u32> out_buffer_size,
55 OutArray<DeliveryCacheDirectoryEntry, BufferAttr_HipcMapAlias> out_buffer) {
56 LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", out_buffer.size());
57
58 R_UNLESS(current_dir != nullptr, ResultNoOpenEntry);
59
60 const auto files = current_dir->GetFiles();
61 *out_buffer_size = static_cast<u32>(std::min(files.size(), out_buffer.size()));
62 std::transform(files.begin(), files.begin() + *out_buffer_size, out_buffer.begin(),
63 [](const auto& file) {
64 FileName name{};
65 std::memcpy(name.data(), file->GetName().data(),
66 std::min(file->GetName().size(), name.size()));
67 return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)};
68 });
69 R_SUCCEED();
70}
71
72Result IDeliveryCacheDirectoryService::GetCount(Out<u32> out_count) {
73 LOG_DEBUG(Service_BCAT, "called");
74
75 R_UNLESS(current_dir != nullptr, ResultNoOpenEntry);
76
77 *out_count = static_cast<u32>(current_dir->GetFiles().size());
78 R_SUCCEED();
79}
80
81} // 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..f544d0947
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_directory_service.h
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/file_sys/vfs/vfs.h"
7#include "core/hle/service/bcat/bcat_types.h"
8#include "core/hle/service/cmif_types.h"
9#include "core/hle/service/service.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::BCAT {
16
17class IDeliveryCacheDirectoryService final
18 : public ServiceFramework<IDeliveryCacheDirectoryService> {
19public:
20 explicit IDeliveryCacheDirectoryService(Core::System& system_, FileSys::VirtualDir root_);
21 ~IDeliveryCacheDirectoryService() override;
22
23private:
24 Result Open(DirectoryName dir_name_raw);
25 Result Read(Out<u32> out_buffer_size,
26 OutArray<DeliveryCacheDirectoryEntry, BufferAttr_HipcMapAlias> out_buffer);
27 Result GetCount(Out<u32> out_count);
28
29 FileSys::VirtualDir root;
30 FileSys::VirtualDir current_dir;
31};
32
33} // 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..c91efd47e
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_file_service.cpp
@@ -0,0 +1,81 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/string_util.h"
5#include "core/hle/service/bcat/bcat_result.h"
6#include "core/hle/service/bcat/bcat_util.h"
7#include "core/hle/service/bcat/delivery_cache_file_service.h"
8#include "core/hle/service/cmif_serialization.h"
9
10namespace Service::BCAT {
11
12IDeliveryCacheFileService::IDeliveryCacheFileService(Core::System& system_,
13 FileSys::VirtualDir root_)
14 : ServiceFramework{system_, "IDeliveryCacheFileService"}, root(std::move(root_)) {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {0, C<&IDeliveryCacheFileService::Open>, "Open"},
18 {1, C<&IDeliveryCacheFileService::Read>, "Read"},
19 {2, C<&IDeliveryCacheFileService::GetSize>, "GetSize"},
20 {3, C<&IDeliveryCacheFileService::GetDigest>, "GetDigest"},
21 };
22 // clang-format on
23
24 RegisterHandlers(functions);
25}
26
27IDeliveryCacheFileService::~IDeliveryCacheFileService() = default;
28
29Result IDeliveryCacheFileService::Open(DirectoryName dir_name_raw, FileName file_name_raw) {
30 const auto dir_name =
31 Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size());
32 const auto file_name =
33 Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size());
34
35 LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name);
36
37 R_TRY(VerifyNameValidDir(dir_name_raw));
38 R_TRY(VerifyNameValidDir(file_name_raw));
39 R_UNLESS(current_file == nullptr, ResultEntityAlreadyOpen);
40
41 const auto dir = root->GetSubdirectory(dir_name);
42 R_UNLESS(dir != nullptr, ResultFailedOpenEntity);
43
44 current_file = dir->GetFile(file_name);
45 R_UNLESS(current_file != nullptr, ResultFailedOpenEntity);
46
47 R_SUCCEED();
48}
49
50Result IDeliveryCacheFileService::Read(Out<u64> out_buffer_size, u64 offset,
51 OutBuffer<BufferAttr_HipcMapAlias> out_buffer) {
52 LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, out_buffer.size());
53
54 R_UNLESS(current_file != nullptr, ResultNoOpenEntry);
55
56 *out_buffer_size = std::min<u64>(current_file->GetSize() - offset, out_buffer.size());
57 const auto buffer = current_file->ReadBytes(*out_buffer_size, offset);
58 memcpy(out_buffer.data(), buffer.data(), buffer.size());
59
60 R_SUCCEED();
61}
62
63Result IDeliveryCacheFileService::GetSize(Out<u64> out_size) {
64 LOG_DEBUG(Service_BCAT, "called");
65
66 R_UNLESS(current_file != nullptr, ResultNoOpenEntry);
67
68 *out_size = current_file->GetSize();
69 R_SUCCEED();
70}
71
72Result IDeliveryCacheFileService::GetDigest(Out<BcatDigest> out_digest) {
73 LOG_DEBUG(Service_BCAT, "called");
74
75 R_UNLESS(current_file != nullptr, ResultNoOpenEntry);
76
77 //*out_digest = DigestFile(current_file);
78 R_SUCCEED();
79}
80
81} // 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..7583b4d1d
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_file_service.h
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/file_sys/vfs/vfs.h"
7#include "core/hle/service/bcat/bcat_types.h"
8#include "core/hle/service/cmif_types.h"
9#include "core/hle/service/service.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::BCAT {
16
17class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> {
18public:
19 explicit IDeliveryCacheFileService(Core::System& system_, FileSys::VirtualDir root_);
20 ~IDeliveryCacheFileService() override;
21
22private:
23 Result Open(DirectoryName dir_name_raw, FileName file_name_raw);
24 Result Read(Out<u64> out_buffer_size, u64 offset,
25 OutBuffer<BufferAttr_HipcMapAlias> out_buffer);
26 Result GetSize(Out<u64> out_size);
27 Result GetDigest(Out<BcatDigest> out_digest);
28
29 FileSys::VirtualDir root;
30 FileSys::VirtualFile current_file;
31};
32
33} // 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..94d341f7e
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_progress_service.cpp
@@ -0,0 +1,41 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/bcat/bcat_types.h"
5#include "core/hle/service/bcat/delivery_cache_progress_service.h"
6#include "core/hle/service/cmif_serialization.h"
7
8namespace Service::BCAT {
9
10IDeliveryCacheProgressService::IDeliveryCacheProgressService(Core::System& system_,
11 Kernel::KReadableEvent& event_,
12 const DeliveryCacheProgressImpl& impl_)
13 : ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{event_}, impl{impl_} {
14 // clang-format off
15 static const FunctionInfo functions[] = {
16 {0, C<&IDeliveryCacheProgressService::GetEvent>, "Get"},
17 {0, C<&IDeliveryCacheProgressService::GetImpl>, "Get"},
18 };
19 // clang-format on
20
21 RegisterHandlers(functions);
22}
23
24IDeliveryCacheProgressService::~IDeliveryCacheProgressService() = default;
25
26Result IDeliveryCacheProgressService::GetEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
27 LOG_DEBUG(Service_BCAT, "called");
28
29 *out_event = &event;
30 R_SUCCEED();
31}
32
33Result IDeliveryCacheProgressService::GetImpl(
34 OutLargeData<DeliveryCacheProgressImpl, BufferAttr_HipcPointer> out_impl) {
35 LOG_DEBUG(Service_BCAT, "called");
36
37 *out_impl = impl;
38 R_SUCCEED();
39}
40
41} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Kernel {
14class KEvent;
15class KReadableEvent;
16} // namespace Kernel
17
18namespace Service::BCAT {
19struct DeliveryCacheProgressImpl;
20
21class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> {
22public:
23 explicit IDeliveryCacheProgressService(Core::System& system_, Kernel::KReadableEvent& event_,
24 const DeliveryCacheProgressImpl& impl_);
25 ~IDeliveryCacheProgressService() override;
26
27private:
28 Result GetEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
29 Result GetImpl(OutLargeData<DeliveryCacheProgressImpl, BufferAttr_HipcPointer> out_impl);
30
31 Kernel::KReadableEvent& event;
32 const DeliveryCacheProgressImpl& impl;
33};
34
35} // 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..5d03df1e7
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_storage_service.cpp
@@ -0,0 +1,57 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/bcat/bcat_result.h"
5#include "core/hle/service/bcat/delivery_cache_directory_service.h"
6#include "core/hle/service/bcat/delivery_cache_file_service.h"
7#include "core/hle/service/bcat/delivery_cache_storage_service.h"
8#include "core/hle/service/cmif_serialization.h"
9
10namespace Service::BCAT {
11
12IDeliveryCacheStorageService::IDeliveryCacheStorageService(Core::System& system_,
13 FileSys::VirtualDir root_)
14 : ServiceFramework{system_, "IDeliveryCacheStorageService"}, root(std::move(root_)) {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {0, C<&IDeliveryCacheStorageService::CreateFileService>, "CreateFileService"},
18 {1, C<&IDeliveryCacheStorageService::CreateDirectoryService>, "CreateDirectoryService"},
19 {2, C<&IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory>, "EnumerateDeliveryCacheDirectory"},
20 };
21 // clang-format on
22
23 RegisterHandlers(functions);
24}
25
26IDeliveryCacheStorageService::~IDeliveryCacheStorageService() = default;
27
28Result IDeliveryCacheStorageService::CreateFileService(
29 OutInterface<IDeliveryCacheFileService> out_interface) {
30 LOG_DEBUG(Service_BCAT, "called");
31
32 *out_interface = std::make_shared<IDeliveryCacheFileService>(system, root);
33 R_SUCCEED();
34}
35
36Result IDeliveryCacheStorageService::CreateDirectoryService(
37 OutInterface<IDeliveryCacheDirectoryService> out_interface) {
38 LOG_DEBUG(Service_BCAT, "called");
39
40 *out_interface = std::make_shared<IDeliveryCacheDirectoryService>(system, root);
41 R_SUCCEED();
42}
43
44Result IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory(
45 Out<u32> out_directories_size,
46 OutArray<DirectoryName, BufferAttr_HipcMapAlias> out_directories) {
47 LOG_DEBUG(Service_BCAT, "called, size={:016X}", out_directories.size());
48
49 *out_directories_size =
50 static_cast<u32>(std::min(out_directories.size(), entries.size() - next_read_index));
51 memcpy(out_directories.data(), entries.data() + next_read_index,
52 *out_directories_size * sizeof(DirectoryName));
53 next_read_index += *out_directories_size;
54 R_SUCCEED();
55}
56
57} // 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..1e86d3dbf
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_storage_service.h
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/file_sys/vfs/vfs.h"
7#include "core/hle/service/bcat/bcat_types.h"
8#include "core/hle/service/cmif_types.h"
9#include "core/hle/service/service.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::BCAT {
16class IDeliveryCacheFileService;
17class IDeliveryCacheDirectoryService;
18
19class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> {
20public:
21 explicit IDeliveryCacheStorageService(Core::System& system_, FileSys::VirtualDir root_);
22 ~IDeliveryCacheStorageService() override;
23
24private:
25 Result CreateFileService(OutInterface<IDeliveryCacheFileService> out_interface);
26 Result CreateDirectoryService(OutInterface<IDeliveryCacheDirectoryService> out_interface);
27 Result EnumerateDeliveryCacheDirectory(
28 Out<u32> out_directories_size,
29 OutArray<DirectoryName, BufferAttr_HipcMapAlias> out_directories);
30
31 FileSys::VirtualDir root;
32 std::vector<DirectoryName> entries;
33 u64 next_read_index = 0;
34};
35
36} // namespace Service::BCAT