summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt22
-rw-r--r--src/core/core.cpp29
-rw-r--r--src/core/core.h19
-rw-r--r--src/core/crypto/key_manager.cpp2
-rw-r--r--src/core/file_sys/bis_factory.cpp5
-rw-r--r--src/core/file_sys/bis_factory.h2
-rw-r--r--src/core/file_sys/romfs_factory.cpp4
-rw-r--r--src/core/file_sys/romfs_factory.h2
-rw-r--r--src/core/file_sys/savedata_factory.cpp5
-rw-r--r--src/core/file_sys/vfs_libzip.cpp79
-rw-r--r--src/core/file_sys/vfs_libzip.h13
-rw-r--r--src/core/gdbstub/gdbstub.cpp3
-rw-r--r--src/core/hle/kernel/handle_table.cpp2
-rw-r--r--src/core/hle/service/am/am.cpp69
-rw-r--r--src/core/hle/service/am/am.h5
-rw-r--r--src/core/hle/service/am/applets/applets.cpp4
-rw-r--r--src/core/hle/service/am/applets/applets.h2
-rw-r--r--src/core/hle/service/apm/controller.cpp3
-rw-r--r--src/core/hle/service/apm/controller.h6
-rw-r--r--src/core/hle/service/audio/audout_u.cpp2
-rw-r--r--src/core/hle/service/bcat/backend/backend.cpp137
-rw-r--r--src/core/hle/service/bcat/backend/backend.h150
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp504
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.h58
-rw-r--r--src/core/hle/service/bcat/bcat.cpp9
-rw-r--r--src/core/hle/service/bcat/bcat.h7
-rw-r--r--src/core/hle/service/bcat/module.cpp561
-rw-r--r--src/core/hle/service/bcat/module.h31
-rw-r--r--src/core/hle/service/es/es.cpp12
-rw-r--r--src/core/hle/service/fatal/fatal.cpp2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp29
-rw-r--r--src/core/hle/service/filesystem/filesystem.h10
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp2
-rw-r--r--src/core/hle/service/friend/friend.cpp1
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp5
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h1
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h1
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h1
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h1
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp18
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h1
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h1
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h1
-rw-r--r--src/core/hle/service/hid/hid.cpp19
-rw-r--r--src/core/hle/service/hid/hid.h1
-rw-r--r--src/core/hle/service/ldr/ldr.cpp6
-rw-r--r--src/core/hle/service/lm/lm.cpp187
-rw-r--r--src/core/hle/service/lm/lm.h6
-rw-r--r--src/core/hle/service/lm/manager.cpp133
-rw-r--r--src/core/hle/service/lm/manager.h106
-rw-r--r--src/core/hle/service/nfp/nfp.cpp9
-rw-r--r--src/core/hle/service/nifm/nifm.cpp13
-rw-r--r--src/core/hle/service/ns/pl_u.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp2
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/loader/nso.cpp1
-rw-r--r--src/core/memory.cpp10
-rw-r--r--src/core/reporter.cpp51
-rw-r--r--src/core/reporter.h14
-rw-r--r--src/core/settings.cpp2
-rw-r--r--src/core/settings.h4
70 files changed, 2131 insertions, 294 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index a6b56c9c6..3b1d72cf9 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1,3 +1,9 @@
1if (YUZU_ENABLE_BOXCAT)
2 set(BCAT_BOXCAT_ADDITIONAL_SOURCES hle/service/bcat/backend/boxcat.cpp hle/service/bcat/backend/boxcat.h)
3else()
4 set(BCAT_BOXCAT_ADDITIONAL_SOURCES)
5endif()
6
1add_library(core STATIC 7add_library(core STATIC
2 arm/arm_interface.h 8 arm/arm_interface.h
3 arm/arm_interface.cpp 9 arm/arm_interface.cpp
@@ -82,6 +88,8 @@ add_library(core STATIC
82 file_sys/vfs_concat.h 88 file_sys/vfs_concat.h
83 file_sys/vfs_layered.cpp 89 file_sys/vfs_layered.cpp
84 file_sys/vfs_layered.h 90 file_sys/vfs_layered.h
91 file_sys/vfs_libzip.cpp
92 file_sys/vfs_libzip.h
85 file_sys/vfs_offset.cpp 93 file_sys/vfs_offset.cpp
86 file_sys/vfs_offset.h 94 file_sys/vfs_offset.h
87 file_sys/vfs_real.cpp 95 file_sys/vfs_real.cpp
@@ -241,6 +249,9 @@ add_library(core STATIC
241 hle/service/audio/errors.h 249 hle/service/audio/errors.h
242 hle/service/audio/hwopus.cpp 250 hle/service/audio/hwopus.cpp
243 hle/service/audio/hwopus.h 251 hle/service/audio/hwopus.h
252 hle/service/bcat/backend/backend.cpp
253 hle/service/bcat/backend/backend.h
254 ${BCAT_BOXCAT_ADDITIONAL_SOURCES}
244 hle/service/bcat/bcat.cpp 255 hle/service/bcat/bcat.cpp
245 hle/service/bcat/bcat.h 256 hle/service/bcat/bcat.h
246 hle/service/bcat/module.cpp 257 hle/service/bcat/module.cpp
@@ -324,6 +335,8 @@ add_library(core STATIC
324 hle/service/ldr/ldr.h 335 hle/service/ldr/ldr.h
325 hle/service/lm/lm.cpp 336 hle/service/lm/lm.cpp
326 hle/service/lm/lm.h 337 hle/service/lm/lm.h
338 hle/service/lm/manager.cpp
339 hle/service/lm/manager.h
327 hle/service/mig/mig.cpp 340 hle/service/mig/mig.cpp
328 hle/service/mig/mig.h 341 hle/service/mig/mig.h
329 hle/service/mii/mii.cpp 342 hle/service/mii/mii.cpp
@@ -499,6 +512,15 @@ create_target_directory_groups(core)
499 512
500target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 513target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
501target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives) 514target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives)
515
516if (YUZU_ENABLE_BOXCAT)
517 get_directory_property(OPENSSL_LIBS
518 DIRECTORY ${PROJECT_SOURCE_DIR}/externals/libressl
519 DEFINITION OPENSSL_LIBS)
520 target_compile_definitions(core PRIVATE -DCPPHTTPLIB_OPENSSL_SUPPORT -DYUZU_ENABLE_BOXCAT)
521 target_link_libraries(core PRIVATE httplib json-headers ${OPENSSL_LIBS} zip)
522endif()
523
502if (ENABLE_WEB_SERVICE) 524if (ENABLE_WEB_SERVICE)
503 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) 525 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
504 target_link_libraries(core PRIVATE web_service) 526 target_link_libraries(core PRIVATE web_service)
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 92ba42fb9..4d0ac72a5 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -35,6 +35,7 @@
35#include "core/hle/service/apm/controller.h" 35#include "core/hle/service/apm/controller.h"
36#include "core/hle/service/filesystem/filesystem.h" 36#include "core/hle/service/filesystem/filesystem.h"
37#include "core/hle/service/glue/manager.h" 37#include "core/hle/service/glue/manager.h"
38#include "core/hle/service/lm/manager.h"
38#include "core/hle/service/service.h" 39#include "core/hle/service/service.h"
39#include "core/hle/service/sm/sm.h" 40#include "core/hle/service/sm/sm.h"
40#include "core/loader/loader.h" 41#include "core/loader/loader.h"
@@ -111,7 +112,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
111} 112}
112struct System::Impl { 113struct System::Impl {
113 explicit Impl(System& system) 114 explicit Impl(System& system)
114 : kernel{system}, cpu_core_manager{system}, applet_manager{system}, reporter{system} {} 115 : kernel{system}, fs_controller{system}, cpu_core_manager{system},
116 applet_manager{system}, reporter{system} {}
115 117
116 Cpu& CurrentCpuCore() { 118 Cpu& CurrentCpuCore() {
117 return cpu_core_manager.GetCurrentCore(); 119 return cpu_core_manager.GetCurrentCore();
@@ -249,6 +251,8 @@ struct System::Impl {
249 telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS", 251 telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS",
250 perf_stats->GetMeanFrametime()); 252 perf_stats->GetMeanFrametime());
251 253
254 lm_manager.Flush();
255
252 is_powered_on = false; 256 is_powered_on = false;
253 exit_lock = false; 257 exit_lock = false;
254 258
@@ -337,8 +341,10 @@ struct System::Impl {
337 bool is_powered_on = false; 341 bool is_powered_on = false;
338 bool exit_lock = false; 342 bool exit_lock = false;
339 343
344 Reporter reporter;
340 std::unique_ptr<Memory::CheatEngine> cheat_engine; 345 std::unique_ptr<Memory::CheatEngine> cheat_engine;
341 std::unique_ptr<Tools::Freezer> memory_freezer; 346 std::unique_ptr<Tools::Freezer> memory_freezer;
347 std::array<u8, 0x20> build_id{};
342 348
343 /// Frontend applets 349 /// Frontend applets
344 Service::AM::Applets::AppletManager applet_manager; 350 Service::AM::Applets::AppletManager applet_manager;
@@ -346,8 +352,9 @@ struct System::Impl {
346 /// APM (Performance) services 352 /// APM (Performance) services
347 Service::APM::Controller apm_controller{core_timing}; 353 Service::APM::Controller apm_controller{core_timing};
348 354
349 /// Glue services 355 /// Service State
350 Service::Glue::ARPManager arp_manager; 356 Service::Glue::ARPManager arp_manager;
357 Service::LM::Manager lm_manager{reporter};
351 358
352 /// Service manager 359 /// Service manager
353 std::shared_ptr<Service::SM::ServiceManager> service_manager; 360 std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -355,8 +362,6 @@ struct System::Impl {
355 /// Telemetry session for this emulation session 362 /// Telemetry session for this emulation session
356 std::unique_ptr<Core::TelemetrySession> telemetry_session; 363 std::unique_ptr<Core::TelemetrySession> telemetry_session;
357 364
358 Reporter reporter;
359
360 ResultStatus status = ResultStatus::Success; 365 ResultStatus status = ResultStatus::Success;
361 std::string status_details = ""; 366 std::string status_details = "";
362 367
@@ -632,6 +637,14 @@ const Service::APM::Controller& System::GetAPMController() const {
632 return impl->apm_controller; 637 return impl->apm_controller;
633} 638}
634 639
640Service::LM::Manager& System::GetLogManager() {
641 return impl->lm_manager;
642}
643
644const Service::LM::Manager& System::GetLogManager() const {
645 return impl->lm_manager;
646}
647
635void System::SetExitLock(bool locked) { 648void System::SetExitLock(bool locked) {
636 impl->exit_lock = locked; 649 impl->exit_lock = locked;
637} 650}
@@ -640,6 +653,14 @@ bool System::GetExitLock() const {
640 return impl->exit_lock; 653 return impl->exit_lock;
641} 654}
642 655
656void System::SetCurrentProcessBuildID(const CurrentBuildProcessID& id) {
657 impl->build_id = id;
658}
659
660const System::CurrentBuildProcessID& System::GetCurrentProcessBuildID() const {
661 return impl->build_id;
662}
663
643System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { 664System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
644 return impl->Init(*this, emu_window); 665 return impl->Init(*this, emu_window);
645} 666}
diff --git a/src/core/core.h b/src/core/core.h
index ff10ebe12..90e7ac607 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -8,7 +8,6 @@
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10 10
11#include <map>
12#include "common/common_types.h" 11#include "common/common_types.h"
13#include "core/file_sys/vfs_types.h" 12#include "core/file_sys/vfs_types.h"
14#include "core/hle/kernel/object.h" 13#include "core/hle/kernel/object.h"
@@ -58,6 +57,10 @@ namespace Glue {
58class ARPManager; 57class ARPManager;
59} 58}
60 59
60namespace LM {
61class Manager;
62} // namespace LM
63
61namespace SM { 64namespace SM {
62class ServiceManager; 65class ServiceManager;
63} // namespace SM 66} // namespace SM
@@ -98,6 +101,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
98 101
99class System { 102class System {
100public: 103public:
104 using CurrentBuildProcessID = std::array<u8, 0x20>;
105
101 System(const System&) = delete; 106 System(const System&) = delete;
102 System& operator=(const System&) = delete; 107 System& operator=(const System&) = delete;
103 108
@@ -326,10 +331,18 @@ public:
326 331
327 const Service::APM::Controller& GetAPMController() const; 332 const Service::APM::Controller& GetAPMController() const;
328 333
334 Service::LM::Manager& GetLogManager();
335
336 const Service::LM::Manager& GetLogManager() const;
337
329 void SetExitLock(bool locked); 338 void SetExitLock(bool locked);
330 339
331 bool GetExitLock() const; 340 bool GetExitLock() const;
332 341
342 void SetCurrentProcessBuildID(const CurrentBuildProcessID& id);
343
344 const CurrentBuildProcessID& GetCurrentProcessBuildID() const;
345
333private: 346private:
334 System(); 347 System();
335 348
@@ -353,8 +366,4 @@ private:
353 static System s_instance; 366 static System s_instance;
354}; 367};
355 368
356inline Kernel::Process* CurrentProcess() {
357 return System::GetInstance().CurrentProcess();
358}
359
360} // namespace Core 369} // namespace Core
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index 46aceec3d..222fc95ba 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -423,7 +423,7 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
423std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, 423std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
424 const RSAKeyPair<2048>& key) { 424 const RSAKeyPair<2048>& key) {
425 const auto issuer = ticket.GetData().issuer; 425 const auto issuer = ticket.GetData().issuer;
426 if (issuer == std::array<u8, 0x40>{}) 426 if (IsAllZeroArray(issuer))
427 return {}; 427 return {};
428 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { 428 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') {
429 LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); 429 LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority.");
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 8f758d6d9..0af44f340 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -136,4 +136,9 @@ u64 BISFactory::GetFullNANDTotalSpace() const {
136 return static_cast<u64>(Settings::values.nand_total_size); 136 return static_cast<u64>(Settings::values.nand_total_size);
137} 137}
138 138
139VirtualDir BISFactory::GetBCATDirectory(u64 title_id) const {
140 return GetOrCreateDirectoryRelative(nand_root,
141 fmt::format("/system/save/bcat/{:016X}", title_id));
142}
143
139} // namespace FileSys 144} // namespace FileSys
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index bdfe728c9..8f0451c98 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -61,6 +61,8 @@ public:
61 u64 GetUserNANDTotalSpace() const; 61 u64 GetUserNANDTotalSpace() const;
62 u64 GetFullNANDTotalSpace() const; 62 u64 GetFullNANDTotalSpace() const;
63 63
64 VirtualDir GetBCATDirectory(u64 title_id) const;
65
64private: 66private:
65 VirtualDir nand_root; 67 VirtualDir nand_root;
66 VirtualDir load_root; 68 VirtualDir load_root;
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index 84cd4684c..4bd2e6183 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -35,11 +35,11 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
35 this->update_raw = std::move(update_raw); 35 this->update_raw = std::move(update_raw);
36} 36}
37 37
38ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() const { 38ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_title_id) const {
39 if (!updatable) 39 if (!updatable)
40 return MakeResult<VirtualFile>(file); 40 return MakeResult<VirtualFile>(file);
41 41
42 const PatchManager patch_manager(Core::CurrentProcess()->GetTitleID()); 42 const PatchManager patch_manager(current_process_title_id);
43 return MakeResult<VirtualFile>( 43 return MakeResult<VirtualFile>(
44 patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); 44 patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
45} 45}
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
index da63a313a..c5d40285c 100644
--- a/src/core/file_sys/romfs_factory.h
+++ b/src/core/file_sys/romfs_factory.h
@@ -33,7 +33,7 @@ public:
33 ~RomFSFactory(); 33 ~RomFSFactory();
34 34
35 void SetPackedUpdate(VirtualFile update_raw); 35 void SetPackedUpdate(VirtualFile update_raw);
36 ResultVal<VirtualFile> OpenCurrentProcess() const; 36 ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const;
37 ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const; 37 ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const;
38 38
39private: 39private:
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index f77cc02ac..fc8755c78 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -127,8 +127,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
127 u128 user_id, u64 save_id) { 127 u128 user_id, u64 save_id) {
128 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should 128 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
129 // be interpreted as the title id of the current process. 129 // be interpreted as the title id of the current process.
130 if (type == SaveDataType::SaveData && title_id == 0) 130 if (type == SaveDataType::SaveData && title_id == 0) {
131 title_id = Core::CurrentProcess()->GetTitleID(); 131 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
132 }
132 133
133 std::string out = GetSaveDataSpaceIdPath(space); 134 std::string out = GetSaveDataSpaceIdPath(space);
134 135
diff --git a/src/core/file_sys/vfs_libzip.cpp b/src/core/file_sys/vfs_libzip.cpp
new file mode 100644
index 000000000..8bdaa7e4a
--- /dev/null
+++ b/src/core/file_sys/vfs_libzip.cpp
@@ -0,0 +1,79 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6#include <zip.h>
7#include "common/logging/backend.h"
8#include "core/file_sys/vfs.h"
9#include "core/file_sys/vfs_libzip.h"
10#include "core/file_sys/vfs_vector.h"
11
12namespace FileSys {
13
14VirtualDir ExtractZIP(VirtualFile file) {
15 zip_error_t error{};
16
17 const auto data = file->ReadAllBytes();
18 std::unique_ptr<zip_source_t, decltype(&zip_source_close)> src{
19 zip_source_buffer_create(data.data(), data.size(), 0, &error), zip_source_close};
20 if (src == nullptr)
21 return nullptr;
22
23 std::unique_ptr<zip_t, decltype(&zip_close)> zip{zip_open_from_source(src.get(), 0, &error),
24 zip_close};
25 if (zip == nullptr)
26 return nullptr;
27
28 std::shared_ptr<VectorVfsDirectory> out = std::make_shared<VectorVfsDirectory>();
29
30 const auto num_entries = zip_get_num_entries(zip.get(), 0);
31
32 zip_stat_t stat{};
33 zip_stat_init(&stat);
34
35 for (std::size_t i = 0; i < num_entries; ++i) {
36 const auto stat_res = zip_stat_index(zip.get(), i, 0, &stat);
37 if (stat_res == -1)
38 return nullptr;
39
40 const std::string name(stat.name);
41 if (name.empty())
42 continue;
43
44 if (name.back() != '/') {
45 std::unique_ptr<zip_file_t, decltype(&zip_fclose)> file{
46 zip_fopen_index(zip.get(), i, 0), zip_fclose};
47
48 std::vector<u8> buf(stat.size);
49 if (zip_fread(file.get(), buf.data(), buf.size()) != buf.size())
50 return nullptr;
51
52 const auto parts = FileUtil::SplitPathComponents(stat.name);
53 const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back());
54
55 std::shared_ptr<VectorVfsDirectory> dtrv = out;
56 for (std::size_t j = 0; j < parts.size() - 1; ++j) {
57 if (dtrv == nullptr)
58 return nullptr;
59 const auto subdir = dtrv->GetSubdirectory(parts[j]);
60 if (subdir == nullptr) {
61 const auto temp = std::make_shared<VectorVfsDirectory>(
62 std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parts[j]);
63 dtrv->AddDirectory(temp);
64 dtrv = temp;
65 } else {
66 dtrv = std::dynamic_pointer_cast<VectorVfsDirectory>(subdir);
67 }
68 }
69
70 if (dtrv == nullptr)
71 return nullptr;
72 dtrv->AddFile(new_file);
73 }
74 }
75
76 return out;
77}
78
79} // namespace FileSys
diff --git a/src/core/file_sys/vfs_libzip.h b/src/core/file_sys/vfs_libzip.h
new file mode 100644
index 000000000..f68af576a
--- /dev/null
+++ b/src/core/file_sys/vfs_libzip.h
@@ -0,0 +1,13 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/file_sys/vfs_types.h"
8
9namespace FileSys {
10
11VirtualDir ExtractZIP(VirtualFile zip);
12
13} // namespace FileSys
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index afa812598..db51d722f 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -641,7 +641,8 @@ static void HandleQuery() {
641 strlen("Xfer:features:read:target.xml:")) == 0) { 641 strlen("Xfer:features:read:target.xml:")) == 0) {
642 SendReply(target_xml); 642 SendReply(target_xml);
643 } else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) { 643 } else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) {
644 const VAddr base_address = Core::CurrentProcess()->VMManager().GetCodeRegionBaseAddress(); 644 const VAddr base_address =
645 Core::System::GetInstance().CurrentProcess()->VMManager().GetCodeRegionBaseAddress();
645 std::string buffer = fmt::format("TextSeg={:0x}", base_address); 646 std::string buffer = fmt::format("TextSeg={:0x}", base_address);
646 SendReply(buffer.c_str()); 647 SendReply(buffer.c_str());
647 } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { 648 } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index bdfaa977f..2cc5d536b 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -103,7 +103,7 @@ SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
103 if (handle == CurrentThread) { 103 if (handle == CurrentThread) {
104 return GetCurrentThread(); 104 return GetCurrentThread();
105 } else if (handle == CurrentProcess) { 105 } else if (handle == CurrentProcess) {
106 return Core::CurrentProcess(); 106 return Core::System::GetInstance().CurrentProcess();
107 } 107 }
108 108
109 if (!IsValid(handle)) { 109 if (!IsValid(handle)) {
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 797c9a06f..941ebc93a 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -31,6 +31,7 @@
31#include "core/hle/service/am/tcap.h" 31#include "core/hle/service/am/tcap.h"
32#include "core/hle/service/apm/controller.h" 32#include "core/hle/service/apm/controller.h"
33#include "core/hle/service/apm/interface.h" 33#include "core/hle/service/apm/interface.h"
34#include "core/hle/service/bcat/backend/backend.h"
34#include "core/hle/service/filesystem/filesystem.h" 35#include "core/hle/service/filesystem/filesystem.h"
35#include "core/hle/service/ns/ns.h" 36#include "core/hle/service/ns/ns.h"
36#include "core/hle/service/nvflinger/nvflinger.h" 37#include "core/hle/service/nvflinger/nvflinger.h"
@@ -46,15 +47,20 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
46constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3}; 47constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3};
47constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; 48constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
48 49
49constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; 50enum class LaunchParameterKind : u32 {
51 ApplicationSpecific = 1,
52 AccountPreselectedUser = 2,
53};
54
55constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA;
50 56
51struct LaunchParameters { 57struct LaunchParameterAccountPreselectedUser {
52 u32_le magic; 58 u32_le magic;
53 u32_le is_account_selected; 59 u32_le is_account_selected;
54 u128 current_user; 60 u128 current_user;
55 INSERT_PADDING_BYTES(0x70); 61 INSERT_PADDING_BYTES(0x70);
56}; 62};
57static_assert(sizeof(LaunchParameters) == 0x88); 63static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88);
58 64
59IWindowController::IWindowController(Core::System& system_) 65IWindowController::IWindowController(Core::System& system_)
60 : ServiceFramework("IWindowController"), system{system_} { 66 : ServiceFramework("IWindowController"), system{system_} {
@@ -1128,26 +1134,55 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx
1128} 1134}
1129 1135
1130void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { 1136void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
1131 LOG_DEBUG(Service_AM, "called"); 1137 IPC::RequestParser rp{ctx};
1138 const auto kind = rp.PopEnum<LaunchParameterKind>();
1132 1139
1133 LaunchParameters params{}; 1140 LOG_DEBUG(Service_AM, "called, kind={:08X}", static_cast<u8>(kind));
1134 1141
1135 params.magic = POP_LAUNCH_PARAMETER_MAGIC; 1142 if (kind == LaunchParameterKind::ApplicationSpecific && !launch_popped_application_specific) {
1136 params.is_account_selected = 1; 1143 const auto backend = BCAT::CreateBackendFromSettings(
1144 [this](u64 tid) { return system.GetFileSystemController().GetBCATDirectory(tid); });
1145 const auto build_id_full = system.GetCurrentProcessBuildID();
1146 u64 build_id{};
1147 std::memcpy(&build_id, build_id_full.data(), sizeof(u64));
1137 1148
1138 Account::ProfileManager profile_manager{}; 1149 const auto data =
1139 const auto uuid = profile_manager.GetUser(Settings::values.current_user); 1150 backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id});
1140 ASSERT(uuid);
1141 params.current_user = uuid->uuid;
1142 1151
1143 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1152 if (data.has_value()) {
1153 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1154 rb.Push(RESULT_SUCCESS);
1155 rb.PushIpcInterface<AM::IStorage>(*data);
1156 launch_popped_application_specific = true;
1157 return;
1158 }
1159 } else if (kind == LaunchParameterKind::AccountPreselectedUser &&
1160 !launch_popped_account_preselect) {
1161 LaunchParameterAccountPreselectedUser params{};
1144 1162
1145 rb.Push(RESULT_SUCCESS); 1163 params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC;
1164 params.is_account_selected = 1;
1146 1165
1147 std::vector<u8> buffer(sizeof(LaunchParameters)); 1166 Account::ProfileManager profile_manager{};
1148 std::memcpy(buffer.data(), &params, buffer.size()); 1167 const auto uuid = profile_manager.GetUser(Settings::values.current_user);
1168 ASSERT(uuid);
1169 params.current_user = uuid->uuid;
1149 1170
1150 rb.PushIpcInterface<AM::IStorage>(buffer); 1171 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1172
1173 rb.Push(RESULT_SUCCESS);
1174
1175 std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));
1176 std::memcpy(buffer.data(), &params, buffer.size());
1177
1178 rb.PushIpcInterface<AM::IStorage>(buffer);
1179 launch_popped_account_preselect = true;
1180 return;
1181 }
1182
1183 LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!");
1184 IPC::ResponseBuilder rb{ctx, 2};
1185 rb.Push(ERR_NO_DATA_IN_CHANNEL);
1151} 1186}
1152 1187
1153void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest( 1188void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
@@ -1165,7 +1200,7 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
1165 LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); 1200 LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
1166 1201
1167 FileSys::SaveDataDescriptor descriptor{}; 1202 FileSys::SaveDataDescriptor descriptor{};
1168 descriptor.title_id = Core::CurrentProcess()->GetTitleID(); 1203 descriptor.title_id = system.CurrentProcess()->GetTitleID();
1169 descriptor.user_id = user_id; 1204 descriptor.user_id = user_id;
1170 descriptor.type = FileSys::SaveDataType::SaveData; 1205 descriptor.type = FileSys::SaveDataType::SaveData;
1171 const auto res = system.GetFileSystemController().CreateSaveData( 1206 const auto res = system.GetFileSystemController().CreateSaveData(
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index a3baeb673..ccd053c13 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -147,6 +147,7 @@ private:
147 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); 147 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
148 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); 148 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
149 149
150 Core::System& system;
150 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 151 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
151 Kernel::EventPair launchable_event; 152 Kernel::EventPair launchable_event;
152 Kernel::EventPair accumulated_suspended_tick_changed_event; 153 Kernel::EventPair accumulated_suspended_tick_changed_event;
@@ -154,8 +155,6 @@ private:
154 u32 idle_time_detection_extension = 0; 155 u32 idle_time_detection_extension = 0;
155 u64 num_fatal_sections_entered = 0; 156 u64 num_fatal_sections_entered = 0;
156 bool is_auto_sleep_disabled = false; 157 bool is_auto_sleep_disabled = false;
157
158 Core::System& system;
159}; 158};
160 159
161class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { 160class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
@@ -255,6 +254,8 @@ private:
255 void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx); 254 void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
256 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); 255 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
257 256
257 bool launch_popped_application_specific = false;
258 bool launch_popped_account_preselect = false;
258 Kernel::EventPair gpu_error_detected_event; 259 Kernel::EventPair gpu_error_detected_event;
259 Core::System& system; 260 Core::System& system;
260}; 261};
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index d2e35362f..720fe766f 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -157,6 +157,10 @@ AppletManager::AppletManager(Core::System& system_) : system{system_} {}
157 157
158AppletManager::~AppletManager() = default; 158AppletManager::~AppletManager() = default;
159 159
160const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
161 return frontend;
162}
163
160void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { 164void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
161 if (set.parental_controls != nullptr) 165 if (set.parental_controls != nullptr)
162 frontend.parental_controls = std::move(set.parental_controls); 166 frontend.parental_controls = std::move(set.parental_controls);
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 764c3418c..226be88b1 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -190,6 +190,8 @@ public:
190 explicit AppletManager(Core::System& system_); 190 explicit AppletManager(Core::System& system_);
191 ~AppletManager(); 191 ~AppletManager();
192 192
193 const AppletFrontendSet& GetAppletFrontendSet() const;
194
193 void SetAppletFrontendSet(AppletFrontendSet set); 195 void SetAppletFrontendSet(AppletFrontendSet set);
194 void SetDefaultAppletFrontendSet(); 196 void SetDefaultAppletFrontendSet();
195 void SetDefaultAppletsIfMissing(); 197 void SetDefaultAppletsIfMissing();
diff --git a/src/core/hle/service/apm/controller.cpp b/src/core/hle/service/apm/controller.cpp
index 4376612eb..073d0f6fa 100644
--- a/src/core/hle/service/apm/controller.cpp
+++ b/src/core/hle/service/apm/controller.cpp
@@ -13,7 +13,7 @@ constexpr PerformanceConfiguration DEFAULT_PERFORMANCE_CONFIGURATION =
13 PerformanceConfiguration::Config7; 13 PerformanceConfiguration::Config7;
14 14
15Controller::Controller(Core::Timing::CoreTiming& core_timing) 15Controller::Controller(Core::Timing::CoreTiming& core_timing)
16 : core_timing(core_timing), configs{ 16 : core_timing{core_timing}, configs{
17 {PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION}, 17 {PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION},
18 {PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION}, 18 {PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION},
19 } {} 19 } {}
@@ -63,6 +63,7 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa
63void Controller::SetClockSpeed(u32 mhz) { 63void Controller::SetClockSpeed(u32 mhz) {
64 LOG_INFO(Service_APM, "called, mhz={:08X}", mhz); 64 LOG_INFO(Service_APM, "called, mhz={:08X}", mhz);
65 // TODO(DarkLordZach): Actually signal core_timing to change clock speed. 65 // TODO(DarkLordZach): Actually signal core_timing to change clock speed.
66 // TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used.
66} 67}
67 68
68} // namespace Service::APM 69} // namespace Service::APM
diff --git a/src/core/hle/service/apm/controller.h b/src/core/hle/service/apm/controller.h
index 8ac80eaea..454caa6eb 100644
--- a/src/core/hle/service/apm/controller.h
+++ b/src/core/hle/service/apm/controller.h
@@ -50,7 +50,7 @@ enum class PerformanceMode : u8 {
50// system during times of high load -- this simply maps to different PerformanceConfigs to use. 50// system during times of high load -- this simply maps to different PerformanceConfigs to use.
51class Controller { 51class Controller {
52public: 52public:
53 Controller(Core::Timing::CoreTiming& core_timing); 53 explicit Controller(Core::Timing::CoreTiming& core_timing);
54 ~Controller(); 54 ~Controller();
55 55
56 void SetPerformanceConfiguration(PerformanceMode mode, PerformanceConfiguration config); 56 void SetPerformanceConfiguration(PerformanceMode mode, PerformanceConfiguration config);
@@ -62,9 +62,9 @@ public:
62private: 62private:
63 void SetClockSpeed(u32 mhz); 63 void SetClockSpeed(u32 mhz);
64 64
65 std::map<PerformanceMode, PerformanceConfiguration> configs; 65 [[maybe_unused]] Core::Timing::CoreTiming& core_timing;
66 66
67 Core::Timing::CoreTiming& core_timing; 67 std::map<PerformanceMode, PerformanceConfiguration> configs;
68}; 68};
69 69
70} // namespace Service::APM 70} // namespace Service::APM
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index fb84a8f13..9afefb5c6 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -205,7 +205,7 @@ private:
205 AudioCore::StreamPtr stream; 205 AudioCore::StreamPtr stream;
206 std::string device_name; 206 std::string device_name;
207 207
208 AudoutParams audio_params{}; 208 [[maybe_unused]] AudoutParams audio_params {};
209 209
210 /// This is the event handle used to check if the audio buffer was released 210 /// This is the event handle used to check if the audio buffer was released
211 Kernel::EventPair buffer_event; 211 Kernel::EventPair buffer_event;
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp
new file mode 100644
index 000000000..9d6946bc5
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/backend.cpp
@@ -0,0 +1,137 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/hex_util.h"
6#include "common/logging/log.h"
7#include "core/core.h"
8#include "core/hle/lock.h"
9#include "core/hle/service/bcat/backend/backend.h"
10
11namespace Service::BCAT {
12
13ProgressServiceBackend::ProgressServiceBackend(std::string_view event_name) {
14 auto& kernel{Core::System::GetInstance().Kernel()};
15 event = Kernel::WritableEvent::CreateEventPair(
16 kernel, Kernel::ResetType::Automatic,
17 std::string("ProgressServiceBackend:UpdateEvent:").append(event_name));
18}
19
20Kernel::SharedPtr<Kernel::ReadableEvent> ProgressServiceBackend::GetEvent() const {
21 return event.readable;
22}
23
24DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() {
25 return impl;
26}
27
28void ProgressServiceBackend::SetNeedHLELock(bool need) {
29 need_hle_lock = need;
30}
31
32void ProgressServiceBackend::SetTotalSize(u64 size) {
33 impl.total_bytes = size;
34 SignalUpdate();
35}
36
37void ProgressServiceBackend::StartConnecting() {
38 impl.status = DeliveryCacheProgressImpl::Status::Connecting;
39 SignalUpdate();
40}
41
42void ProgressServiceBackend::StartProcessingDataList() {
43 impl.status = DeliveryCacheProgressImpl::Status::ProcessingDataList;
44 SignalUpdate();
45}
46
47void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name,
48 std::string_view file_name, u64 file_size) {
49 impl.status = DeliveryCacheProgressImpl::Status::Downloading;
50 impl.current_downloaded_bytes = 0;
51 impl.current_total_bytes = file_size;
52 std::memcpy(impl.current_directory.data(), dir_name.data(),
53 std::min<u64>(dir_name.size(), 0x31ull));
54 std::memcpy(impl.current_file.data(), file_name.data(),
55 std::min<u64>(file_name.size(), 0x31ull));
56 SignalUpdate();
57}
58
59void ProgressServiceBackend::UpdateFileProgress(u64 downloaded) {
60 impl.current_downloaded_bytes = downloaded;
61 SignalUpdate();
62}
63
64void ProgressServiceBackend::FinishDownloadingFile() {
65 impl.total_downloaded_bytes += impl.current_total_bytes;
66 SignalUpdate();
67}
68
69void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) {
70 impl.status = DeliveryCacheProgressImpl::Status::Committing;
71 impl.current_file.fill(0);
72 impl.current_downloaded_bytes = 0;
73 impl.current_total_bytes = 0;
74 std::memcpy(impl.current_directory.data(), dir_name.data(),
75 std::min<u64>(dir_name.size(), 0x31ull));
76 SignalUpdate();
77}
78
79void ProgressServiceBackend::FinishDownload(ResultCode result) {
80 impl.total_downloaded_bytes = impl.total_bytes;
81 impl.status = DeliveryCacheProgressImpl::Status::Done;
82 impl.result = result;
83 SignalUpdate();
84}
85
86void ProgressServiceBackend::SignalUpdate() const {
87 if (need_hle_lock) {
88 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
89 event.writable->Signal();
90 } else {
91 event.writable->Signal();
92 }
93}
94
95Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
96
97Backend::~Backend() = default;
98
99NullBackend::NullBackend(DirectoryGetter getter) : Backend(std::move(getter)) {}
100
101NullBackend::~NullBackend() = default;
102
103bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
104 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
105 title.build_id);
106
107 progress.FinishDownload(RESULT_SUCCESS);
108 return true;
109}
110
111bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
112 ProgressServiceBackend& progress) {
113 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id,
114 title.build_id, name);
115
116 progress.FinishDownload(RESULT_SUCCESS);
117 return true;
118}
119
120bool NullBackend::Clear(u64 title_id) {
121 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}");
122
123 return true;
124}
125
126void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
127 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase = {}", title_id,
128 Common::HexToString(passphrase));
129}
130
131std::optional<std::vector<u8>> NullBackend::GetLaunchParameter(TitleIDVersion title) {
132 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
133 title.build_id);
134 return std::nullopt;
135}
136
137} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h
new file mode 100644
index 000000000..51dbd3316
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/backend.h
@@ -0,0 +1,150 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <optional>
9#include <string>
10#include <string_view>
11
12#include "common/common_types.h"
13#include "core/file_sys/vfs_types.h"
14#include "core/hle/kernel/readable_event.h"
15#include "core/hle/kernel/writable_event.h"
16#include "core/hle/result.h"
17
18namespace Service::BCAT {
19
20struct DeliveryCacheProgressImpl;
21
22using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>;
23using Passphrase = std::array<u8, 0x20>;
24
25struct TitleIDVersion {
26 u64 title_id;
27 u64 build_id;
28};
29
30using DirectoryName = std::array<char, 0x20>;
31using FileName = std::array<char, 0x20>;
32
33struct DeliveryCacheProgressImpl {
34 enum class Status : s32 {
35 None = 0x0,
36 Queued = 0x1,
37 Connecting = 0x2,
38 ProcessingDataList = 0x3,
39 Downloading = 0x4,
40 Committing = 0x5,
41 Done = 0x9,
42 };
43
44 Status status;
45 ResultCode result = RESULT_SUCCESS;
46 DirectoryName current_directory;
47 FileName current_file;
48 s64 current_downloaded_bytes; ///< Bytes downloaded on current file.
49 s64 current_total_bytes; ///< Bytes total on current file.
50 s64 total_downloaded_bytes; ///< Bytes downloaded on overall download.
51 s64 total_bytes; ///< Bytes total on overall download.
52 INSERT_PADDING_BYTES(
53 0x198); ///< Appears to be unused in official code, possibly reserved for future use.
54};
55static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200,
56 "DeliveryCacheProgressImpl has incorrect size.");
57
58// A class to manage the signalling to the game about BCAT download progress.
59// Some of this class is implemented in module.cpp to avoid exposing the implementation structure.
60class ProgressServiceBackend {
61 friend class IBcatService;
62
63public:
64 // Clients should call this with true if any of the functions are going to be called from a
65 // non-HLE thread and this class need to lock the hle mutex. (default is false)
66 void SetNeedHLELock(bool need);
67
68 // Sets the number of bytes total in the entire download.
69 void SetTotalSize(u64 size);
70
71 // Notifies the application that the backend has started connecting to the server.
72 void StartConnecting();
73 // Notifies the application that the backend has begun accumulating and processing metadata.
74 void StartProcessingDataList();
75
76 // Notifies the application that a file is starting to be downloaded.
77 void StartDownloadingFile(std::string_view dir_name, std::string_view file_name, u64 file_size);
78 // Updates the progress of the current file to the size passed.
79 void UpdateFileProgress(u64 downloaded);
80 // Notifies the application that the current file has completed download.
81 void FinishDownloadingFile();
82
83 // Notifies the application that all files in this directory have completed and are being
84 // finalized.
85 void CommitDirectory(std::string_view dir_name);
86
87 // Notifies the application that the operation completed with result code result.
88 void FinishDownload(ResultCode result);
89
90private:
91 explicit ProgressServiceBackend(std::string_view event_name);
92
93 Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent() const;
94 DeliveryCacheProgressImpl& GetImpl();
95
96 void SignalUpdate() const;
97
98 DeliveryCacheProgressImpl impl{};
99 Kernel::EventPair event;
100 bool need_hle_lock = false;
101};
102
103// A class representing an abstract backend for BCAT functionality.
104class Backend {
105public:
106 explicit Backend(DirectoryGetter getter);
107 virtual ~Backend();
108
109 // Called when the backend is needed to synchronize the data for the game with title ID and
110 // version in title. A ProgressServiceBackend object is provided to alert the application of
111 // status.
112 virtual bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) = 0;
113 // Very similar to Synchronize, but only for the directory provided. Backends should not alter
114 // the data for any other directories.
115 virtual bool SynchronizeDirectory(TitleIDVersion title, std::string name,
116 ProgressServiceBackend& progress) = 0;
117
118 // Removes all cached data associated with title id provided.
119 virtual bool Clear(u64 title_id) = 0;
120
121 // Sets the BCAT Passphrase to be used with the associated title ID.
122 virtual void SetPassphrase(u64 title_id, const Passphrase& passphrase) = 0;
123
124 // Gets the launch parameter used by AM associated with the title ID and version provided.
125 virtual std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) = 0;
126
127protected:
128 DirectoryGetter dir_getter;
129};
130
131// A backend of BCAT that provides no operation.
132class NullBackend : public Backend {
133public:
134 explicit NullBackend(DirectoryGetter getter);
135 ~NullBackend() override;
136
137 bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
138 bool SynchronizeDirectory(TitleIDVersion title, std::string name,
139 ProgressServiceBackend& progress) override;
140
141 bool Clear(u64 title_id) override;
142
143 void SetPassphrase(u64 title_id, const Passphrase& passphrase) override;
144
145 std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
146};
147
148std::unique_ptr<Backend> CreateBackendFromSettings(DirectoryGetter getter);
149
150} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp
new file mode 100644
index 000000000..64022982b
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -0,0 +1,504 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <fmt/ostream.h>
6#include <httplib.h>
7#include <json.hpp>
8#include <mbedtls/sha256.h>
9#include "common/hex_util.h"
10#include "common/logging/backend.h"
11#include "common/logging/log.h"
12#include "core/core.h"
13#include "core/file_sys/vfs.h"
14#include "core/file_sys/vfs_libzip.h"
15#include "core/file_sys/vfs_vector.h"
16#include "core/frontend/applets/error.h"
17#include "core/hle/service/am/applets/applets.h"
18#include "core/hle/service/bcat/backend/boxcat.h"
19#include "core/settings.h"
20
21namespace {
22
23// Prevents conflicts with windows macro called CreateFile
24FileSys::VirtualFile VfsCreateFileWrap(FileSys::VirtualDir dir, std::string_view name) {
25 return dir->CreateFile(name);
26}
27
28// Prevents conflicts with windows macro called DeleteFile
29bool VfsDeleteFileWrap(FileSys::VirtualDir dir, std::string_view name) {
30 return dir->DeleteFile(name);
31}
32
33} // Anonymous namespace
34
35namespace Service::BCAT {
36
37constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1};
38
39constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org";
40
41// Formatted using fmt with arg[0] = hex title id
42constexpr char BOXCAT_PATHNAME_DATA[] = "/game-assets/{:016X}/boxcat";
43constexpr char BOXCAT_PATHNAME_LAUNCHPARAM[] = "/game-assets/{:016X}/launchparam";
44
45constexpr char BOXCAT_PATHNAME_EVENTS[] = "/game-assets/boxcat/events";
46
47constexpr char BOXCAT_API_VERSION[] = "1";
48constexpr char BOXCAT_CLIENT_TYPE[] = "yuzu";
49
50// HTTP status codes for Boxcat
51enum class ResponseStatus {
52 Ok = 200, ///< Operation completed successfully.
53 BadClientVersion = 301, ///< The Boxcat-Client-Version doesn't match the server.
54 NoUpdate = 304, ///< The digest provided would match the new data, no need to update.
55 NoMatchTitleId = 404, ///< The title ID provided doesn't have a boxcat implementation.
56 NoMatchBuildId = 406, ///< The build ID provided is blacklisted (potentially because of format
57 ///< issues or whatnot) and has no data.
58};
59
60enum class DownloadResult {
61 Success = 0,
62 NoResponse,
63 GeneralWebError,
64 NoMatchTitleId,
65 NoMatchBuildId,
66 InvalidContentType,
67 GeneralFSError,
68 BadClientVersion,
69};
70
71constexpr std::array<const char*, 8> DOWNLOAD_RESULT_LOG_MESSAGES{
72 "Success",
73 "There was no response from the server.",
74 "There was a general web error code returned from the server.",
75 "The title ID of the current game doesn't have a boxcat implementation. If you believe an "
76 "implementation should be added, contact yuzu support.",
77 "The build ID of the current version of the game is marked as incompatible with the current "
78 "BCAT distribution. Try upgrading or downgrading your game version or contacting yuzu support.",
79 "The content type of the web response was invalid.",
80 "There was a general filesystem error while saving the zip file.",
81 "The server is either too new or too old to serve the request. Try using the latest version of "
82 "an official release of yuzu.",
83};
84
85std::ostream& operator<<(std::ostream& os, DownloadResult result) {
86 return os << DOWNLOAD_RESULT_LOG_MESSAGES.at(static_cast<std::size_t>(result));
87}
88
89constexpr u32 PORT = 443;
90constexpr u32 TIMEOUT_SECONDS = 30;
91[[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB
92
93namespace {
94
95std::string GetBINFilePath(u64 title_id) {
96 return fmt::format("{}bcat/{:016X}/launchparam.bin",
97 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id);
98}
99
100std::string GetZIPFilePath(u64 title_id) {
101 return fmt::format("{}bcat/{:016X}/data.zip",
102 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id);
103}
104
105// If the error is something the user should know about (build ID mismatch, bad client version),
106// display an error.
107void HandleDownloadDisplayResult(DownloadResult res) {
108 if (res == DownloadResult::Success || res == DownloadResult::NoResponse ||
109 res == DownloadResult::GeneralWebError || res == DownloadResult::GeneralFSError ||
110 res == DownloadResult::NoMatchTitleId || res == DownloadResult::InvalidContentType) {
111 return;
112 }
113
114 const auto& frontend{Core::System::GetInstance().GetAppletManager().GetAppletFrontendSet()};
115 frontend.error->ShowCustomErrorText(
116 ResultCode(-1), "There was an error while attempting to use Boxcat.",
117 DOWNLOAD_RESULT_LOG_MESSAGES[static_cast<std::size_t>(res)], [] {});
118}
119
120bool VfsRawCopyProgress(FileSys::VirtualFile src, FileSys::VirtualFile dest,
121 std::string_view dir_name, ProgressServiceBackend& progress,
122 std::size_t block_size = 0x1000) {
123 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
124 return false;
125 if (!dest->Resize(src->GetSize()))
126 return false;
127
128 progress.StartDownloadingFile(dir_name, src->GetName(), src->GetSize());
129
130 std::vector<u8> temp(std::min(block_size, src->GetSize()));
131 for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
132 const auto read = std::min(block_size, src->GetSize() - i);
133
134 if (src->Read(temp.data(), read, i) != read) {
135 return false;
136 }
137
138 if (dest->Write(temp.data(), read, i) != read) {
139 return false;
140 }
141
142 progress.UpdateFileProgress(i);
143 }
144
145 progress.FinishDownloadingFile();
146
147 return true;
148}
149
150bool VfsRawCopyDProgressSingle(FileSys::VirtualDir src, FileSys::VirtualDir dest,
151 ProgressServiceBackend& progress, std::size_t block_size = 0x1000) {
152 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
153 return false;
154
155 for (const auto& file : src->GetFiles()) {
156 const auto out_file = VfsCreateFileWrap(dest, file->GetName());
157 if (!VfsRawCopyProgress(file, out_file, src->GetName(), progress, block_size)) {
158 return false;
159 }
160 }
161 progress.CommitDirectory(src->GetName());
162
163 return true;
164}
165
166bool VfsRawCopyDProgress(FileSys::VirtualDir src, FileSys::VirtualDir dest,
167 ProgressServiceBackend& progress, std::size_t block_size = 0x1000) {
168 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
169 return false;
170
171 for (const auto& dir : src->GetSubdirectories()) {
172 const auto out = dest->CreateSubdirectory(dir->GetName());
173 if (!VfsRawCopyDProgressSingle(dir, out, progress, block_size)) {
174 return false;
175 }
176 }
177
178 return true;
179}
180
181} // Anonymous namespace
182
183class Boxcat::Client {
184public:
185 Client(std::string path, u64 title_id, u64 build_id)
186 : path(std::move(path)), title_id(title_id), build_id(build_id) {}
187
188 DownloadResult DownloadDataZip() {
189 return DownloadInternal(fmt::format(BOXCAT_PATHNAME_DATA, title_id), TIMEOUT_SECONDS,
190 "application/zip");
191 }
192
193 DownloadResult DownloadLaunchParam() {
194 return DownloadInternal(fmt::format(BOXCAT_PATHNAME_LAUNCHPARAM, title_id),
195 TIMEOUT_SECONDS / 3, "application/octet-stream");
196 }
197
198private:
199 DownloadResult DownloadInternal(const std::string& resolved_path, u32 timeout_seconds,
200 const std::string& content_type_name) {
201 if (client == nullptr) {
202 client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT, timeout_seconds);
203 }
204
205 httplib::Headers headers{
206 {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
207 {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)},
208 {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)},
209 };
210
211 if (FileUtil::Exists(path)) {
212 FileUtil::IOFile file{path, "rb"};
213 if (file.IsOpen()) {
214 std::vector<u8> bytes(file.GetSize());
215 file.ReadBytes(bytes.data(), bytes.size());
216 const auto digest = DigestFile(bytes);
217 headers.insert({std::string("If-None-Match"), Common::HexToString(digest, false)});
218 }
219 }
220
221 const auto response = client->Get(resolved_path.c_str(), headers);
222 if (response == nullptr)
223 return DownloadResult::NoResponse;
224
225 if (response->status == static_cast<int>(ResponseStatus::NoUpdate))
226 return DownloadResult::Success;
227 if (response->status == static_cast<int>(ResponseStatus::BadClientVersion))
228 return DownloadResult::BadClientVersion;
229 if (response->status == static_cast<int>(ResponseStatus::NoMatchTitleId))
230 return DownloadResult::NoMatchTitleId;
231 if (response->status == static_cast<int>(ResponseStatus::NoMatchBuildId))
232 return DownloadResult::NoMatchBuildId;
233 if (response->status != static_cast<int>(ResponseStatus::Ok))
234 return DownloadResult::GeneralWebError;
235
236 const auto content_type = response->headers.find("content-type");
237 if (content_type == response->headers.end() ||
238 content_type->second.find(content_type_name) == std::string::npos) {
239 return DownloadResult::InvalidContentType;
240 }
241
242 FileUtil::CreateFullPath(path);
243 FileUtil::IOFile file{path, "wb"};
244 if (!file.IsOpen())
245 return DownloadResult::GeneralFSError;
246 if (!file.Resize(response->body.size()))
247 return DownloadResult::GeneralFSError;
248 if (file.WriteBytes(response->body.data(), response->body.size()) != response->body.size())
249 return DownloadResult::GeneralFSError;
250
251 return DownloadResult::Success;
252 }
253
254 using Digest = std::array<u8, 0x20>;
255 static Digest DigestFile(std::vector<u8> bytes) {
256 Digest out{};
257 mbedtls_sha256(bytes.data(), bytes.size(), out.data(), 0);
258 return out;
259 }
260
261 std::unique_ptr<httplib::Client> client;
262 std::string path;
263 u64 title_id;
264 u64 build_id;
265};
266
267Boxcat::Boxcat(DirectoryGetter getter) : Backend(std::move(getter)) {}
268
269Boxcat::~Boxcat() = default;
270
271void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title,
272 ProgressServiceBackend& progress,
273 std::optional<std::string> dir_name = {}) {
274 progress.SetNeedHLELock(true);
275
276 if (Settings::values.bcat_boxcat_local) {
277 LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download.");
278 const auto dir = dir_getter(title.title_id);
279 if (dir)
280 progress.SetTotalSize(dir->GetSize());
281 progress.FinishDownload(RESULT_SUCCESS);
282 return;
283 }
284
285 const auto zip_path{GetZIPFilePath(title.title_id)};
286 Boxcat::Client client{zip_path, title.title_id, title.build_id};
287
288 progress.StartConnecting();
289
290 const auto res = client.DownloadDataZip();
291 if (res != DownloadResult::Success) {
292 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
293
294 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
295 FileUtil::Delete(zip_path);
296 }
297
298 HandleDownloadDisplayResult(res);
299 progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
300 return;
301 }
302
303 progress.StartProcessingDataList();
304
305 FileUtil::IOFile zip{zip_path, "rb"};
306 const auto size = zip.GetSize();
307 std::vector<u8> bytes(size);
308 if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
309 LOG_ERROR(Service_BCAT, "Boxcat failed to read ZIP file at path '{}'!", zip_path);
310 progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
311 return;
312 }
313
314 const auto extracted = FileSys::ExtractZIP(std::make_shared<FileSys::VectorVfsFile>(bytes));
315 if (extracted == nullptr) {
316 LOG_ERROR(Service_BCAT, "Boxcat failed to extract ZIP file!");
317 progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
318 return;
319 }
320
321 if (dir_name == std::nullopt) {
322 progress.SetTotalSize(extracted->GetSize());
323
324 const auto target_dir = dir_getter(title.title_id);
325 if (target_dir == nullptr || !VfsRawCopyDProgress(extracted, target_dir, progress)) {
326 LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!");
327 progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
328 return;
329 }
330 } else {
331 const auto target_dir = dir_getter(title.title_id);
332 if (target_dir == nullptr) {
333 LOG_ERROR(Service_BCAT, "Boxcat failed to get directory for title ID!");
334 progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
335 return;
336 }
337
338 const auto target_sub = target_dir->GetSubdirectory(*dir_name);
339 const auto source_sub = extracted->GetSubdirectory(*dir_name);
340
341 progress.SetTotalSize(source_sub->GetSize());
342
343 std::vector<std::string> filenames;
344 {
345 const auto files = target_sub->GetFiles();
346 std::transform(files.begin(), files.end(), std::back_inserter(filenames),
347 [](const auto& vfile) { return vfile->GetName(); });
348 }
349
350 for (const auto& filename : filenames) {
351 VfsDeleteFileWrap(target_sub, filename);
352 }
353
354 if (target_sub == nullptr || source_sub == nullptr ||
355 !VfsRawCopyDProgressSingle(source_sub, target_sub, progress)) {
356 LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!");
357 progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
358 return;
359 }
360 }
361
362 progress.FinishDownload(RESULT_SUCCESS);
363}
364
365bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
366 is_syncing.exchange(true);
367 std::thread([this, title, &progress] { SynchronizeInternal(dir_getter, title, progress); })
368 .detach();
369 return true;
370}
371
372bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name,
373 ProgressServiceBackend& progress) {
374 is_syncing.exchange(true);
375 std::thread(
376 [this, title, name, &progress] { SynchronizeInternal(dir_getter, title, progress, name); })
377 .detach();
378 return true;
379}
380
381bool Boxcat::Clear(u64 title_id) {
382 if (Settings::values.bcat_boxcat_local) {
383 LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping clear.");
384 return true;
385 }
386
387 const auto dir = dir_getter(title_id);
388
389 std::vector<std::string> dirnames;
390
391 for (const auto& subdir : dir->GetSubdirectories())
392 dirnames.push_back(subdir->GetName());
393
394 for (const auto& subdir : dirnames) {
395 if (!dir->DeleteSubdirectoryRecursive(subdir))
396 return false;
397 }
398
399 return true;
400}
401
402void Boxcat::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
403 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
404 Common::HexToString(passphrase));
405}
406
407std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title) {
408 const auto path{GetBINFilePath(title.title_id)};
409
410 if (Settings::values.bcat_boxcat_local) {
411 LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download.");
412 } else {
413 Boxcat::Client client{path, title.title_id, title.build_id};
414
415 const auto res = client.DownloadLaunchParam();
416 if (res != DownloadResult::Success) {
417 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
418
419 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
420 FileUtil::Delete(path);
421 }
422
423 HandleDownloadDisplayResult(res);
424 return std::nullopt;
425 }
426 }
427
428 FileUtil::IOFile bin{path, "rb"};
429 const auto size = bin.GetSize();
430 std::vector<u8> bytes(size);
431 if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
432 LOG_ERROR(Service_BCAT, "Boxcat failed to read launch parameter binary at path '{}'!",
433 path);
434 return std::nullopt;
435 }
436
437 return bytes;
438}
439
440Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global,
441 std::map<std::string, EventStatus>& games) {
442 httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT),
443 static_cast<int>(TIMEOUT_SECONDS)};
444
445 httplib::Headers headers{
446 {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
447 {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)},
448 };
449
450 const auto response = client.Get(BOXCAT_PATHNAME_EVENTS, headers);
451 if (response == nullptr)
452 return StatusResult::Offline;
453
454 if (response->status == static_cast<int>(ResponseStatus::BadClientVersion))
455 return StatusResult::BadClientVersion;
456
457 try {
458 nlohmann::json json = nlohmann::json::parse(response->body);
459
460 if (!json["online"].get<bool>())
461 return StatusResult::Offline;
462
463 if (json["global"].is_null())
464 global = std::nullopt;
465 else
466 global = json["global"].get<std::string>();
467
468 if (json["games"].is_array()) {
469 for (const auto object : json["games"]) {
470 if (object.is_object() && object.find("name") != object.end()) {
471 EventStatus detail{};
472 if (object["header"].is_string()) {
473 detail.header = object["header"].get<std::string>();
474 } else {
475 detail.header = std::nullopt;
476 }
477
478 if (object["footer"].is_string()) {
479 detail.footer = object["footer"].get<std::string>();
480 } else {
481 detail.footer = std::nullopt;
482 }
483
484 if (object["events"].is_array()) {
485 for (const auto& event : object["events"]) {
486 if (!event.is_string())
487 continue;
488 detail.events.push_back(event.get<std::string>());
489 }
490 }
491
492 games.insert_or_assign(object["name"], std::move(detail));
493 }
494 }
495 }
496
497 return StatusResult::Success;
498 } catch (const nlohmann::json::parse_error& error) {
499 LOG_ERROR(Service_BCAT, "{}", error.what());
500 return StatusResult::ParseError;
501 }
502}
503
504} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/backend/boxcat.h b/src/core/hle/service/bcat/backend/boxcat.h
new file mode 100644
index 000000000..601151189
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/boxcat.h
@@ -0,0 +1,58 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <atomic>
8#include <map>
9#include <optional>
10#include "core/hle/service/bcat/backend/backend.h"
11
12namespace Service::BCAT {
13
14struct EventStatus {
15 std::optional<std::string> header;
16 std::optional<std::string> footer;
17 std::vector<std::string> events;
18};
19
20/// Boxcat is yuzu's custom backend implementation of Nintendo's BCAT service. It is free to use and
21/// doesn't require a switch or nintendo account. The content is controlled by the yuzu team.
22class Boxcat final : public Backend {
23 friend void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title,
24 ProgressServiceBackend& progress,
25 std::optional<std::string> dir_name);
26
27public:
28 explicit Boxcat(DirectoryGetter getter);
29 ~Boxcat() override;
30
31 bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
32 bool SynchronizeDirectory(TitleIDVersion title, std::string name,
33 ProgressServiceBackend& progress) override;
34
35 bool Clear(u64 title_id) override;
36
37 void SetPassphrase(u64 title_id, const Passphrase& passphrase) override;
38
39 std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
40
41 enum class StatusResult {
42 Success,
43 Offline,
44 ParseError,
45 BadClientVersion,
46 };
47
48 static StatusResult GetStatus(std::optional<std::string>& global,
49 std::map<std::string, EventStatus>& games);
50
51private:
52 std::atomic_bool is_syncing{false};
53
54 class Client;
55 std::unique_ptr<Client> client;
56};
57
58} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp
index 179aa4949..8bb2528c9 100644
--- a/src/core/hle/service/bcat/bcat.cpp
+++ b/src/core/hle/service/bcat/bcat.cpp
@@ -6,11 +6,16 @@
6 6
7namespace Service::BCAT { 7namespace Service::BCAT {
8 8
9BCAT::BCAT(std::shared_ptr<Module> module, const char* name) 9BCAT::BCAT(Core::System& system, std::shared_ptr<Module> module,
10 : Module::Interface(std::move(module), name) { 10 FileSystem::FileSystemController& fsc, const char* name)
11 : Interface(system, std::move(module), fsc, name) {
12 // clang-format off
11 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
12 {0, &BCAT::CreateBcatService, "CreateBcatService"}, 14 {0, &BCAT::CreateBcatService, "CreateBcatService"},
15 {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"},
16 {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"},
13 }; 17 };
18 // clang-format on
14 RegisterHandlers(functions); 19 RegisterHandlers(functions);
15} 20}
16 21
diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h
index 802bd689a..6354465fc 100644
--- a/src/core/hle/service/bcat/bcat.h
+++ b/src/core/hle/service/bcat/bcat.h
@@ -6,11 +6,16 @@
6 6
7#include "core/hle/service/bcat/module.h" 7#include "core/hle/service/bcat/module.h"
8 8
9namespace Core {
10class System;
11}
12
9namespace Service::BCAT { 13namespace Service::BCAT {
10 14
11class BCAT final : public Module::Interface { 15class BCAT final : public Module::Interface {
12public: 16public:
13 explicit BCAT(std::shared_ptr<Module> module, const char* name); 17 explicit BCAT(Core::System& system, std::shared_ptr<Module> module,
18 FileSystem::FileSystemController& fsc, const char* name);
14 ~BCAT() override; 19 ~BCAT() override;
15}; 20};
16 21
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp
index b7bd738fc..4e4aa758b 100644
--- a/src/core/hle/service/bcat/module.cpp
+++ b/src/core/hle/service/bcat/module.cpp
@@ -2,34 +2,257 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cctype>
6#include <mbedtls/md5.h>
7#include "backend/boxcat.h"
8#include "common/hex_util.h"
5#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/string_util.h"
11#include "core/file_sys/vfs.h"
6#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/readable_event.h"
15#include "core/hle/kernel/writable_event.h"
16#include "core/hle/service/bcat/backend/backend.h"
7#include "core/hle/service/bcat/bcat.h" 17#include "core/hle/service/bcat/bcat.h"
8#include "core/hle/service/bcat/module.h" 18#include "core/hle/service/bcat/module.h"
19#include "core/hle/service/filesystem/filesystem.h"
20#include "core/settings.h"
9 21
10namespace Service::BCAT { 22namespace Service::BCAT {
11 23
24constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1};
25constexpr ResultCode ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2};
26constexpr ResultCode ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6};
27constexpr ResultCode ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7};
28
29// The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files
30// and if any of them have a non-zero result it just forwards that result. This is the FS error code
31// for permission denied, which is the closest approximation of this scenario.
32constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400};
33
34using BCATDigest = std::array<u8, 0x10>;
35
36namespace {
37
38u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
39 u64 out{};
40 std::memcpy(&out, id.data(), sizeof(u64));
41 return out;
42}
43
44// The digest is only used to determine if a file is unique compared to others of the same name.
45// Since the algorithm isn't ever checked in game, MD5 is safe.
46BCATDigest DigestFile(const FileSys::VirtualFile& file) {
47 BCATDigest out{};
48 const auto bytes = file->ReadAllBytes();
49 mbedtls_md5(bytes.data(), bytes.size(), out.data());
50 return out;
51}
52
53// For a name to be valid it must be non-empty, must have a null terminating character as the final
54// char, can only contain numbers, letters, underscores and a hyphen if directory and a period if
55// file.
56bool VerifyNameValidInternal(Kernel::HLERequestContext& ctx, std::array<char, 0x20> name,
57 char match_char) {
58 const auto null_chars = std::count(name.begin(), name.end(), 0);
59 const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) {
60 return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0';
61 });
62 if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') {
63 LOG_ERROR(Service_BCAT, "Name passed was invalid!");
64 IPC::ResponseBuilder rb{ctx, 2};
65 rb.Push(ERROR_INVALID_ARGUMENT);
66 return false;
67 }
68
69 return true;
70}
71
72bool VerifyNameValidDir(Kernel::HLERequestContext& ctx, DirectoryName name) {
73 return VerifyNameValidInternal(ctx, name, '-');
74}
75
76bool VerifyNameValidFile(Kernel::HLERequestContext& ctx, FileName name) {
77 return VerifyNameValidInternal(ctx, name, '.');
78}
79
80} // Anonymous namespace
81
82struct DeliveryCacheDirectoryEntry {
83 FileName name;
84 u64 size;
85 BCATDigest digest;
86};
87
88class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> {
89public:
90 IDeliveryCacheProgressService(Kernel::SharedPtr<Kernel::ReadableEvent> event,
91 const DeliveryCacheProgressImpl& impl)
92 : ServiceFramework{"IDeliveryCacheProgressService"}, event(std::move(event)), impl(impl) {
93 // clang-format off
94 static const FunctionInfo functions[] = {
95 {0, &IDeliveryCacheProgressService::GetEvent, "GetEvent"},
96 {1, &IDeliveryCacheProgressService::GetImpl, "GetImpl"},
97 };
98 // clang-format on
99
100 RegisterHandlers(functions);
101 }
102
103private:
104 void GetEvent(Kernel::HLERequestContext& ctx) {
105 LOG_DEBUG(Service_BCAT, "called");
106
107 IPC::ResponseBuilder rb{ctx, 2, 1};
108 rb.Push(RESULT_SUCCESS);
109 rb.PushCopyObjects(event);
110 }
111
112 void GetImpl(Kernel::HLERequestContext& ctx) {
113 LOG_DEBUG(Service_BCAT, "called");
114
115 ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl));
116
117 IPC::ResponseBuilder rb{ctx, 2};
118 rb.Push(RESULT_SUCCESS);
119 }
120
121 Kernel::SharedPtr<Kernel::ReadableEvent> event;
122 const DeliveryCacheProgressImpl& impl;
123};
124
12class IBcatService final : public ServiceFramework<IBcatService> { 125class IBcatService final : public ServiceFramework<IBcatService> {
13public: 126public:
14 IBcatService() : ServiceFramework("IBcatService") { 127 explicit IBcatService(Core::System& system_, Backend& backend_)
128 : ServiceFramework("IBcatService"), system{system_}, backend{backend_} {
129 // clang-format off
15 static const FunctionInfo functions[] = { 130 static const FunctionInfo functions[] = {
16 {10100, nullptr, "RequestSyncDeliveryCache"}, 131 {10100, &IBcatService::RequestSyncDeliveryCache, "RequestSyncDeliveryCache"},
17 {10101, nullptr, "RequestSyncDeliveryCacheWithDirectoryName"}, 132 {10101, &IBcatService::RequestSyncDeliveryCacheWithDirectoryName, "RequestSyncDeliveryCacheWithDirectoryName"},
18 {10200, nullptr, "CancelSyncDeliveryCacheRequest"}, 133 {10200, nullptr, "CancelSyncDeliveryCacheRequest"},
19 {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"}, 134 {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"},
20 {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"}, 135 {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"},
21 {30100, nullptr, "SetPassphrase"}, 136 {30100, &IBcatService::SetPassphrase, "SetPassphrase"},
22 {30200, nullptr, "RegisterBackgroundDeliveryTask"}, 137 {30200, nullptr, "RegisterBackgroundDeliveryTask"},
23 {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, 138 {30201, nullptr, "UnregisterBackgroundDeliveryTask"},
24 {30202, nullptr, "BlockDeliveryTask"}, 139 {30202, nullptr, "BlockDeliveryTask"},
25 {30203, nullptr, "UnblockDeliveryTask"}, 140 {30203, nullptr, "UnblockDeliveryTask"},
26 {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, 141 {90100, nullptr, "EnumerateBackgroundDeliveryTask"},
27 {90200, nullptr, "GetDeliveryList"}, 142 {90200, nullptr, "GetDeliveryList"},
28 {90201, nullptr, "ClearDeliveryCacheStorage"}, 143 {90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"},
29 {90300, nullptr, "GetPushNotificationLog"}, 144 {90300, nullptr, "GetPushNotificationLog"},
30 }; 145 };
146 // clang-format on
31 RegisterHandlers(functions); 147 RegisterHandlers(functions);
32 } 148 }
149
150private:
151 enum class SyncType {
152 Normal,
153 Directory,
154 Count,
155 };
156
157 std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) {
158 auto& backend{progress.at(static_cast<std::size_t>(type))};
159 return std::make_shared<IDeliveryCacheProgressService>(backend.GetEvent(),
160 backend.GetImpl());
161 }
162
163 void RequestSyncDeliveryCache(Kernel::HLERequestContext& ctx) {
164 LOG_DEBUG(Service_BCAT, "called");
165
166 backend.Synchronize({system.CurrentProcess()->GetTitleID(),
167 GetCurrentBuildID(system.GetCurrentProcessBuildID())},
168 progress.at(static_cast<std::size_t>(SyncType::Normal)));
169
170 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
171 rb.Push(RESULT_SUCCESS);
172 rb.PushIpcInterface(CreateProgressService(SyncType::Normal));
173 }
174
175 void RequestSyncDeliveryCacheWithDirectoryName(Kernel::HLERequestContext& ctx) {
176 IPC::RequestParser rp{ctx};
177 const auto name_raw = rp.PopRaw<DirectoryName>();
178 const auto name =
179 Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
180
181 LOG_DEBUG(Service_BCAT, "called, name={}", name);
182
183 backend.SynchronizeDirectory({system.CurrentProcess()->GetTitleID(),
184 GetCurrentBuildID(system.GetCurrentProcessBuildID())},
185 name,
186 progress.at(static_cast<std::size_t>(SyncType::Directory)));
187
188 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
189 rb.Push(RESULT_SUCCESS);
190 rb.PushIpcInterface(CreateProgressService(SyncType::Directory));
191 }
192
193 void SetPassphrase(Kernel::HLERequestContext& ctx) {
194 IPC::RequestParser rp{ctx};
195 const auto title_id = rp.PopRaw<u64>();
196
197 const auto passphrase_raw = ctx.ReadBuffer();
198
199 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
200 Common::HexToString(passphrase_raw));
201
202 if (title_id == 0) {
203 LOG_ERROR(Service_BCAT, "Invalid title ID!");
204 IPC::ResponseBuilder rb{ctx, 2};
205 rb.Push(ERROR_INVALID_ARGUMENT);
206 }
207
208 if (passphrase_raw.size() > 0x40) {
209 LOG_ERROR(Service_BCAT, "Passphrase too large!");
210 IPC::ResponseBuilder rb{ctx, 2};
211 rb.Push(ERROR_INVALID_ARGUMENT);
212 return;
213 }
214
215 Passphrase passphrase{};
216 std::memcpy(passphrase.data(), passphrase_raw.data(),
217 std::min(passphrase.size(), passphrase_raw.size()));
218
219 backend.SetPassphrase(title_id, passphrase);
220
221 IPC::ResponseBuilder rb{ctx, 2};
222 rb.Push(RESULT_SUCCESS);
223 }
224
225 void ClearDeliveryCacheStorage(Kernel::HLERequestContext& ctx) {
226 IPC::RequestParser rp{ctx};
227 const auto title_id = rp.PopRaw<u64>();
228
229 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
230
231 if (title_id == 0) {
232 LOG_ERROR(Service_BCAT, "Invalid title ID!");
233 IPC::ResponseBuilder rb{ctx, 2};
234 rb.Push(ERROR_INVALID_ARGUMENT);
235 return;
236 }
237
238 if (!backend.Clear(title_id)) {
239 LOG_ERROR(Service_BCAT, "Could not clear the directory successfully!");
240 IPC::ResponseBuilder rb{ctx, 2};
241 rb.Push(ERROR_FAILED_CLEAR_CACHE);
242 return;
243 }
244
245 IPC::ResponseBuilder rb{ctx, 2};
246 rb.Push(RESULT_SUCCESS);
247 }
248
249 Core::System& system;
250 Backend& backend;
251
252 std::array<ProgressServiceBackend, static_cast<std::size_t>(SyncType::Count)> progress{
253 ProgressServiceBackend{"Normal"},
254 ProgressServiceBackend{"Directory"},
255 };
33}; 256};
34 257
35void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { 258void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
@@ -37,20 +260,332 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
37 260
38 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 261 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
39 rb.Push(RESULT_SUCCESS); 262 rb.Push(RESULT_SUCCESS);
40 rb.PushIpcInterface<IBcatService>(); 263 rb.PushIpcInterface<IBcatService>(system, *backend);
264}
265
266class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> {
267public:
268 IDeliveryCacheFileService(FileSys::VirtualDir root_)
269 : ServiceFramework{"IDeliveryCacheFileService"}, root(std::move(root_)) {
270 // clang-format off
271 static const FunctionInfo functions[] = {
272 {0, &IDeliveryCacheFileService::Open, "Open"},
273 {1, &IDeliveryCacheFileService::Read, "Read"},
274 {2, &IDeliveryCacheFileService::GetSize, "GetSize"},
275 {3, &IDeliveryCacheFileService::GetDigest, "GetDigest"},
276 };
277 // clang-format on
278
279 RegisterHandlers(functions);
280 }
281
282private:
283 void Open(Kernel::HLERequestContext& ctx) {
284 IPC::RequestParser rp{ctx};
285 const auto dir_name_raw = rp.PopRaw<DirectoryName>();
286 const auto file_name_raw = rp.PopRaw<FileName>();
287
288 const auto dir_name =
289 Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size());
290 const auto file_name =
291 Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size());
292
293 LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name);
294
295 if (!VerifyNameValidDir(ctx, dir_name_raw) || !VerifyNameValidFile(ctx, file_name_raw))
296 return;
297
298 if (current_file != nullptr) {
299 LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!");
300 IPC::ResponseBuilder rb{ctx, 2};
301 rb.Push(ERROR_ENTITY_ALREADY_OPEN);
302 return;
303 }
304
305 const auto dir = root->GetSubdirectory(dir_name);
306
307 if (dir == nullptr) {
308 LOG_ERROR(Service_BCAT, "The directory of name={} couldn't be opened!", dir_name);
309 IPC::ResponseBuilder rb{ctx, 2};
310 rb.Push(ERROR_FAILED_OPEN_ENTITY);
311 return;
312 }
313
314 current_file = dir->GetFile(file_name);
315
316 if (current_file == nullptr) {
317 LOG_ERROR(Service_BCAT, "The file of name={} couldn't be opened!", file_name);
318 IPC::ResponseBuilder rb{ctx, 2};
319 rb.Push(ERROR_FAILED_OPEN_ENTITY);
320 return;
321 }
322
323 IPC::ResponseBuilder rb{ctx, 2};
324 rb.Push(RESULT_SUCCESS);
325 }
326
327 void Read(Kernel::HLERequestContext& ctx) {
328 IPC::RequestParser rp{ctx};
329 const auto offset{rp.PopRaw<u64>()};
330
331 auto size = ctx.GetWriteBufferSize();
332
333 LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, size);
334
335 if (current_file == nullptr) {
336 LOG_ERROR(Service_BCAT, "There is no file currently open!");
337 IPC::ResponseBuilder rb{ctx, 2};
338 rb.Push(ERROR_NO_OPEN_ENTITY);
339 }
340
341 size = std::min<u64>(current_file->GetSize() - offset, size);
342 const auto buffer = current_file->ReadBytes(size, offset);
343 ctx.WriteBuffer(buffer);
344
345 IPC::ResponseBuilder rb{ctx, 4};
346 rb.Push(RESULT_SUCCESS);
347 rb.Push<u64>(buffer.size());
348 }
349
350 void GetSize(Kernel::HLERequestContext& ctx) {
351 LOG_DEBUG(Service_BCAT, "called");
352
353 if (current_file == nullptr) {
354 LOG_ERROR(Service_BCAT, "There is no file currently open!");
355 IPC::ResponseBuilder rb{ctx, 2};
356 rb.Push(ERROR_NO_OPEN_ENTITY);
357 }
358
359 IPC::ResponseBuilder rb{ctx, 4};
360 rb.Push(RESULT_SUCCESS);
361 rb.Push<u64>(current_file->GetSize());
362 }
363
364 void GetDigest(Kernel::HLERequestContext& ctx) {
365 LOG_DEBUG(Service_BCAT, "called");
366
367 if (current_file == nullptr) {
368 LOG_ERROR(Service_BCAT, "There is no file currently open!");
369 IPC::ResponseBuilder rb{ctx, 2};
370 rb.Push(ERROR_NO_OPEN_ENTITY);
371 }
372
373 IPC::ResponseBuilder rb{ctx, 6};
374 rb.Push(RESULT_SUCCESS);
375 rb.PushRaw(DigestFile(current_file));
376 }
377
378 FileSys::VirtualDir root;
379 FileSys::VirtualFile current_file;
380};
381
382class IDeliveryCacheDirectoryService final
383 : public ServiceFramework<IDeliveryCacheDirectoryService> {
384public:
385 IDeliveryCacheDirectoryService(FileSys::VirtualDir root_)
386 : ServiceFramework{"IDeliveryCacheDirectoryService"}, root(std::move(root_)) {
387 // clang-format off
388 static const FunctionInfo functions[] = {
389 {0, &IDeliveryCacheDirectoryService::Open, "Open"},
390 {1, &IDeliveryCacheDirectoryService::Read, "Read"},
391 {2, &IDeliveryCacheDirectoryService::GetCount, "GetCount"},
392 };
393 // clang-format on
394
395 RegisterHandlers(functions);
396 }
397
398private:
399 void Open(Kernel::HLERequestContext& ctx) {
400 IPC::RequestParser rp{ctx};
401 const auto name_raw = rp.PopRaw<DirectoryName>();
402 const auto name =
403 Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
404
405 LOG_DEBUG(Service_BCAT, "called, name={}", name);
406
407 if (!VerifyNameValidDir(ctx, name_raw))
408 return;
409
410 if (current_dir != nullptr) {
411 LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!");
412 IPC::ResponseBuilder rb{ctx, 2};
413 rb.Push(ERROR_ENTITY_ALREADY_OPEN);
414 return;
415 }
416
417 current_dir = root->GetSubdirectory(name);
418
419 if (current_dir == nullptr) {
420 LOG_ERROR(Service_BCAT, "Failed to open the directory name={}!", name);
421 IPC::ResponseBuilder rb{ctx, 2};
422 rb.Push(ERROR_FAILED_OPEN_ENTITY);
423 return;
424 }
425
426 IPC::ResponseBuilder rb{ctx, 2};
427 rb.Push(RESULT_SUCCESS);
428 }
429
430 void Read(Kernel::HLERequestContext& ctx) {
431 auto write_size = ctx.GetWriteBufferSize() / sizeof(DeliveryCacheDirectoryEntry);
432
433 LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size);
434
435 if (current_dir == nullptr) {
436 LOG_ERROR(Service_BCAT, "There is no open directory!");
437 IPC::ResponseBuilder rb{ctx, 2};
438 rb.Push(ERROR_NO_OPEN_ENTITY);
439 return;
440 }
441
442 const auto files = current_dir->GetFiles();
443 write_size = std::min<u64>(write_size, files.size());
444 std::vector<DeliveryCacheDirectoryEntry> entries(write_size);
445 std::transform(
446 files.begin(), files.begin() + write_size, entries.begin(), [](const auto& file) {
447 FileName name{};
448 std::memcpy(name.data(), file->GetName().data(),
449 std::min(file->GetName().size(), name.size()));
450 return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)};
451 });
452
453 ctx.WriteBuffer(entries);
454
455 IPC::ResponseBuilder rb{ctx, 3};
456 rb.Push(RESULT_SUCCESS);
457 rb.Push(static_cast<u32>(write_size * sizeof(DeliveryCacheDirectoryEntry)));
458 }
459
460 void GetCount(Kernel::HLERequestContext& ctx) {
461 LOG_DEBUG(Service_BCAT, "called");
462
463 if (current_dir == nullptr) {
464 LOG_ERROR(Service_BCAT, "There is no open directory!");
465 IPC::ResponseBuilder rb{ctx, 2};
466 rb.Push(ERROR_NO_OPEN_ENTITY);
467 return;
468 }
469
470 const auto files = current_dir->GetFiles();
471
472 IPC::ResponseBuilder rb{ctx, 3};
473 rb.Push(RESULT_SUCCESS);
474 rb.Push(static_cast<u32>(files.size()));
475 }
476
477 FileSys::VirtualDir root;
478 FileSys::VirtualDir current_dir;
479};
480
481class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> {
482public:
483 IDeliveryCacheStorageService(FileSys::VirtualDir root_)
484 : ServiceFramework{"IDeliveryCacheStorageService"}, root(std::move(root_)) {
485 // clang-format off
486 static const FunctionInfo functions[] = {
487 {0, &IDeliveryCacheStorageService::CreateFileService, "CreateFileService"},
488 {1, &IDeliveryCacheStorageService::CreateDirectoryService, "CreateDirectoryService"},
489 {10, &IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory, "EnumerateDeliveryCacheDirectory"},
490 };
491 // clang-format on
492
493 RegisterHandlers(functions);
494
495 for (const auto& subdir : root->GetSubdirectories()) {
496 DirectoryName name{};
497 std::memcpy(name.data(), subdir->GetName().data(),
498 std::min(sizeof(DirectoryName) - 1, subdir->GetName().size()));
499 entries.push_back(name);
500 }
501 }
502
503private:
504 void CreateFileService(Kernel::HLERequestContext& ctx) {
505 LOG_DEBUG(Service_BCAT, "called");
506
507 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
508 rb.Push(RESULT_SUCCESS);
509 rb.PushIpcInterface<IDeliveryCacheFileService>(root);
510 }
511
512 void CreateDirectoryService(Kernel::HLERequestContext& ctx) {
513 LOG_DEBUG(Service_BCAT, "called");
514
515 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
516 rb.Push(RESULT_SUCCESS);
517 rb.PushIpcInterface<IDeliveryCacheDirectoryService>(root);
518 }
519
520 void EnumerateDeliveryCacheDirectory(Kernel::HLERequestContext& ctx) {
521 auto size = ctx.GetWriteBufferSize() / sizeof(DirectoryName);
522
523 LOG_DEBUG(Service_BCAT, "called, size={:016X}", size);
524
525 size = std::min<u64>(size, entries.size() - next_read_index);
526 ctx.WriteBuffer(entries.data() + next_read_index, size * sizeof(DirectoryName));
527 next_read_index += size;
528
529 IPC::ResponseBuilder rb{ctx, 3};
530 rb.Push(RESULT_SUCCESS);
531 rb.Push(static_cast<u32>(size));
532 }
533
534 FileSys::VirtualDir root;
535 std::vector<DirectoryName> entries;
536 u64 next_read_index = 0;
537};
538
539void Module::Interface::CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx) {
540 LOG_DEBUG(Service_BCAT, "called");
541
542 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
543 rb.Push(RESULT_SUCCESS);
544 rb.PushIpcInterface<IDeliveryCacheStorageService>(
545 fsc.GetBCATDirectory(system.CurrentProcess()->GetTitleID()));
546}
547
548void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId(
549 Kernel::HLERequestContext& ctx) {
550 IPC::RequestParser rp{ctx};
551 const auto title_id = rp.PopRaw<u64>();
552
553 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
554
555 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
556 rb.Push(RESULT_SUCCESS);
557 rb.PushIpcInterface<IDeliveryCacheStorageService>(fsc.GetBCATDirectory(title_id));
558}
559
560std::unique_ptr<Backend> CreateBackendFromSettings(DirectoryGetter getter) {
561 const auto backend = Settings::values.bcat_backend;
562
563#ifdef YUZU_ENABLE_BOXCAT
564 if (backend == "boxcat")
565 return std::make_unique<Boxcat>(std::move(getter));
566#endif
567
568 return std::make_unique<NullBackend>(std::move(getter));
41} 569}
42 570
43Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 571Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
44 : ServiceFramework(name), module(std::move(module)) {} 572 FileSystem::FileSystemController& fsc_, const char* name)
573 : ServiceFramework(name), fsc{fsc_}, module{std::move(module_)},
574 backend{CreateBackendFromSettings([&fsc_](u64 tid) { return fsc_.GetBCATDirectory(tid); })},
575 system{system_} {}
45 576
46Module::Interface::~Interface() = default; 577Module::Interface::~Interface() = default;
47 578
48void InstallInterfaces(SM::ServiceManager& service_manager) { 579void InstallInterfaces(Core::System& system) {
49 auto module = std::make_shared<Module>(); 580 auto module = std::make_shared<Module>();
50 std::make_shared<BCAT>(module, "bcat:a")->InstallAsService(service_manager); 581 std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:a")
51 std::make_shared<BCAT>(module, "bcat:m")->InstallAsService(service_manager); 582 ->InstallAsService(system.ServiceManager());
52 std::make_shared<BCAT>(module, "bcat:u")->InstallAsService(service_manager); 583 std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:m")
53 std::make_shared<BCAT>(module, "bcat:s")->InstallAsService(service_manager); 584 ->InstallAsService(system.ServiceManager());
585 std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:u")
586 ->InstallAsService(system.ServiceManager());
587 std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:s")
588 ->InstallAsService(system.ServiceManager());
54} 589}
55 590
56} // namespace Service::BCAT 591} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/module.h b/src/core/hle/service/bcat/module.h
index f0d63cab0..e4ba23ba0 100644
--- a/src/core/hle/service/bcat/module.h
+++ b/src/core/hle/service/bcat/module.h
@@ -6,23 +6,46 @@
6 6
7#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8 8
9namespace Service::BCAT { 9namespace Core {
10class System;
11}
12
13namespace Service {
14
15namespace FileSystem {
16class FileSystemController;
17} // namespace FileSystem
18
19namespace BCAT {
20
21class Backend;
10 22
11class Module final { 23class Module final {
12public: 24public:
13 class Interface : public ServiceFramework<Interface> { 25 class Interface : public ServiceFramework<Interface> {
14 public: 26 public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 27 explicit Interface(Core::System& system_, std::shared_ptr<Module> module_,
28 FileSystem::FileSystemController& fsc_, const char* name);
16 ~Interface() override; 29 ~Interface() override;
17 30
18 void CreateBcatService(Kernel::HLERequestContext& ctx); 31 void CreateBcatService(Kernel::HLERequestContext& ctx);
32 void CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx);
33 void CreateDeliveryCacheStorageServiceWithApplicationId(Kernel::HLERequestContext& ctx);
19 34
20 protected: 35 protected:
36 FileSystem::FileSystemController& fsc;
37
21 std::shared_ptr<Module> module; 38 std::shared_ptr<Module> module;
39 std::unique_ptr<Backend> backend;
40
41 private:
42 Core::System& system;
22 }; 43 };
23}; 44};
24 45
25/// Registers all BCAT services with the specified service manager. 46/// Registers all BCAT services with the specified service manager.
26void InstallInterfaces(SM::ServiceManager& service_manager); 47void InstallInterfaces(Core::System& system);
48
49} // namespace BCAT
27 50
28} // namespace Service::BCAT 51} // namespace Service
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index af70d174d..f77ddd739 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -128,7 +128,7 @@ private:
128 void CountCommonTicket(Kernel::HLERequestContext& ctx) { 128 void CountCommonTicket(Kernel::HLERequestContext& ctx) {
129 LOG_DEBUG(Service_ETicket, "called"); 129 LOG_DEBUG(Service_ETicket, "called");
130 130
131 const auto count = keys.GetCommonTickets().size(); 131 const u32 count = static_cast<u32>(keys.GetCommonTickets().size());
132 132
133 IPC::ResponseBuilder rb{ctx, 3}; 133 IPC::ResponseBuilder rb{ctx, 3};
134 rb.Push(RESULT_SUCCESS); 134 rb.Push(RESULT_SUCCESS);
@@ -138,7 +138,7 @@ private:
138 void CountPersonalizedTicket(Kernel::HLERequestContext& ctx) { 138 void CountPersonalizedTicket(Kernel::HLERequestContext& ctx) {
139 LOG_DEBUG(Service_ETicket, "called"); 139 LOG_DEBUG(Service_ETicket, "called");
140 140
141 const auto count = keys.GetPersonalizedTickets().size(); 141 const u32 count = static_cast<u32>(keys.GetPersonalizedTickets().size());
142 142
143 IPC::ResponseBuilder rb{ctx, 3}; 143 IPC::ResponseBuilder rb{ctx, 3};
144 rb.Push(RESULT_SUCCESS); 144 rb.Push(RESULT_SUCCESS);
@@ -150,7 +150,7 @@ private:
150 if (keys.GetCommonTickets().empty()) 150 if (keys.GetCommonTickets().empty())
151 out_entries = 0; 151 out_entries = 0;
152 else 152 else
153 out_entries = ctx.GetWriteBufferSize() / sizeof(u128); 153 out_entries = static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(u128));
154 154
155 LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); 155 LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries);
156 156
@@ -160,7 +160,7 @@ private:
160 std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), 160 std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids),
161 [](const auto& pair) { return pair.first; }); 161 [](const auto& pair) { return pair.first; });
162 162
163 out_entries = std::min<u32>(ids.size(), out_entries); 163 out_entries = static_cast<u32>(std::min<std::size_t>(ids.size(), out_entries));
164 ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); 164 ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128));
165 165
166 IPC::ResponseBuilder rb{ctx, 3}; 166 IPC::ResponseBuilder rb{ctx, 3};
@@ -173,7 +173,7 @@ private:
173 if (keys.GetPersonalizedTickets().empty()) 173 if (keys.GetPersonalizedTickets().empty())
174 out_entries = 0; 174 out_entries = 0;
175 else 175 else
176 out_entries = ctx.GetWriteBufferSize() / sizeof(u128); 176 out_entries = static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(u128));
177 177
178 LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); 178 LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries);
179 179
@@ -183,7 +183,7 @@ private:
183 std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), 183 std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids),
184 [](const auto& pair) { return pair.first; }); 184 [](const auto& pair) { return pair.first; });
185 185
186 out_entries = std::min<u32>(ids.size(), out_entries); 186 out_entries = static_cast<u32>(std::min<std::size_t>(ids.size(), out_entries));
187 ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); 187 ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128));
188 188
189 IPC::ResponseBuilder rb{ctx, 3}; 189 IPC::ResponseBuilder rb{ctx, 3};
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index b2ebf6240..2546d7595 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -66,7 +66,7 @@ enum class FatalType : u32 {
66 66
67static void GenerateErrorReport(Core::System& system, ResultCode error_code, 67static void GenerateErrorReport(Core::System& system, ResultCode error_code,
68 const FatalInfo& info) { 68 const FatalInfo& info) {
69 const auto title_id = Core::CurrentProcess()->GetTitleID(); 69 const auto title_id = system.CurrentProcess()->GetTitleID();
70 std::string crash_report = fmt::format( 70 std::string crash_report = fmt::format(
71 "Yuzu {}-{} crash report\n" 71 "Yuzu {}-{} crash report\n"
72 "Title ID: {:016x}\n" 72 "Title ID: {:016x}\n"
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 14cd0e322..11e5c56b7 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -241,7 +241,7 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
241 return FileSys::ERROR_PATH_NOT_FOUND; 241 return FileSys::ERROR_PATH_NOT_FOUND;
242} 242}
243 243
244FileSystemController::FileSystemController() = default; 244FileSystemController::FileSystemController(Core::System& system_) : system{system_} {}
245 245
246FileSystemController::~FileSystemController() = default; 246FileSystemController::~FileSystemController() = default;
247 247
@@ -290,7 +290,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess()
290 return ResultCode(-1); 290 return ResultCode(-1);
291 } 291 }
292 292
293 return romfs_factory->OpenCurrentProcess(); 293 return romfs_factory->OpenCurrentProcess(system.CurrentProcess()->GetTitleID());
294} 294}
295 295
296ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS( 296ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
@@ -447,10 +447,10 @@ FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataTy
447 FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE}; 447 FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
448 448
449 FileSys::NACP nacp; 449 FileSys::NACP nacp;
450 const auto res = Core::System::GetInstance().GetAppLoader().ReadControlData(nacp); 450 const auto res = system.GetAppLoader().ReadControlData(nacp);
451 451
452 if (res != Loader::ResultStatus::Success) { 452 if (res != Loader::ResultStatus::Success) {
453 FileSys::PatchManager pm{Core::CurrentProcess()->GetTitleID()}; 453 FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
454 auto [nacp_unique, discard] = pm.GetControlMetadata(); 454 auto [nacp_unique, discard] = pm.GetControlMetadata();
455 455
456 if (nacp_unique != nullptr) { 456 if (nacp_unique != nullptr) {
@@ -674,6 +674,15 @@ FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id)
674 return bis_factory->GetModificationDumpRoot(title_id); 674 return bis_factory->GetModificationDumpRoot(title_id);
675} 675}
676 676
677FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const {
678 LOG_TRACE(Service_FS, "Opening BCAT root for tid={:016X}", title_id);
679
680 if (bis_factory == nullptr)
681 return nullptr;
682
683 return bis_factory->GetBCATDirectory(title_id);
684}
685
677void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { 686void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
678 if (overwrite) { 687 if (overwrite) {
679 bis_factory = nullptr; 688 bis_factory = nullptr;
@@ -693,10 +702,10 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
693 if (bis_factory == nullptr) { 702 if (bis_factory == nullptr) {
694 bis_factory = 703 bis_factory =
695 std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory); 704 std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
696 Core::System::GetInstance().RegisterContentProvider( 705 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SysNAND,
697 FileSys::ContentProviderUnionSlot::SysNAND, bis_factory->GetSystemNANDContents()); 706 bis_factory->GetSystemNANDContents());
698 Core::System::GetInstance().RegisterContentProvider( 707 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::UserNAND,
699 FileSys::ContentProviderUnionSlot::UserNAND, bis_factory->GetUserNANDContents()); 708 bis_factory->GetUserNANDContents());
700 } 709 }
701 710
702 if (save_data_factory == nullptr) { 711 if (save_data_factory == nullptr) {
@@ -705,8 +714,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
705 714
706 if (sdmc_factory == nullptr) { 715 if (sdmc_factory == nullptr) {
707 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); 716 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
708 Core::System::GetInstance().RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC, 717 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
709 sdmc_factory->GetSDMCContents()); 718 sdmc_factory->GetSDMCContents());
710 } 719 }
711} 720}
712 721
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 3e0c03ec0..1b0a6a949 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -10,6 +10,10 @@
10#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs.h"
11#include "core/hle/result.h" 11#include "core/hle/result.h"
12 12
13namespace Core {
14class System;
15}
16
13namespace FileSys { 17namespace FileSys {
14class BISFactory; 18class BISFactory;
15class RegisteredCache; 19class RegisteredCache;
@@ -52,7 +56,7 @@ enum class ImageDirectoryId : u32 {
52 56
53class FileSystemController { 57class FileSystemController {
54public: 58public:
55 FileSystemController(); 59 explicit FileSystemController(Core::System& system_);
56 ~FileSystemController(); 60 ~FileSystemController();
57 61
58 ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); 62 ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
@@ -110,6 +114,8 @@ public:
110 FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const; 114 FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const;
111 FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const; 115 FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const;
112 116
117 FileSys::VirtualDir GetBCATDirectory(u64 title_id) const;
118
113 // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function 119 // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
114 // above is called. 120 // above is called.
115 void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); 121 void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
@@ -123,6 +129,8 @@ private:
123 std::unique_ptr<FileSys::XCI> gamecard; 129 std::unique_ptr<FileSys::XCI> gamecard;
124 std::unique_ptr<FileSys::RegisteredCache> gamecard_registered; 130 std::unique_ptr<FileSys::RegisteredCache> gamecard_registered;
125 std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder; 131 std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder;
132
133 Core::System& system;
126}; 134};
127 135
128void InstallInterfaces(Core::System& system); 136void InstallInterfaces(Core::System& system);
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index eb982ad49..cbd5466c1 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -803,7 +803,7 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
803 IPC::RequestParser rp{ctx}; 803 IPC::RequestParser rp{ctx};
804 804
805 auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>(); 805 auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
806 auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); 806 [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
807 u128 uid = rp.PopRaw<u128>(); 807 u128 uid = rp.PopRaw<u128>();
808 808
809 LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(), 809 LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 42b4ee861..75dd9043b 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -237,7 +237,6 @@ private:
237 }; 237 };
238 238
239 Common::UUID uuid; 239 Common::UUID uuid;
240 bool is_event_created = false;
241 Kernel::EventPair notification_event; 240 Kernel::EventPair notification_event;
242 std::queue<SizedNotificationInfo> notifications; 241 std::queue<SizedNotificationInfo> notifications;
243 States states{}; 242 States states{};
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 8e8263f5b..1f2131ec8 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -11,11 +11,10 @@
11namespace Service::HID { 11namespace Service::HID {
12 12
13constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 13constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
14constexpr s32 HID_JOYSTICK_MIN = -0x7fff; 14[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
15enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; 15enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
16 16
17Controller_DebugPad::Controller_DebugPad(Core::System& system) 17Controller_DebugPad::Controller_DebugPad(Core::System& system) : ControllerBase(system) {}
18 : ControllerBase(system), system(system) {}
19Controller_DebugPad::~Controller_DebugPad() = default; 18Controller_DebugPad::~Controller_DebugPad() = default;
20 19
21void Controller_DebugPad::OnInit() {} 20void Controller_DebugPad::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 6c4de817e..555b29d76 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -89,6 +89,5 @@ private:
89 buttons; 89 buttons;
90 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> 90 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
91 analogs; 91 analogs;
92 Core::System& system;
93}; 92};
94} // namespace Service::HID 93} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 80da0a0d3..6e990dd00 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -10,8 +10,7 @@
10namespace Service::HID { 10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00; 11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
12 12
13Controller_Gesture::Controller_Gesture(Core::System& system) 13Controller_Gesture::Controller_Gesture(Core::System& system) : ControllerBase(system) {}
14 : ControllerBase(system), system(system) {}
15Controller_Gesture::~Controller_Gesture() = default; 14Controller_Gesture::~Controller_Gesture() = default;
16 15
17void Controller_Gesture::OnInit() {} 16void Controller_Gesture::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 396897527..f650b8338 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -59,6 +59,5 @@ private:
59 std::array<GestureState, 17> gesture_states; 59 std::array<GestureState, 17> gesture_states;
60 }; 60 };
61 SharedMemory shared_memory{}; 61 SharedMemory shared_memory{};
62 Core::System& system;
63}; 62};
64} // namespace Service::HID 63} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index e587b2e15..358cb9329 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -12,8 +12,7 @@ namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
13constexpr u8 KEYS_PER_BYTE = 8; 13constexpr u8 KEYS_PER_BYTE = 8;
14 14
15Controller_Keyboard::Controller_Keyboard(Core::System& system) 15Controller_Keyboard::Controller_Keyboard(Core::System& system) : ControllerBase(system) {}
16 : ControllerBase(system), system(system) {}
17Controller_Keyboard::~Controller_Keyboard() = default; 16Controller_Keyboard::~Controller_Keyboard() = default;
18 17
19void Controller_Keyboard::OnInit() {} 18void Controller_Keyboard::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index ef586f7eb..f3eef5936 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -53,6 +53,5 @@ private:
53 keyboard_keys; 53 keyboard_keys;
54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods> 54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
55 keyboard_mods; 55 keyboard_mods;
56 Core::System& system;
57}; 56};
58} // namespace Service::HID 57} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 88f2ca4c1..93d88ea50 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -11,7 +11,7 @@
11namespace Service::HID { 11namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
13 13
14Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system), system(system) {} 14Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system) {}
15Controller_Mouse::~Controller_Mouse() = default; 15Controller_Mouse::~Controller_Mouse() = default;
16 16
17void Controller_Mouse::OnInit() {} 17void Controller_Mouse::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index df2da6ae3..357ab7107 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -53,6 +53,5 @@ private:
53 std::unique_ptr<Input::MouseDevice> mouse_device; 53 std::unique_ptr<Input::MouseDevice> mouse_device;
54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons> 54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
55 mouse_button_devices; 55 mouse_button_devices;
56 Core::System& system;
57}; 56};
58} // namespace Service::HID 57} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 44b668fbf..a2b25a796 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -20,7 +20,7 @@
20 20
21namespace Service::HID { 21namespace Service::HID {
22constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 22constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
23constexpr s32 HID_JOYSTICK_MIN = -0x7fff; 23[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
24constexpr std::size_t NPAD_OFFSET = 0x9A00; 24constexpr std::size_t NPAD_OFFSET = 0x9A00;
25constexpr u32 BATTERY_FULL = 2; 25constexpr u32 BATTERY_FULL = 2;
26constexpr u32 MAX_NPAD_ID = 7; 26constexpr u32 MAX_NPAD_ID = 7;
@@ -105,6 +105,8 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
105 controller.joy_styles.raw = 0; // Zero out 105 controller.joy_styles.raw = 0; // Zero out
106 controller.device_type.raw = 0; 106 controller.device_type.raw = 0;
107 switch (controller_type) { 107 switch (controller_type) {
108 case NPadControllerType::None:
109 UNREACHABLE();
108 case NPadControllerType::Handheld: 110 case NPadControllerType::Handheld:
109 controller.joy_styles.handheld.Assign(1); 111 controller.joy_styles.handheld.Assign(1);
110 controller.device_type.handheld.Assign(1); 112 controller.device_type.handheld.Assign(1);
@@ -165,13 +167,14 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
165 controller.battery_level[0] = BATTERY_FULL; 167 controller.battery_level[0] = BATTERY_FULL;
166 controller.battery_level[1] = BATTERY_FULL; 168 controller.battery_level[1] = BATTERY_FULL;
167 controller.battery_level[2] = BATTERY_FULL; 169 controller.battery_level[2] = BATTERY_FULL;
170 styleset_changed_events[controller_idx].writable->Signal();
168} 171}
169 172
170void Controller_NPad::OnInit() { 173void Controller_NPad::OnInit() {
171 auto& kernel = system.Kernel(); 174 auto& kernel = system.Kernel();
172 for (std::size_t i = 0; i < styleset_changed_events.size(); i++) { 175 for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
173 styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair( 176 styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
174 kernel, Kernel::ResetType::Automatic, fmt::format("npad:NpadStyleSetChanged_{}", i)); 177 kernel, Kernel::ResetType::Manual, fmt::format("npad:NpadStyleSetChanged_{}", i));
175 } 178 }
176 179
177 if (!IsControllerActivated()) { 180 if (!IsControllerActivated()) {
@@ -238,7 +241,7 @@ void Controller_NPad::OnRelease() {}
238 241
239void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { 242void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
240 const auto controller_idx = NPadIdToIndex(npad_id); 243 const auto controller_idx = NPadIdToIndex(npad_id);
241 const auto controller_type = connected_controllers[controller_idx].type; 244 [[maybe_unused]] const auto controller_type = connected_controllers[controller_idx].type;
242 if (!connected_controllers[controller_idx].is_connected) { 245 if (!connected_controllers[controller_idx].is_connected) {
243 return; 246 return;
244 } 247 }
@@ -345,6 +348,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
345 libnx_entry.connection_status.raw = 0; 348 libnx_entry.connection_status.raw = 0;
346 349
347 switch (controller_type) { 350 switch (controller_type) {
351 case NPadControllerType::None:
352 UNREACHABLE();
348 case NPadControllerType::Handheld: 353 case NPadControllerType::Handheld:
349 handheld_entry.connection_status.raw = 0; 354 handheld_entry.connection_status.raw = 0;
350 handheld_entry.connection_status.IsWired.Assign(1); 355 handheld_entry.connection_status.IsWired.Assign(1);
@@ -433,7 +438,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
433 supported_npad_id_types.clear(); 438 supported_npad_id_types.clear();
434 supported_npad_id_types.resize(length / sizeof(u32)); 439 supported_npad_id_types.resize(length / sizeof(u32));
435 std::memcpy(supported_npad_id_types.data(), data, length); 440 std::memcpy(supported_npad_id_types.data(), data, length);
436 bool had_controller_update = false;
437 for (std::size_t i = 0; i < connected_controllers.size(); i++) { 441 for (std::size_t i = 0; i < connected_controllers.size(); i++) {
438 auto& controller = connected_controllers[i]; 442 auto& controller = connected_controllers[i];
439 if (!controller.is_connected) { 443 if (!controller.is_connected) {
@@ -452,10 +456,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
452 controller.type = requested_controller; 456 controller.type = requested_controller;
453 InitNewlyAddedControler(i); 457 InitNewlyAddedControler(i);
454 } 458 }
455 had_controller_update = true;
456 }
457 if (had_controller_update) {
458 styleset_changed_events[i].writable->Signal();
459 } 459 }
460 } 460 }
461} 461}
@@ -481,7 +481,6 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode)
481 const std::size_t npad_index = NPadIdToIndex(npad_id); 481 const std::size_t npad_index = NPadIdToIndex(npad_id);
482 ASSERT(npad_index < shared_memory_entries.size()); 482 ASSERT(npad_index < shared_memory_entries.size());
483 if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) { 483 if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) {
484 styleset_changed_events[npad_index].writable->Signal();
485 shared_memory_entries[npad_index].pad_assignment = assignment_mode; 484 shared_memory_entries[npad_index].pad_assignment = assignment_mode;
486 } 485 }
487} 486}
@@ -507,7 +506,6 @@ Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEven
507 // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should 506 // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
508 // be signalled at least once, and signaled after a new controller is connected? 507 // be signalled at least once, and signaled after a new controller is connected?
509 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; 508 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
510 styleset_event.writable->Signal();
511 return styleset_event.readable; 509 return styleset_event.readable;
512} 510}
513 511
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index 9b829341e..9e527d176 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -9,8 +9,7 @@
9 9
10namespace Service::HID { 10namespace Service::HID {
11 11
12Controller_Stubbed::Controller_Stubbed(Core::System& system) 12Controller_Stubbed::Controller_Stubbed(Core::System& system) : ControllerBase(system) {}
13 : ControllerBase(system), system(system) {}
14Controller_Stubbed::~Controller_Stubbed() = default; 13Controller_Stubbed::~Controller_Stubbed() = default;
15 14
16void Controller_Stubbed::OnInit() {} 15void Controller_Stubbed::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
index 37d7d8538..4fa83ac85 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -30,6 +30,5 @@ public:
30private: 30private:
31 bool smart_update{}; 31 bool smart_update{};
32 std::size_t common_offset{}; 32 std::size_t common_offset{};
33 Core::System& system;
34}; 33};
35} // namespace Service::HID 34} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 25912fd69..1c6e55566 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -13,8 +13,7 @@
13namespace Service::HID { 13namespace Service::HID {
14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; 14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
15 15
16Controller_Touchscreen::Controller_Touchscreen(Core::System& system) 16Controller_Touchscreen::Controller_Touchscreen(Core::System& system) : ControllerBase(system) {}
17 : ControllerBase(system), system(system) {}
18Controller_Touchscreen::~Controller_Touchscreen() = default; 17Controller_Touchscreen::~Controller_Touchscreen() = default;
19 18
20void Controller_Touchscreen::OnInit() {} 19void Controller_Touchscreen::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 3429c84db..a1d97269e 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -69,6 +69,5 @@ private:
69 TouchScreenSharedMemory shared_memory{}; 69 TouchScreenSharedMemory shared_memory{};
70 std::unique_ptr<Input::TouchDevice> touch_device; 70 std::unique_ptr<Input::TouchDevice> touch_device;
71 s64_le last_touch{}; 71 s64_le last_touch{};
72 Core::System& system;
73}; 72};
74} // namespace Service::HID 73} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index 1bce044b4..27511b27b 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -10,7 +10,7 @@
10namespace Service::HID { 10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; 11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
12 12
13Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system), system(system) {} 13Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system) {}
14Controller_XPad::~Controller_XPad() = default; 14Controller_XPad::~Controller_XPad() = default;
15 15
16void Controller_XPad::OnInit() {} 16void Controller_XPad::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index c445ebec0..ad229787c 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -56,6 +56,5 @@ private:
56 }; 56 };
57 static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size"); 57 static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
58 SharedMemory shared_memory{}; 58 SharedMemory shared_memory{};
59 Core::System& system;
60}; 59};
61} // namespace Service::HID 60} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 8d76ba746..ba1da4181 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -38,8 +38,10 @@ namespace Service::HID {
38// Updating period for each HID device. 38// Updating period for each HID device.
39// TODO(ogniK): Find actual polling rate of hid 39// TODO(ogniK): Find actual polling rate of hid
40constexpr s64 pad_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 66); 40constexpr s64 pad_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 66);
41constexpr s64 accelerometer_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100); 41[[maybe_unused]] constexpr s64 accelerometer_update_ticks =
42constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100); 42 static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
43[[maybe_unused]] constexpr s64 gyroscope_update_ticks =
44 static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
43constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 45constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
44 46
45IAppletResource::IAppletResource(Core::System& system) 47IAppletResource::IAppletResource(Core::System& system)
@@ -193,7 +195,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
193 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, 195 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
194 {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, 196 {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
195 {103, &Hid::ActivateNpad, "ActivateNpad"}, 197 {103, &Hid::ActivateNpad, "ActivateNpad"},
196 {104, nullptr, "DeactivateNpad"}, 198 {104, &Hid::DeactivateNpad, "DeactivateNpad"},
197 {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, 199 {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
198 {107, &Hid::DisconnectNpad, "DisconnectNpad"}, 200 {107, &Hid::DisconnectNpad, "DisconnectNpad"},
199 {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, 201 {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
@@ -468,6 +470,17 @@ void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) {
468 applet_resource->ActivateController(HidController::NPad); 470 applet_resource->ActivateController(HidController::NPad);
469} 471}
470 472
473void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
474 IPC::RequestParser rp{ctx};
475 const auto applet_resource_user_id{rp.Pop<u64>()};
476
477 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
478
479 IPC::ResponseBuilder rb{ctx, 2};
480 rb.Push(RESULT_SUCCESS);
481 applet_resource->DeactivateController(HidController::NPad);
482}
483
471void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { 484void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
472 IPC::RequestParser rp{ctx}; 485 IPC::RequestParser rp{ctx};
473 const auto npad_id{rp.Pop<u32>()}; 486 const auto npad_id{rp.Pop<u32>()};
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 35b663679..01852e019 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -99,6 +99,7 @@ private:
99 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); 99 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
100 void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx); 100 void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
101 void ActivateNpad(Kernel::HLERequestContext& ctx); 101 void ActivateNpad(Kernel::HLERequestContext& ctx);
102 void DeactivateNpad(Kernel::HLERequestContext& ctx);
102 void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx); 103 void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
103 void DisconnectNpad(Kernel::HLERequestContext& ctx); 104 void DisconnectNpad(Kernel::HLERequestContext& ctx);
104 void GetPlayerLedPattern(Kernel::HLERequestContext& ctx); 105 void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 3164ca26e..499376bfc 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -163,7 +163,7 @@ public:
163 return; 163 return;
164 } 164 }
165 165
166 if (Core::CurrentProcess()->GetTitleID() != header.title_id) { 166 if (system.CurrentProcess()->GetTitleID() != header.title_id) {
167 LOG_ERROR(Service_LDR, 167 LOG_ERROR(Service_LDR,
168 "Attempting to load NRR with title ID other than current process. (actual " 168 "Attempting to load NRR with title ID other than current process. (actual "
169 "{:016X})!", 169 "{:016X})!",
@@ -327,7 +327,7 @@ public:
327 } 327 }
328 328
329 // Load NRO as new executable module 329 // Load NRO as new executable module
330 auto* process = Core::CurrentProcess(); 330 auto* process = system.CurrentProcess();
331 auto& vm_manager = process->VMManager(); 331 auto& vm_manager = process->VMManager();
332 auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size); 332 auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size);
333 333
@@ -411,7 +411,7 @@ public:
411 return; 411 return;
412 } 412 }
413 413
414 auto& vm_manager = Core::CurrentProcess()->VMManager(); 414 auto& vm_manager = system.CurrentProcess()->VMManager();
415 const auto& nro_info = iter->second; 415 const auto& nro_info = iter->second;
416 416
417 // Unmap the mirrored memory 417 // Unmap the mirrored memory
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index 2a61593e2..435f2d286 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -6,8 +6,10 @@
6#include <string> 6#include <string>
7 7
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/scope_exit.h"
9#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
10#include "core/hle/service/lm/lm.h" 11#include "core/hle/service/lm/lm.h"
12#include "core/hle/service/lm/manager.h"
11#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
12#include "core/memory.h" 14#include "core/memory.h"
13 15
@@ -15,65 +17,16 @@ namespace Service::LM {
15 17
16class ILogger final : public ServiceFramework<ILogger> { 18class ILogger final : public ServiceFramework<ILogger> {
17public: 19public:
18 ILogger() : ServiceFramework("ILogger") { 20 ILogger(Manager& manager) : ServiceFramework("ILogger"), manager(manager) {
19 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
20 {0x00000000, &ILogger::Initialize, "Initialize"}, 22 {0, &ILogger::Log, "Log"},
21 {0x00000001, &ILogger::SetDestination, "SetDestination"}, 23 {1, &ILogger::SetDestination, "SetDestination"},
22 }; 24 };
23 RegisterHandlers(functions); 25 RegisterHandlers(functions);
24 } 26 }
25 27
26private: 28private:
27 struct MessageHeader { 29 void Log(Kernel::HLERequestContext& ctx) {
28 enum Flags : u32_le {
29 IsHead = 1,
30 IsTail = 2,
31 };
32 enum Severity : u32_le {
33 Trace,
34 Info,
35 Warning,
36 Error,
37 Critical,
38 };
39
40 u64_le pid;
41 u64_le threadContext;
42 union {
43 BitField<0, 16, Flags> flags;
44 BitField<16, 8, Severity> severity;
45 BitField<24, 8, u32> verbosity;
46 };
47 u32_le payload_size;
48
49 bool IsHeadLog() const {
50 return flags & Flags::IsHead;
51 }
52 bool IsTailLog() const {
53 return flags & Flags::IsTail;
54 }
55 };
56 static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size");
57
58 /// Log field type
59 enum class Field : u8 {
60 Skip = 1,
61 Message = 2,
62 Line = 3,
63 Filename = 4,
64 Function = 5,
65 Module = 6,
66 Thread = 7,
67 };
68
69 /**
70 * ILogger::Initialize service function
71 * Inputs:
72 * 0: 0x00000000
73 * Outputs:
74 * 0: ResultCode
75 */
76 void Initialize(Kernel::HLERequestContext& ctx) {
77 // This function only succeeds - Get that out of the way 30 // This function only succeeds - Get that out of the way
78 IPC::ResponseBuilder rb{ctx, 2}; 31 IPC::ResponseBuilder rb{ctx, 2};
79 rb.Push(RESULT_SUCCESS); 32 rb.Push(RESULT_SUCCESS);
@@ -85,140 +38,70 @@ private:
85 Memory::ReadBlock(addr, &header, sizeof(MessageHeader)); 38 Memory::ReadBlock(addr, &header, sizeof(MessageHeader));
86 addr += sizeof(MessageHeader); 39 addr += sizeof(MessageHeader);
87 40
88 if (header.IsHeadLog()) { 41 FieldMap fields;
89 log_stream.str("");
90 log_stream.clear();
91 }
92
93 // Parse out log metadata
94 u32 line{};
95 std::string module;
96 std::string message;
97 std::string filename;
98 std::string function;
99 std::string thread;
100 while (addr < end_addr) { 42 while (addr < end_addr) {
101 const Field field{static_cast<Field>(Memory::Read8(addr++))}; 43 const auto field = static_cast<Field>(Memory::Read8(addr++));
102 const std::size_t length{Memory::Read8(addr++)}; 44 const auto length = Memory::Read8(addr++);
103 45
104 if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { 46 if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) {
105 ++addr; 47 ++addr;
106 } 48 }
107 49
108 switch (field) { 50 SCOPE_EXIT({ addr += length; });
109 case Field::Skip:
110 break;
111 case Field::Message:
112 message = Memory::ReadCString(addr, length);
113 break;
114 case Field::Line:
115 line = Memory::Read32(addr);
116 break;
117 case Field::Filename:
118 filename = Memory::ReadCString(addr, length);
119 break;
120 case Field::Function:
121 function = Memory::ReadCString(addr, length);
122 break;
123 case Field::Module:
124 module = Memory::ReadCString(addr, length);
125 break;
126 case Field::Thread:
127 thread = Memory::ReadCString(addr, length);
128 break;
129 }
130 51
131 addr += length; 52 if (field == Field::Skip) {
132 } 53 continue;
54 }
133 55
134 // Empty log - nothing to do here 56 std::vector<u8> data(length);
135 if (log_stream.str().empty() && message.empty()) { 57 Memory::ReadBlock(addr, data.data(), length);
136 return; 58 fields.emplace(field, std::move(data));
137 } 59 }
138 60
139 // Format a nicely printable string out of the log metadata 61 manager.Log({header, std::move(fields)});
140 if (!filename.empty()) {
141 log_stream << filename << ':';
142 }
143 if (!module.empty()) {
144 log_stream << module << ':';
145 }
146 if (!function.empty()) {
147 log_stream << function << ':';
148 }
149 if (line) {
150 log_stream << std::to_string(line) << ':';
151 }
152 if (!thread.empty()) {
153 log_stream << thread << ':';
154 }
155 if (log_stream.str().length() > 0 && log_stream.str().back() == ':') {
156 log_stream << ' ';
157 }
158 log_stream << message;
159
160 if (header.IsTailLog()) {
161 switch (header.severity) {
162 case MessageHeader::Severity::Trace:
163 LOG_DEBUG(Debug_Emulated, "{}", log_stream.str());
164 break;
165 case MessageHeader::Severity::Info:
166 LOG_INFO(Debug_Emulated, "{}", log_stream.str());
167 break;
168 case MessageHeader::Severity::Warning:
169 LOG_WARNING(Debug_Emulated, "{}", log_stream.str());
170 break;
171 case MessageHeader::Severity::Error:
172 LOG_ERROR(Debug_Emulated, "{}", log_stream.str());
173 break;
174 case MessageHeader::Severity::Critical:
175 LOG_CRITICAL(Debug_Emulated, "{}", log_stream.str());
176 break;
177 }
178 }
179 } 62 }
180 63
181 // This service function is intended to be used as a way to
182 // redirect logging output to different destinations, however,
183 // given we always want to see the logging output, it's sufficient
184 // to do nothing and return success here.
185 void SetDestination(Kernel::HLERequestContext& ctx) { 64 void SetDestination(Kernel::HLERequestContext& ctx) {
186 LOG_DEBUG(Service_LM, "called"); 65 IPC::RequestParser rp{ctx};
66 const auto destination = rp.PopEnum<DestinationFlag>();
67
68 LOG_DEBUG(Service_LM, "called, destination={:08X}", static_cast<u32>(destination));
69
70 manager.SetDestination(destination);
187 71
188 IPC::ResponseBuilder rb{ctx, 2}; 72 IPC::ResponseBuilder rb{ctx, 2};
189 rb.Push(RESULT_SUCCESS); 73 rb.Push(RESULT_SUCCESS);
190 } 74 }
191 75
192 std::ostringstream log_stream; 76 Manager& manager;
193}; 77};
194 78
195class LM final : public ServiceFramework<LM> { 79class LM final : public ServiceFramework<LM> {
196public: 80public:
197 explicit LM() : ServiceFramework{"lm"} { 81 explicit LM(Manager& manager) : ServiceFramework{"lm"}, manager(manager) {
82 // clang-format off
198 static const FunctionInfo functions[] = { 83 static const FunctionInfo functions[] = {
199 {0x00000000, &LM::OpenLogger, "OpenLogger"}, 84 {0, &LM::OpenLogger, "OpenLogger"},
200 }; 85 };
86 // clang-format on
87
201 RegisterHandlers(functions); 88 RegisterHandlers(functions);
202 } 89 }
203 90
204 /** 91private:
205 * LM::OpenLogger service function
206 * Inputs:
207 * 0: 0x00000000
208 * Outputs:
209 * 0: ResultCode
210 */
211 void OpenLogger(Kernel::HLERequestContext& ctx) { 92 void OpenLogger(Kernel::HLERequestContext& ctx) {
212 LOG_DEBUG(Service_LM, "called"); 93 LOG_DEBUG(Service_LM, "called");
213 94
214 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 95 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
215 rb.Push(RESULT_SUCCESS); 96 rb.Push(RESULT_SUCCESS);
216 rb.PushIpcInterface<ILogger>(); 97 rb.PushIpcInterface<ILogger>(manager);
217 } 98 }
99
100 Manager& manager;
218}; 101};
219 102
220void InstallInterfaces(SM::ServiceManager& service_manager) { 103void InstallInterfaces(Core::System& system) {
221 std::make_shared<LM>()->InstallAsService(service_manager); 104 std::make_shared<LM>(system.GetLogManager())->InstallAsService(system.ServiceManager());
222} 105}
223 106
224} // namespace Service::LM 107} // namespace Service::LM
diff --git a/src/core/hle/service/lm/lm.h b/src/core/hle/service/lm/lm.h
index 7806ae27b..d40410b5c 100644
--- a/src/core/hle/service/lm/lm.h
+++ b/src/core/hle/service/lm/lm.h
@@ -4,13 +4,13 @@
4 4
5#pragma once 5#pragma once
6 6
7namespace Service::SM { 7namespace Core {
8class ServiceManager; 8class System;
9} 9}
10 10
11namespace Service::LM { 11namespace Service::LM {
12 12
13/// Registers all LM services with the specified service manager. 13/// Registers all LM services with the specified service manager.
14void InstallInterfaces(SM::ServiceManager& service_manager); 14void InstallInterfaces(Core::System& system);
15 15
16} // namespace Service::LM 16} // namespace Service::LM
diff --git a/src/core/hle/service/lm/manager.cpp b/src/core/hle/service/lm/manager.cpp
new file mode 100644
index 000000000..b67081b86
--- /dev/null
+++ b/src/core/hle/service/lm/manager.cpp
@@ -0,0 +1,133 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "common/string_util.h"
8#include "core/hle/service/lm/manager.h"
9#include "core/reporter.h"
10
11namespace Service::LM {
12
13std::ostream& operator<<(std::ostream& os, DestinationFlag dest) {
14 std::vector<std::string> array;
15 const auto check_single_flag = [dest, &array](DestinationFlag check, std::string name) {
16 if ((static_cast<u32>(check) & static_cast<u32>(dest)) != 0) {
17 array.emplace_back(std::move(name));
18 }
19 };
20
21 check_single_flag(DestinationFlag::Default, "Default");
22 check_single_flag(DestinationFlag::UART, "UART");
23 check_single_flag(DestinationFlag::UARTSleeping, "UART (Sleeping)");
24
25 os << "[";
26 for (const auto& entry : array) {
27 os << entry << ", ";
28 }
29 return os << "]";
30}
31
32std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity) {
33 switch (severity) {
34 case MessageHeader::Severity::Trace:
35 return os << "Trace";
36 case MessageHeader::Severity::Info:
37 return os << "Info";
38 case MessageHeader::Severity::Warning:
39 return os << "Warning";
40 case MessageHeader::Severity::Error:
41 return os << "Error";
42 case MessageHeader::Severity::Critical:
43 return os << "Critical";
44 default:
45 return os << fmt::format("{:08X}", static_cast<u32>(severity));
46 }
47}
48
49std::ostream& operator<<(std::ostream& os, Field field) {
50 switch (field) {
51 case Field::Skip:
52 return os << "Skip";
53 case Field::Message:
54 return os << "Message";
55 case Field::Line:
56 return os << "Line";
57 case Field::Filename:
58 return os << "Filename";
59 case Field::Function:
60 return os << "Function";
61 case Field::Module:
62 return os << "Module";
63 case Field::Thread:
64 return os << "Thread";
65 default:
66 return os << fmt::format("{:08X}", static_cast<u32>(field));
67 }
68}
69
70std::string FormatField(Field type, const std::vector<u8>& data) {
71 switch (type) {
72 case Field::Skip:
73 return "";
74 case Field::Line:
75 if (data.size() >= sizeof(u32)) {
76 u32 line;
77 std::memcpy(&line, data.data(), sizeof(u32));
78 return fmt::format("{}", line);
79 }
80 return "[ERROR DECODING LINE NUMBER]";
81 case Field::Message:
82 case Field::Filename:
83 case Field::Function:
84 case Field::Module:
85 case Field::Thread:
86 return Common::StringFromFixedZeroTerminatedBuffer(
87 reinterpret_cast<const char*>(data.data()), data.size());
88 default:
89 UNIMPLEMENTED();
90 }
91}
92
93Manager::Manager(Core::Reporter& reporter) : reporter(reporter) {}
94
95Manager::~Manager() = default;
96
97void Manager::SetEnabled(bool enabled) {
98 this->enabled = enabled;
99}
100
101void Manager::SetDestination(DestinationFlag destination) {
102 this->destination = destination;
103}
104
105void Manager::Log(LogMessage message) {
106 if (message.header.IsHeadLog()) {
107 InitializeLog();
108 }
109
110 current_log.emplace_back(std::move(message));
111
112 if (current_log.back().header.IsTailLog()) {
113 FinalizeLog();
114 }
115}
116
117void Manager::Flush() {
118 FinalizeLog();
119}
120
121void Manager::InitializeLog() {
122 current_log.clear();
123
124 LOG_INFO(Service_LM, "Initialized new log session");
125}
126
127void Manager::FinalizeLog() {
128 reporter.SaveLogReport(static_cast<u32>(destination), std::move(current_log));
129
130 LOG_INFO(Service_LM, "Finalized current log session");
131}
132
133} // namespace Service::LM
diff --git a/src/core/hle/service/lm/manager.h b/src/core/hle/service/lm/manager.h
new file mode 100644
index 000000000..544e636ba
--- /dev/null
+++ b/src/core/hle/service/lm/manager.h
@@ -0,0 +1,106 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8#include <ostream>
9#include <vector>
10#include "common/bit_field.h"
11#include "common/common_types.h"
12#include "common/swap.h"
13
14namespace Core {
15class Reporter;
16}
17
18namespace Service::LM {
19
20enum class DestinationFlag : u32 {
21 Default = 1,
22 UART = 2,
23 UARTSleeping = 4,
24
25 All = 0xFFFF,
26};
27
28struct MessageHeader {
29 enum Flags : u32_le {
30 IsHead = 1,
31 IsTail = 2,
32 };
33 enum Severity : u32_le {
34 Trace,
35 Info,
36 Warning,
37 Error,
38 Critical,
39 };
40
41 u64_le pid;
42 u64_le thread_context;
43 union {
44 BitField<0, 16, Flags> flags;
45 BitField<16, 8, Severity> severity;
46 BitField<24, 8, u32> verbosity;
47 };
48 u32_le payload_size;
49
50 bool IsHeadLog() const {
51 return flags & IsHead;
52 }
53 bool IsTailLog() const {
54 return flags & IsTail;
55 }
56};
57static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size");
58
59enum class Field : u8 {
60 Skip = 1,
61 Message = 2,
62 Line = 3,
63 Filename = 4,
64 Function = 5,
65 Module = 6,
66 Thread = 7,
67};
68
69std::ostream& operator<<(std::ostream& os, DestinationFlag dest);
70std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity);
71std::ostream& operator<<(std::ostream& os, Field field);
72
73using FieldMap = std::map<Field, std::vector<u8>>;
74
75struct LogMessage {
76 MessageHeader header;
77 FieldMap fields;
78};
79
80std::string FormatField(Field type, const std::vector<u8>& data);
81
82class Manager {
83public:
84 explicit Manager(Core::Reporter& reporter);
85 ~Manager();
86
87 void SetEnabled(bool enabled);
88 void SetDestination(DestinationFlag destination);
89
90 void Log(LogMessage message);
91
92 void Flush();
93
94private:
95 void InitializeLog();
96 void FinalizeLog();
97
98 bool enabled = true;
99 DestinationFlag destination = DestinationFlag::All;
100
101 std::vector<LogMessage> current_log;
102
103 Core::Reporter& reporter;
104};
105
106} // namespace Service::LM
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index a42c22d44..aa886cd3e 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -18,8 +18,8 @@
18namespace Service::NFP { 18namespace Service::NFP {
19 19
20namespace ErrCodes { 20namespace ErrCodes {
21constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP, 21[[maybe_unused]] constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
22 -1); // TODO(ogniK): Find the actual error code 22 -1); // TODO(ogniK): Find the actual error code
23constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); 23constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
24} // namespace ErrCodes 24} // namespace ErrCodes
25 25
@@ -35,7 +35,7 @@ Module::Interface::~Interface() = default;
35class IUser final : public ServiceFramework<IUser> { 35class IUser final : public ServiceFramework<IUser> {
36public: 36public:
37 IUser(Module::Interface& nfp_interface, Core::System& system) 37 IUser(Module::Interface& nfp_interface, Core::System& system)
38 : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface), system(system) { 38 : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) {
39 static const FunctionInfo functions[] = { 39 static const FunctionInfo functions[] = {
40 {0, &IUser::Initialize, "Initialize"}, 40 {0, &IUser::Initialize, "Initialize"},
41 {1, &IUser::Finalize, "Finalize"}, 41 {1, &IUser::Finalize, "Finalize"},
@@ -183,6 +183,8 @@ private:
183 case DeviceState::TagRemoved: 183 case DeviceState::TagRemoved:
184 device_state = DeviceState::Initialized; 184 device_state = DeviceState::Initialized;
185 break; 185 break;
186 default:
187 break;
186 } 188 }
187 IPC::ResponseBuilder rb{ctx, 2}; 189 IPC::ResponseBuilder rb{ctx, 2};
188 rb.Push(RESULT_SUCCESS); 190 rb.Push(RESULT_SUCCESS);
@@ -324,7 +326,6 @@ private:
324 Kernel::EventPair deactivate_event; 326 Kernel::EventPair deactivate_event;
325 Kernel::EventPair availability_change_event; 327 Kernel::EventPair availability_change_event;
326 const Module::Interface& nfp_interface; 328 const Module::Interface& nfp_interface;
327 Core::System& system;
328}; 329};
329 330
330void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { 331void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 24d1813a7..756a2af57 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -12,6 +12,13 @@
12 12
13namespace Service::NIFM { 13namespace Service::NIFM {
14 14
15enum class RequestState : u32 {
16 NotSubmitted = 1,
17 Error = 1, ///< The duplicate 1 is intentional; it means both not submitted and error on HW.
18 Pending = 2,
19 Connected = 3,
20};
21
15class IScanRequest final : public ServiceFramework<IScanRequest> { 22class IScanRequest final : public ServiceFramework<IScanRequest> {
16public: 23public:
17 explicit IScanRequest() : ServiceFramework("IScanRequest") { 24 explicit IScanRequest() : ServiceFramework("IScanRequest") {
@@ -81,7 +88,7 @@ private:
81 88
82 IPC::ResponseBuilder rb{ctx, 3}; 89 IPC::ResponseBuilder rb{ctx, 3};
83 rb.Push(RESULT_SUCCESS); 90 rb.Push(RESULT_SUCCESS);
84 rb.Push<u32>(0); 91 rb.PushEnum(RequestState::Connected);
85 } 92 }
86 93
87 void GetResult(Kernel::HLERequestContext& ctx) { 94 void GetResult(Kernel::HLERequestContext& ctx) {
@@ -189,14 +196,14 @@ private:
189 196
190 IPC::ResponseBuilder rb{ctx, 3}; 197 IPC::ResponseBuilder rb{ctx, 3};
191 rb.Push(RESULT_SUCCESS); 198 rb.Push(RESULT_SUCCESS);
192 rb.Push<u8>(0); 199 rb.Push<u8>(1);
193 } 200 }
194 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { 201 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
195 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 202 LOG_WARNING(Service_NIFM, "(STUBBED) called");
196 203
197 IPC::ResponseBuilder rb{ctx, 3}; 204 IPC::ResponseBuilder rb{ctx, 3};
198 rb.Push(RESULT_SUCCESS); 205 rb.Push(RESULT_SUCCESS);
199 rb.Push<u8>(0); 206 rb.Push<u8>(1);
200 } 207 }
201 Core::System& system; 208 Core::System& system;
202}; 209};
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 7dcdb4a07..f64535237 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -324,14 +324,14 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
324void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { 324void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
325 // Map backing memory for the font data 325 // Map backing memory for the font data
326 LOG_DEBUG(Service_NS, "called"); 326 LOG_DEBUG(Service_NS, "called");
327 Core::CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0, 327 system.CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0,
328 SHARED_FONT_MEM_SIZE, 328 SHARED_FONT_MEM_SIZE,
329 Kernel::MemoryState::Shared); 329 Kernel::MemoryState::Shared);
330 330
331 // Create shared font memory object 331 // Create shared font memory object
332 auto& kernel = system.Kernel(); 332 auto& kernel = system.Kernel();
333 impl->shared_font_mem = Kernel::SharedMemory::Create( 333 impl->shared_font_mem = Kernel::SharedMemory::Create(
334 kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, 334 kernel, system.CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
335 Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, 335 Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
336 "PL_U:shared_font_mem"); 336 "PL_U:shared_font_mem");
337 337
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 6bc053f27..07c88465e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -45,6 +45,8 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std:
45 return GetVARegions(input, output); 45 return GetVARegions(input, output);
46 case IoctlCommand::IocUnmapBufferCommand: 46 case IoctlCommand::IocUnmapBufferCommand:
47 return UnmapBuffer(input, output); 47 return UnmapBuffer(input, output);
48 default:
49 break;
48 } 50 }
49 51
50 if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) 52 if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand)
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index ff6b1abae..eb88fee1b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -38,9 +38,10 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::v
38 return IocCtrlEventUnregister(input, output); 38 return IocCtrlEventUnregister(input, output);
39 case IoctlCommand::IocCtrlEventSignalCommand: 39 case IoctlCommand::IocCtrlEventSignalCommand:
40 return IocCtrlEventSignal(input, output); 40 return IocCtrlEventSignal(input, output);
41 default:
42 UNIMPLEMENTED_MSG("Unimplemented ioctl");
43 return 0;
41 } 44 }
42 UNIMPLEMENTED_MSG("Unimplemented ioctl");
43 return 0;
44} 45}
45 46
46u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { 47u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 389ace76f..cc2192e5c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -40,9 +40,10 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input,
40 return FlushL2(input, output); 40 return FlushL2(input, output);
41 case IoctlCommand::IocGetGpuTime: 41 case IoctlCommand::IocGetGpuTime:
42 return GetGpuTime(input, output); 42 return GetGpuTime(input, output);
43 default:
44 UNIMPLEMENTED_MSG("Unimplemented ioctl");
45 return 0;
43 } 46 }
44 UNIMPLEMENTED_MSG("Unimplemented ioctl");
45 return 0;
46} 47}
47 48
48u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, 49u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 2b8d1bef6..9de0ace22 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -44,6 +44,8 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
44 return GetWaitbase(input, output); 44 return GetWaitbase(input, output);
45 case IoctlCommand::IocChannelSetTimeoutCommand: 45 case IoctlCommand::IocChannelSetTimeoutCommand:
46 return ChannelSetTimeout(input, output); 46 return ChannelSetTimeout(input, output);
47 default:
48 break;
47 } 49 }
48 50
49 if (command.group == NVGPU_IOCTL_MAGIC) { 51 if (command.group == NVGPU_IOCTL_MAGIC) {
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 831a427de..7c5302017 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -208,7 +208,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
208 AOC::InstallInterfaces(*sm, system); 208 AOC::InstallInterfaces(*sm, system);
209 APM::InstallInterfaces(system); 209 APM::InstallInterfaces(system);
210 Audio::InstallInterfaces(*sm, system); 210 Audio::InstallInterfaces(*sm, system);
211 BCAT::InstallInterfaces(*sm); 211 BCAT::InstallInterfaces(system);
212 BPC::InstallInterfaces(*sm); 212 BPC::InstallInterfaces(*sm);
213 BtDrv::InstallInterfaces(*sm, system); 213 BtDrv::InstallInterfaces(*sm, system);
214 BTM::InstallInterfaces(*sm, system); 214 BTM::InstallInterfaces(*sm, system);
@@ -226,7 +226,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
226 LBL::InstallInterfaces(*sm); 226 LBL::InstallInterfaces(*sm);
227 LDN::InstallInterfaces(*sm); 227 LDN::InstallInterfaces(*sm);
228 LDR::InstallInterfaces(*sm, system); 228 LDR::InstallInterfaces(*sm, system);
229 LM::InstallInterfaces(*sm); 229 LM::InstallInterfaces(system);
230 Migration::InstallInterfaces(*sm); 230 Migration::InstallInterfaces(*sm);
231 Mii::InstallInterfaces(*sm); 231 Mii::InstallInterfaces(*sm);
232 MM::InstallInterfaces(*sm); 232 MM::InstallInterfaces(*sm);
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index e75c700ad..f629892ae 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -150,6 +150,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
150 // Apply cheats if they exist and the program has a valid title ID 150 // Apply cheats if they exist and the program has a valid title ID
151 if (pm) { 151 if (pm) {
152 auto& system = Core::System::GetInstance(); 152 auto& system = Core::System::GetInstance();
153 system.SetCurrentProcessBuildID(nso_header.build_id);
153 const auto cheats = pm->CreateCheatList(system, nso_header.build_id); 154 const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
154 if (!cheats.empty()) { 155 if (!cheats.empty()) {
155 system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size); 156 system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size);
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 9e030789d..fa49f3dd0 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -146,7 +146,7 @@ static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
146 * using a VMA from the current process. 146 * using a VMA from the current process.
147 */ 147 */
148static u8* GetPointerFromVMA(VAddr vaddr) { 148static u8* GetPointerFromVMA(VAddr vaddr) {
149 return GetPointerFromVMA(*Core::CurrentProcess(), vaddr); 149 return GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr);
150} 150}
151 151
152template <typename T> 152template <typename T>
@@ -226,7 +226,7 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
226} 226}
227 227
228bool IsValidVirtualAddress(const VAddr vaddr) { 228bool IsValidVirtualAddress(const VAddr vaddr) {
229 return IsValidVirtualAddress(*Core::CurrentProcess(), vaddr); 229 return IsValidVirtualAddress(*Core::System::GetInstance().CurrentProcess(), vaddr);
230} 230}
231 231
232bool IsKernelVirtualAddress(const VAddr vaddr) { 232bool IsKernelVirtualAddress(const VAddr vaddr) {
@@ -387,7 +387,7 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
387} 387}
388 388
389void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { 389void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
390 ReadBlock(*Core::CurrentProcess(), src_addr, dest_buffer, size); 390 ReadBlock(*Core::System::GetInstance().CurrentProcess(), src_addr, dest_buffer, size);
391} 391}
392 392
393void Write8(const VAddr addr, const u8 data) { 393void Write8(const VAddr addr, const u8 data) {
@@ -450,7 +450,7 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
450} 450}
451 451
452void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { 452void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
453 WriteBlock(*Core::CurrentProcess(), dest_addr, src_buffer, size); 453 WriteBlock(*Core::System::GetInstance().CurrentProcess(), dest_addr, src_buffer, size);
454} 454}
455 455
456void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { 456void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) {
@@ -539,7 +539,7 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
539} 539}
540 540
541void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) { 541void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
542 CopyBlock(*Core::CurrentProcess(), dest_addr, src_addr, size); 542 CopyBlock(*Core::System::GetInstance().CurrentProcess(), dest_addr, src_addr, size);
543} 543}
544 544
545} // namespace Memory 545} // namespace Memory
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index 9c657929e..6f4af77fd 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -7,6 +7,7 @@
7 7
8#include <fmt/chrono.h> 8#include <fmt/chrono.h>
9#include <fmt/format.h> 9#include <fmt/format.h>
10#include <fmt/ostream.h>
10#include <json.hpp> 11#include <json.hpp>
11 12
12#include "common/file_util.h" 13#include "common/file_util.h"
@@ -17,6 +18,7 @@
17#include "core/hle/kernel/hle_ipc.h" 18#include "core/hle/kernel/hle_ipc.h"
18#include "core/hle/kernel/process.h" 19#include "core/hle/kernel/process.h"
19#include "core/hle/result.h" 20#include "core/hle/result.h"
21#include "core/hle/service/lm/manager.h"
20#include "core/reporter.h" 22#include "core/reporter.h"
21#include "core/settings.h" 23#include "core/settings.h"
22 24
@@ -354,6 +356,55 @@ void Reporter::SaveErrorReport(u64 title_id, ResultCode result,
354 SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); 356 SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp));
355} 357}
356 358
359void Reporter::SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const {
360 if (!IsReportingEnabled()) {
361 return;
362 }
363
364 const auto timestamp = GetTimestamp();
365 json out;
366
367 out["yuzu_version"] = GetYuzuVersionData();
368 out["report_common"] =
369 GetReportCommonData(system.CurrentProcess()->GetTitleID(), RESULT_SUCCESS, timestamp);
370
371 out["log_destination"] =
372 fmt::format("{}", static_cast<Service::LM::DestinationFlag>(destination));
373
374 auto json_messages = json::array();
375 std::transform(messages.begin(), messages.end(), std::back_inserter(json_messages),
376 [](const Service::LM::LogMessage& message) {
377 json out;
378 out["is_head"] = fmt::format("{}", message.header.IsHeadLog());
379 out["is_tail"] = fmt::format("{}", message.header.IsTailLog());
380 out["pid"] = fmt::format("{:016X}", message.header.pid);
381 out["thread_context"] =
382 fmt::format("{:016X}", message.header.thread_context);
383 out["payload_size"] = fmt::format("{:016X}", message.header.payload_size);
384 out["flags"] = fmt::format("{:04X}", message.header.flags.Value());
385 out["severity"] = fmt::format("{}", message.header.severity.Value());
386 out["verbosity"] = fmt::format("{:02X}", message.header.verbosity);
387
388 auto fields = json::array();
389 std::transform(message.fields.begin(), message.fields.end(),
390 std::back_inserter(fields), [](const auto& kv) {
391 json out;
392 out["type"] = fmt::format("{}", kv.first);
393 out["data"] =
394 Service::LM::FormatField(kv.first, kv.second);
395 return out;
396 });
397
398 out["fields"] = std::move(fields);
399 return out;
400 });
401
402 out["log_messages"] = std::move(json_messages);
403
404 SaveToFile(std::move(out),
405 GetPath("log_report", system.CurrentProcess()->GetTitleID(), timestamp));
406}
407
357void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, 408void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode,
358 std::string log_message) const { 409 std::string log_message) const {
359 if (!IsReportingEnabled()) 410 if (!IsReportingEnabled())
diff --git a/src/core/reporter.h b/src/core/reporter.h
index f08aa11fb..380941b1b 100644
--- a/src/core/reporter.h
+++ b/src/core/reporter.h
@@ -20,6 +20,10 @@ namespace Service::FileSystem {
20enum class LogMode : u32; 20enum class LogMode : u32;
21} 21}
22 22
23namespace Service::LM {
24struct LogMessage;
25} // namespace Service::LM
26
23namespace Core { 27namespace Core {
24 28
25class System; 29class System;
@@ -29,18 +33,22 @@ public:
29 explicit Reporter(System& system); 33 explicit Reporter(System& system);
30 ~Reporter(); 34 ~Reporter();
31 35
36 // Used by fatal services
32 void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp, 37 void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp,
33 u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far, 38 u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far,
34 const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace, 39 const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace,
35 u32 backtrace_size, const std::string& arch, u32 unk10) const; 40 u32 backtrace_size, const std::string& arch, u32 unk10) const;
36 41
42 // Used by syscall svcBreak
37 void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, 43 void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2,
38 std::optional<std::vector<u8>> resolved_buffer = {}) const; 44 std::optional<std::vector<u8>> resolved_buffer = {}) const;
39 45
46 // Used by HLE service handler
40 void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, 47 void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id,
41 const std::string& name, 48 const std::string& name,
42 const std::string& service_name) const; 49 const std::string& service_name) const;
43 50
51 // Used by stub applet implementation
44 void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version, 52 void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version,
45 u32 theme_color, bool startup_sound, u64 system_tick, 53 u32 theme_color, bool startup_sound, u64 system_tick,
46 std::vector<std::vector<u8>> normal_channel, 54 std::vector<std::vector<u8>> normal_channel,
@@ -55,6 +63,7 @@ public:
55 void SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data, 63 void SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data,
56 std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const; 64 std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const;
57 65
66 // Used by error applet
58 void SaveErrorReport(u64 title_id, ResultCode result, 67 void SaveErrorReport(u64 title_id, ResultCode result,
59 std::optional<std::string> custom_text_main = {}, 68 std::optional<std::string> custom_text_main = {},
60 std::optional<std::string> custom_text_detail = {}) const; 69 std::optional<std::string> custom_text_detail = {}) const;
@@ -62,6 +71,11 @@ public:
62 void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, 71 void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode,
63 std::string log_message) const; 72 std::string log_message) const;
64 73
74 // Used by lm services
75 void SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const;
76
77 // Can be used anywhere to generate a backtrace and general info report at any point during
78 // execution. Not intended to be used for anything other than debugging or testing.
65 void SaveUserReport() const; 79 void SaveUserReport() const;
66 80
67private: 81private:
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 7de3fd1e5..d1fc94060 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -103,6 +103,8 @@ void LogSettings() {
103 LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub); 103 LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub);
104 LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port); 104 LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port);
105 LogSetting("Debugging_ProgramArgs", Settings::values.program_args); 105 LogSetting("Debugging_ProgramArgs", Settings::values.program_args);
106 LogSetting("Services_BCATBackend", Settings::values.bcat_backend);
107 LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local);
106} 108}
107 109
108} // namespace Settings 110} // namespace Settings
diff --git a/src/core/settings.h b/src/core/settings.h
index 47bddfb30..9c98a9287 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -448,6 +448,10 @@ struct Values {
448 bool reporting_services; 448 bool reporting_services;
449 bool quest_flag; 449 bool quest_flag;
450 450
451 // BCAT
452 std::string bcat_backend;
453 bool bcat_boxcat_local;
454
451 // WebService 455 // WebService
452 bool enable_telemetry; 456 bool enable_telemetry;
453 std::string web_api_url; 457 std::string web_api_url;