summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/arm/cpu_interrupt_handler.cpp6
-rw-r--r--src/core/arm/cpu_interrupt_handler.h3
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp13
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp13
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.h4
-rw-r--r--src/core/core.cpp29
-rw-r--r--src/core/core.h6
-rw-r--r--src/core/core_timing.cpp37
-rw-r--r--src/core/core_timing.h23
-rw-r--r--src/core/core_timing_util.cpp1
-rw-r--r--src/core/core_timing_util.h1
-rw-r--r--src/core/cpu_manager.cpp37
-rw-r--r--src/core/crypto/aes_util.cpp23
-rw-r--r--src/core/crypto/aes_util.h9
-rw-r--r--src/core/crypto/ctr_encryption_layer.cpp9
-rw-r--r--src/core/crypto/ctr_encryption_layer.h9
-rw-r--r--src/core/crypto/key_manager.cpp456
-rw-r--r--src/core/crypto/key_manager.h14
-rw-r--r--src/core/crypto/partition_data_manager.cpp216
-rw-r--r--src/core/device_memory.cpp5
-rw-r--r--src/core/device_memory.h8
-rw-r--r--src/core/file_sys/bis_factory.cpp11
-rw-r--r--src/core/file_sys/bis_factory.h5
-rw-r--r--src/core/file_sys/card_image.cpp5
-rw-r--r--src/core/file_sys/card_image.h7
-rw-r--r--src/core/file_sys/content_archive.cpp22
-rw-r--r--src/core/file_sys/content_archive.h2
-rw-r--r--src/core/file_sys/control_metadata.cpp1
-rw-r--r--src/core/file_sys/control_metadata.h4
-rw-r--r--src/core/file_sys/ips_layer.cpp6
-rw-r--r--src/core/file_sys/kernel_executable.cpp3
-rw-r--r--src/core/file_sys/kernel_executable.h9
-rw-r--r--src/core/file_sys/mode.h9
-rw-r--r--src/core/file_sys/nca_metadata.cpp1
-rw-r--r--src/core/file_sys/nca_metadata.h2
-rw-r--r--src/core/file_sys/nca_patch.cpp86
-rw-r--r--src/core/file_sys/nca_patch.h4
-rw-r--r--src/core/file_sys/partition_filesystem.cpp8
-rw-r--r--src/core/file_sys/partition_filesystem.h8
-rw-r--r--src/core/file_sys/patch_manager.cpp141
-rw-r--r--src/core/file_sys/patch_manager.h52
-rw-r--r--src/core/file_sys/program_metadata.cpp1
-rw-r--r--src/core/file_sys/program_metadata.h2
-rw-r--r--src/core/file_sys/registered_cache.cpp161
-rw-r--r--src/core/file_sys/registered_cache.h12
-rw-r--r--src/core/file_sys/romfs.h1
-rw-r--r--src/core/file_sys/romfs_factory.cpp50
-rw-r--r--src/core/file_sys/romfs_factory.h21
-rw-r--r--src/core/file_sys/savedata_factory.cpp33
-rw-r--r--src/core/file_sys/savedata_factory.h47
-rw-r--r--src/core/file_sys/sdmc_factory.cpp2
-rw-r--r--src/core/file_sys/sdmc_factory.h2
-rw-r--r--src/core/file_sys/submission_package.cpp35
-rw-r--r--src/core/file_sys/submission_package.h6
-rw-r--r--src/core/file_sys/system_archive/mii_model.cpp18
-rw-r--r--src/core/file_sys/system_archive/ng_word.cpp42
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.cpp51
-rw-r--r--src/core/file_sys/vfs.cpp63
-rw-r--r--src/core/file_sys/vfs_libzip.cpp2
-rw-r--r--src/core/file_sys/vfs_offset.cpp7
-rw-r--r--src/core/file_sys/vfs_real.cpp267
-rw-r--r--src/core/file_sys/vfs_real.h8
-rw-r--r--src/core/file_sys/vfs_static.h8
-rw-r--r--src/core/file_sys/vfs_vector.h13
-rw-r--r--src/core/file_sys/xts_archive.cpp26
-rw-r--r--src/core/file_sys/xts_archive.h10
-rw-r--r--src/core/frontend/applets/controller.cpp81
-rw-r--r--src/core/frontend/applets/controller.h56
-rw-r--r--src/core/frontend/emu_window.h4
-rw-r--r--src/core/frontend/framebuffer_layout.h1
-rw-r--r--src/core/frontend/input.h25
-rw-r--r--src/core/hardware_interrupt_manager.cpp15
-rw-r--r--src/core/hle/ipc_helpers.h4
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp2
-rw-r--r--src/core/hle/kernel/client_session.cpp5
-rw-r--r--src/core/hle/kernel/client_session.h7
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp46
-rw-r--r--src/core/hle/kernel/hle_ipc.h30
-rw-r--r--src/core/hle/kernel/kernel.cpp11
-rw-r--r--src/core/hle/kernel/memory/page_table.cpp23
-rw-r--r--src/core/hle/kernel/memory/system_control.cpp21
-rw-r--r--src/core/hle/kernel/memory/system_control.h5
-rw-r--r--src/core/hle/kernel/scheduler.cpp39
-rw-r--r--src/core/hle/kernel/scheduler.h4
-rw-r--r--src/core/hle/kernel/server_session.cpp16
-rw-r--r--src/core/hle/kernel/server_session.h11
-rw-r--r--src/core/hle/kernel/svc.cpp2
-rw-r--r--src/core/hle/kernel/time_manager.cpp13
-rw-r--r--src/core/hle/result.h12
-rw-r--r--src/core/hle/service/acc/acc.cpp25
-rw-r--r--src/core/hle/service/acc/acc.h1
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp2
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp65
-rw-r--r--src/core/hle/service/am/am.cpp27
-rw-r--r--src/core/hle/service/am/am.h9
-rw-r--r--src/core/hle/service/am/applets/applets.cpp72
-rw-r--r--src/core/hle/service/am/applets/applets.h19
-rw-r--r--src/core/hle/service/am/applets/controller.cpp210
-rw-r--r--src/core/hle/service/am/applets/controller.h123
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp60
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.h1
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp19
-rw-r--r--src/core/hle/service/audio/audout_u.cpp4
-rw-r--r--src/core/hle/service/audio/audren_u.cpp149
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp26
-rw-r--r--src/core/hle/service/bcat/module.cpp2
-rw-r--r--src/core/hle/service/caps/caps_c.cpp16
-rw-r--r--src/core/hle/service/caps/caps_c.h3
-rw-r--r--src/core/hle/service/caps/caps_su.cpp7
-rw-r--r--src/core/hle/service/caps/caps_u.cpp15
-rw-r--r--src/core/hle/service/caps/caps_u.h1
-rw-r--r--src/core/hle/service/es/es.cpp2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp92
-rw-r--r--src/core/hle/service/filesystem/filesystem.h6
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp85
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h6
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h4
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp53
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp17
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp672
-rw-r--r--src/core/hle/service/hid/controllers/npad.h92
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp12
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h1
-rw-r--r--src/core/hle/service/hid/hid.cpp171
-rw-r--r--src/core/hle/service/hid/hid.h14
-rw-r--r--src/core/hle/service/ldr/ldr.cpp5
-rw-r--r--src/core/hle/service/mii/manager.cpp113
-rw-r--r--src/core/hle/service/nfp/nfp.cpp31
-rw-r--r--src/core/hle/service/nifm/nifm.cpp13
-rw-r--r--src/core/hle/service/ns/ns.cpp1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp202
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h79
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp70
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h52
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp74
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h63
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp33
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h6
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp36
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp21
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp27
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h24
-rw-r--r--src/core/hle/service/service.cpp13
-rw-r--r--src/core/hle/service/service.h4
-rw-r--r--src/core/hle/service/set/set.cpp2
-rw-r--r--src/core/hle/service/sm/sm.cpp13
-rw-r--r--src/core/hle/service/sm/sm.h12
-rw-r--r--src/core/hle/service/sockets/blocking_worker.h161
-rw-r--r--src/core/hle/service/sockets/bsd.cpp810
-rw-r--r--src/core/hle/service/sockets/bsd.h150
-rw-r--r--src/core/hle/service/sockets/sockets.cpp6
-rw-r--r--src/core/hle/service/sockets/sockets.h85
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp165
-rw-r--r--src/core/hle/service/sockets/sockets_translate.h48
-rw-r--r--src/core/hle/service/time/time.cpp4
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp4
-rw-r--r--src/core/hle/service/time/time_zone_service.cpp4
-rw-r--r--src/core/hle/service/vi/vi.cpp28
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp18
-rw-r--r--src/core/loader/deconstructed_rom_directory.h6
-rw-r--r--src/core/loader/elf.cpp3
-rw-r--r--src/core/loader/elf.h6
-rw-r--r--src/core/loader/kip.cpp3
-rw-r--r--src/core/loader/kip.h6
-rw-r--r--src/core/loader/loader.cpp54
-rw-r--r--src/core/loader/loader.h7
-rw-r--r--src/core/loader/nax.cpp5
-rw-r--r--src/core/loader/nax.h8
-rw-r--r--src/core/loader/nca.cpp8
-rw-r--r--src/core/loader/nca.h6
-rw-r--r--src/core/loader/nro.cpp6
-rw-r--r--src/core/loader/nro.h6
-rw-r--r--src/core/loader/nso.cpp13
-rw-r--r--src/core/loader/nso.h12
-rw-r--r--src/core/loader/nsp.cpp7
-rw-r--r--src/core/loader/nsp.h6
-rw-r--r--src/core/loader/xci.cpp7
-rw-r--r--src/core/loader/xci.h6
-rw-r--r--src/core/memory.cpp12
-rw-r--r--src/core/memory/cheat_engine.cpp79
-rw-r--r--src/core/memory/cheat_engine.h8
-rw-r--r--src/core/memory/dmnt_cheat_vm.cpp226
-rw-r--r--src/core/network/network.cpp654
-rw-r--r--src/core/network/network.h87
-rw-r--r--src/core/network/sockets.h94
-rw-r--r--src/core/perf_stats.cpp24
-rw-r--r--src/core/perf_stats.h11
-rw-r--r--src/core/reporter.cpp9
-rw-r--r--src/core/settings.cpp57
-rw-r--r--src/core/settings.h356
-rw-r--r--src/core/telemetry_session.cpp16
-rw-r--r--src/core/telemetry_session.h5
-rw-r--r--src/core/tools/freezer.cpp45
-rw-r--r--src/core/tools/freezer.h10
199 files changed, 6283 insertions, 2697 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index c42f95705..d0c405ec7 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -126,6 +126,8 @@ add_library(core STATIC
126 file_sys/vfs_vector.h 126 file_sys/vfs_vector.h
127 file_sys/xts_archive.cpp 127 file_sys/xts_archive.cpp
128 file_sys/xts_archive.h 128 file_sys/xts_archive.h
129 frontend/applets/controller.cpp
130 frontend/applets/controller.h
129 frontend/applets/error.cpp 131 frontend/applets/error.cpp
130 frontend/applets/error.h 132 frontend/applets/error.h
131 frontend/applets/general_frontend.cpp 133 frontend/applets/general_frontend.cpp
@@ -244,6 +246,8 @@ add_library(core STATIC
244 hle/service/am/applet_oe.h 246 hle/service/am/applet_oe.h
245 hle/service/am/applets/applets.cpp 247 hle/service/am/applets/applets.cpp
246 hle/service/am/applets/applets.h 248 hle/service/am/applets/applets.h
249 hle/service/am/applets/controller.cpp
250 hle/service/am/applets/controller.h
247 hle/service/am/applets/error.cpp 251 hle/service/am/applets/error.cpp
248 hle/service/am/applets/error.h 252 hle/service/am/applets/error.h
249 hle/service/am/applets/general_backend.cpp 253 hle/service/am/applets/general_backend.cpp
@@ -491,6 +495,7 @@ add_library(core STATIC
491 hle/service/sm/controller.h 495 hle/service/sm/controller.h
492 hle/service/sm/sm.cpp 496 hle/service/sm/sm.cpp
493 hle/service/sm/sm.h 497 hle/service/sm/sm.h
498 hle/service/sockets/blocking_worker.h
494 hle/service/sockets/bsd.cpp 499 hle/service/sockets/bsd.cpp
495 hle/service/sockets/bsd.h 500 hle/service/sockets/bsd.h
496 hle/service/sockets/ethc.cpp 501 hle/service/sockets/ethc.cpp
@@ -501,6 +506,8 @@ add_library(core STATIC
501 hle/service/sockets/sfdnsres.h 506 hle/service/sockets/sfdnsres.h
502 hle/service/sockets/sockets.cpp 507 hle/service/sockets/sockets.cpp
503 hle/service/sockets/sockets.h 508 hle/service/sockets/sockets.h
509 hle/service/sockets/sockets_translate.cpp
510 hle/service/sockets/sockets_translate.h
504 hle/service/spl/csrng.cpp 511 hle/service/spl/csrng.cpp
505 hle/service/spl/csrng.h 512 hle/service/spl/csrng.h
506 hle/service/spl/module.cpp 513 hle/service/spl/module.cpp
@@ -586,6 +593,9 @@ add_library(core STATIC
586 memory/dmnt_cheat_vm.h 593 memory/dmnt_cheat_vm.h
587 memory.cpp 594 memory.cpp
588 memory.h 595 memory.h
596 network/network.cpp
597 network/network.h
598 network/sockets.h
589 perf_stats.cpp 599 perf_stats.cpp
590 perf_stats.h 600 perf_stats.h
591 reporter.cpp 601 reporter.cpp
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
index df0350881..9c8898700 100644
--- a/src/core/arm/cpu_interrupt_handler.cpp
+++ b/src/core/arm/cpu_interrupt_handler.cpp
@@ -7,9 +7,7 @@
7 7
8namespace Core { 8namespace Core {
9 9
10CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} { 10CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {}
11 interrupt_event = std::make_unique<Common::Event>();
12}
13 11
14CPUInterruptHandler::~CPUInterruptHandler() = default; 12CPUInterruptHandler::~CPUInterruptHandler() = default;
15 13
@@ -17,7 +15,7 @@ void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
17 if (is_interrupted_) { 15 if (is_interrupted_) {
18 interrupt_event->Set(); 16 interrupt_event->Set();
19 } 17 }
20 this->is_interrupted = is_interrupted_; 18 is_interrupted = is_interrupted_;
21} 19}
22 20
23void CPUInterruptHandler::AwaitInterrupt() { 21void CPUInterruptHandler::AwaitInterrupt() {
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
index 3d062d326..71e582f79 100644
--- a/src/core/arm/cpu_interrupt_handler.h
+++ b/src/core/arm/cpu_interrupt_handler.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
8 9
9namespace Common { 10namespace Common {
@@ -32,8 +33,8 @@ public:
32 void AwaitInterrupt(); 33 void AwaitInterrupt();
33 34
34private: 35private:
35 bool is_interrupted{};
36 std::unique_ptr<Common::Event> interrupt_event; 36 std::unique_ptr<Common::Event> interrupt_event;
37 std::atomic_bool is_interrupted{false};
37}; 38};
38 39
39} // namespace Core 40} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 443ca72eb..b5f28a86e 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -143,7 +143,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
143 config.wall_clock_cntpct = uses_wall_clock; 143 config.wall_clock_cntpct = uses_wall_clock;
144 144
145 // Safe optimizations 145 // Safe optimizations
146 if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) { 146 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
147 if (!Settings::values.cpuopt_page_tables) { 147 if (!Settings::values.cpuopt_page_tables) {
148 config.page_table = nullptr; 148 config.page_table = nullptr;
149 } 149 }
@@ -170,6 +170,17 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
170 } 170 }
171 } 171 }
172 172
173 // Unsafe optimizations
174 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) {
175 config.unsafe_optimizations = true;
176 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
177 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
178 }
179 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
180 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
181 }
182 }
183
173 return std::make_unique<Dynarmic::A32::Jit>(config); 184 return std::make_unique<Dynarmic::A32::Jit>(config);
174} 185}
175 186
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index a63a04a25..ce9968724 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -195,7 +195,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
195 config.wall_clock_cntpct = uses_wall_clock; 195 config.wall_clock_cntpct = uses_wall_clock;
196 196
197 // Safe optimizations 197 // Safe optimizations
198 if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) { 198 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
199 if (!Settings::values.cpuopt_page_tables) { 199 if (!Settings::values.cpuopt_page_tables) {
200 config.page_table = nullptr; 200 config.page_table = nullptr;
201 } 201 }
@@ -222,6 +222,17 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
222 } 222 }
223 } 223 }
224 224
225 // Unsafe optimizations
226 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) {
227 config.unsafe_optimizations = true;
228 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
229 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
230 }
231 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
232 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
233 }
234 }
235
225 return std::make_shared<Dynarmic::A64::Jit>(config); 236 return std::make_shared<Dynarmic::A64::Jit>(config);
226} 237}
227 238
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
index 54556e0f9..caefc09f4 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -34,7 +34,7 @@ std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigne
34 CoprocReg CRm, unsigned opc2) { 34 CoprocReg CRm, unsigned opc2) {
35 LOG_CRITICAL(Core_ARM, "CP15: cdp{} p15, {}, {}, {}, {}, {}", two ? "2" : "", opc1, CRd, CRn, 35 LOG_CRITICAL(Core_ARM, "CP15: cdp{} p15, {}, {}, {}, {}, {}", two ? "2" : "", opc1, CRd, CRn,
36 CRm, opc2); 36 CRm, opc2);
37 return {}; 37 return std::nullopt;
38} 38}
39 39
40CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, 40CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn,
@@ -115,7 +115,7 @@ std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_trans
115 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "", 115 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "",
116 long_transfer ? "l" : "", CRd); 116 long_transfer ? "l" : "", CRd);
117 } 117 }
118 return {}; 118 return std::nullopt;
119} 119}
120 120
121std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, 121std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
@@ -127,7 +127,7 @@ std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_tran
127 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "", 127 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "",
128 long_transfer ? "l" : "", CRd); 128 long_transfer ? "l" : "", CRd);
129 } 129 }
130 return {}; 130 return std::nullopt;
131} 131}
132 132
133} // namespace Core 133} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
index 7356d252e..dc6f4af3a 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
@@ -35,8 +35,8 @@ public:
35 std::optional<u8> option) override; 35 std::optional<u8> option) override;
36 36
37 ARM_Dynarmic_32& parent; 37 ARM_Dynarmic_32& parent;
38 u32 uprw; 38 u32 uprw = 0;
39 u32 uro; 39 u32 uro = 0;
40}; 40};
41 41
42} // namespace Core 42} // namespace Core
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 69a1aa0a5..81e8cc338 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -43,6 +43,7 @@
43#include "core/loader/loader.h" 43#include "core/loader/loader.h"
44#include "core/memory.h" 44#include "core/memory.h"
45#include "core/memory/cheat_engine.h" 45#include "core/memory/cheat_engine.h"
46#include "core/network/network.h"
46#include "core/perf_stats.h" 47#include "core/perf_stats.h"
47#include "core/reporter.h" 48#include "core/reporter.h"
48#include "core/settings.h" 49#include "core/settings.h"
@@ -112,7 +113,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
112 return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName()); 113 return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
113 } 114 }
114 115
115 if (FileUtil::IsDirectory(path)) 116 if (Common::FS::IsDirectory(path))
116 return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read); 117 return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read);
117 118
118 return vfs->OpenFile(path, FileSys::Mode::Read); 119 return vfs->OpenFile(path, FileSys::Mode::Read);
@@ -145,7 +146,7 @@ struct System::Impl {
145 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { 146 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
146 LOG_DEBUG(HW_Memory, "initialized OK"); 147 LOG_DEBUG(HW_Memory, "initialized OK");
147 148
148 device_memory = std::make_unique<Core::DeviceMemory>(system); 149 device_memory = std::make_unique<Core::DeviceMemory>();
149 150
150 is_multicore = Settings::values.use_multi_core.GetValue(); 151 is_multicore = Settings::values.use_multi_core.GetValue();
151 is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue(); 152 is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue();
@@ -177,7 +178,7 @@ struct System::Impl {
177 arp_manager.ResetAll(); 178 arp_manager.ResetAll();
178 179
179 telemetry_session = std::make_unique<Core::TelemetrySession>(); 180 telemetry_session = std::make_unique<Core::TelemetrySession>();
180 service_manager = std::make_shared<Service::SM::ServiceManager>(); 181 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
181 182
182 Service::Init(service_manager, system); 183 Service::Init(service_manager, system);
183 GDBStub::DeferStart(); 184 GDBStub::DeferStart();
@@ -187,7 +188,6 @@ struct System::Impl {
187 if (!gpu_core) { 188 if (!gpu_core) {
188 return ResultStatus::ErrorVideoCore; 189 return ResultStatus::ErrorVideoCore;
189 } 190 }
190 gpu_core->Renderer().Rasterizer().SetupDirtyFlags();
191 191
192 is_powered_on = true; 192 is_powered_on = true;
193 exit_lock = false; 193 exit_lock = false;
@@ -221,7 +221,7 @@ struct System::Impl {
221 telemetry_session->AddInitialInfo(*app_loader); 221 telemetry_session->AddInitialInfo(*app_loader);
222 auto main_process = 222 auto main_process =
223 Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland); 223 Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland);
224 const auto [load_result, load_parameters] = app_loader->Load(*main_process); 224 const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
225 if (load_result != Loader::ResultStatus::Success) { 225 if (load_result != Loader::ResultStatus::Success) {
226 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); 226 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
227 Shutdown(); 227 Shutdown();
@@ -268,14 +268,14 @@ struct System::Impl {
268 // Log last frame performance stats if game was loded 268 // Log last frame performance stats if game was loded
269 if (perf_stats) { 269 if (perf_stats) {
270 const auto perf_results = GetAndResetPerfStats(); 270 const auto perf_results = GetAndResetPerfStats();
271 telemetry_session->AddField(Telemetry::FieldType::Performance, 271 constexpr auto performance = Common::Telemetry::FieldType::Performance;
272 "Shutdown_EmulationSpeed", 272
273 telemetry_session->AddField(performance, "Shutdown_EmulationSpeed",
273 perf_results.emulation_speed * 100.0); 274 perf_results.emulation_speed * 100.0);
274 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate", 275 telemetry_session->AddField(performance, "Shutdown_Framerate", perf_results.game_fps);
275 perf_results.game_fps); 276 telemetry_session->AddField(performance, "Shutdown_Frametime",
276 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
277 perf_results.frametime * 1000.0); 277 perf_results.frametime * 1000.0);
278 telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS", 278 telemetry_session->AddField(performance, "Mean_Frametime_MS",
279 perf_stats->GetMeanFrametime()); 279 perf_stats->GetMeanFrametime());
280 } 280 }
281 281
@@ -394,6 +394,9 @@ struct System::Impl {
394 /// Telemetry session for this emulation session 394 /// Telemetry session for this emulation session
395 std::unique_ptr<Core::TelemetrySession> telemetry_session; 395 std::unique_ptr<Core::TelemetrySession> telemetry_session;
396 396
397 /// Network instance
398 Network::NetworkInstance network_instance;
399
397 ResultStatus status = ResultStatus::Success; 400 ResultStatus status = ResultStatus::Success;
398 std::string status_details = ""; 401 std::string status_details = "";
399 402
@@ -626,11 +629,11 @@ Loader::AppLoader& System::GetAppLoader() const {
626 return *impl->app_loader; 629 return *impl->app_loader;
627} 630}
628 631
629void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { 632void System::SetFilesystem(FileSys::VirtualFilesystem vfs) {
630 impl->virtual_filesystem = std::move(vfs); 633 impl->virtual_filesystem = std::move(vfs);
631} 634}
632 635
633std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const { 636FileSys::VirtualFilesystem System::GetFilesystem() const {
634 return impl->virtual_filesystem; 637 return impl->virtual_filesystem;
635} 638}
636 639
diff --git a/src/core/core.h b/src/core/core.h
index 5c6cfbffe..27efe30bb 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -120,7 +120,7 @@ public:
120 * Gets the instance of the System singleton class. 120 * Gets the instance of the System singleton class.
121 * @returns Reference to the instance of the System singleton class. 121 * @returns Reference to the instance of the System singleton class.
122 */ 122 */
123 static System& GetInstance() { 123 [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() {
124 return s_instance; 124 return s_instance;
125 } 125 }
126 126
@@ -316,9 +316,9 @@ public:
316 Service::SM::ServiceManager& ServiceManager(); 316 Service::SM::ServiceManager& ServiceManager();
317 const Service::SM::ServiceManager& ServiceManager() const; 317 const Service::SM::ServiceManager& ServiceManager() const;
318 318
319 void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs); 319 void SetFilesystem(FileSys::VirtualFilesystem vfs);
320 320
321 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; 321 FileSys::VirtualFilesystem GetFilesystem() const;
322 322
323 void RegisterCheatList(const std::vector<Memory::CheatEntry>& list, 323 void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
324 const std::array<u8, 0x20>& build_id, VAddr main_region_begin, 324 const std::array<u8, 0x20>& build_id, VAddr main_region_begin,
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index a63e60461..e6c8461a5 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -7,14 +7,14 @@
7#include <string> 7#include <string>
8#include <tuple> 8#include <tuple>
9 9
10#include "common/assert.h"
11#include "common/microprofile.h" 10#include "common/microprofile.h"
12#include "core/core_timing.h" 11#include "core/core_timing.h"
13#include "core/core_timing_util.h" 12#include "core/core_timing_util.h"
13#include "core/hardware_properties.h"
14 14
15namespace Core::Timing { 15namespace Core::Timing {
16 16
17constexpr u64 MAX_SLICE_LENGTH = 4000; 17constexpr s64 MAX_SLICE_LENGTH = 4000;
18 18
19std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { 19std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
20 return std::make_shared<EventType>(std::move(callback), std::move(name)); 20 return std::make_shared<EventType>(std::move(callback), std::move(name));
@@ -23,7 +23,7 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac
23struct CoreTiming::Event { 23struct CoreTiming::Event {
24 u64 time; 24 u64 time;
25 u64 fifo_order; 25 u64 fifo_order;
26 u64 userdata; 26 std::uintptr_t user_data;
27 std::weak_ptr<EventType> type; 27 std::weak_ptr<EventType> type;
28 28
29 // Sort by time, unless the times are the same, in which case sort by 29 // Sort by time, unless the times are the same, in which case sort by
@@ -37,10 +37,8 @@ struct CoreTiming::Event {
37 } 37 }
38}; 38};
39 39
40CoreTiming::CoreTiming() { 40CoreTiming::CoreTiming()
41 clock = 41 : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {}
42 Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ);
43}
44 42
45CoreTiming::~CoreTiming() = default; 43CoreTiming::~CoreTiming() = default;
46 44
@@ -53,12 +51,12 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) {
53 instance.ThreadLoop(); 51 instance.ThreadLoop();
54} 52}
55 53
56void CoreTiming::Initialize(std::function<void(void)>&& on_thread_init_) { 54void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
57 on_thread_init = std::move(on_thread_init_); 55 on_thread_init = std::move(on_thread_init_);
58 event_fifo_id = 0; 56 event_fifo_id = 0;
59 shutting_down = false; 57 shutting_down = false;
60 ticks = 0; 58 ticks = 0;
61 const auto empty_timed_callback = [](u64, s64) {}; 59 const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {};
62 ev_lost = CreateEvent("_lost_event", empty_timed_callback); 60 ev_lost = CreateEvent("_lost_event", empty_timed_callback);
63 if (is_multicore) { 61 if (is_multicore) {
64 timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); 62 timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this));
@@ -106,23 +104,25 @@ bool CoreTiming::HasPendingEvents() const {
106 return !(wait_set && event_queue.empty()); 104 return !(wait_set && event_queue.empty());
107} 105}
108 106
109void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type, 107void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
110 u64 userdata) { 108 const std::shared_ptr<EventType>& event_type,
109 std::uintptr_t user_data) {
111 { 110 {
112 std::scoped_lock scope{basic_lock}; 111 std::scoped_lock scope{basic_lock};
113 const u64 timeout = static_cast<u64>(GetGlobalTimeNs().count() + ns_into_future); 112 const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count());
114 113
115 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); 114 event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type});
116 115
117 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 116 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
118 } 117 }
119 event.Set(); 118 event.Set();
120} 119}
121 120
122void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { 121void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
122 std::uintptr_t user_data) {
123 std::scoped_lock scope{basic_lock}; 123 std::scoped_lock scope{basic_lock};
124 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { 124 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
125 return e.type.lock().get() == event_type.get() && e.userdata == userdata; 125 return e.type.lock().get() == event_type.get() && e.user_data == user_data;
126 }); 126 });
127 127
128 // Removing random items breaks the invariant so we have to re-establish it. 128 // Removing random items breaks the invariant so we have to re-establish it.
@@ -134,7 +134,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u
134 134
135void CoreTiming::AddTicks(u64 ticks) { 135void CoreTiming::AddTicks(u64 ticks) {
136 this->ticks += ticks; 136 this->ticks += ticks;
137 downcount -= ticks; 137 downcount -= static_cast<s64>(ticks);
138} 138}
139 139
140void CoreTiming::Idle() { 140void CoreTiming::Idle() {
@@ -195,8 +195,9 @@ std::optional<s64> CoreTiming::Advance() {
195 event_queue.pop_back(); 195 event_queue.pop_back();
196 basic_lock.unlock(); 196 basic_lock.unlock();
197 197
198 if (auto event_type{evt.type.lock()}) { 198 if (const auto event_type{evt.type.lock()}) {
199 event_type->callback(evt.userdata, global_timer - evt.time); 199 event_type->callback(
200 evt.user_data, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)});
200 } 201 }
201 202
202 basic_lock.lock(); 203 basic_lock.lock();
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 72faaab64..b0b6036e4 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -17,14 +17,13 @@
17#include "common/common_types.h" 17#include "common/common_types.h"
18#include "common/spin_lock.h" 18#include "common/spin_lock.h"
19#include "common/thread.h" 19#include "common/thread.h"
20#include "common/threadsafe_queue.h"
21#include "common/wall_clock.h" 20#include "common/wall_clock.h"
22#include "core/hardware_properties.h"
23 21
24namespace Core::Timing { 22namespace Core::Timing {
25 23
26/// A callback that may be scheduled for a particular core timing event. 24/// A callback that may be scheduled for a particular core timing event.
27using TimedCallback = std::function<void(u64 userdata, s64 cycles_late)>; 25using TimedCallback =
26 std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>;
28 27
29/// Contains the characteristics of a particular event. 28/// Contains the characteristics of a particular event.
30struct EventType { 29struct EventType {
@@ -42,12 +41,12 @@ struct EventType {
42 * in main CPU clock cycles. 41 * in main CPU clock cycles.
43 * 42 *
44 * To schedule an event, you first have to register its type. This is where you pass in the 43 * To schedule an event, you first have to register its type. This is where you pass in the
45 * callback. You then schedule events using the type id you get back. 44 * callback. You then schedule events using the type ID you get back.
46 * 45 *
47 * The int cyclesLate that the callbacks get is how many cycles late it was. 46 * The s64 ns_late that the callbacks get is how many ns late it was.
48 * So to schedule a new event on a regular basis: 47 * So to schedule a new event on a regular basis:
49 * inside callback: 48 * inside callback:
50 * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") 49 * ScheduleEvent(period_in_ns - ns_late, callback, "whatever")
51 */ 50 */
52class CoreTiming { 51class CoreTiming {
53public: 52public:
@@ -62,7 +61,7 @@ public:
62 61
63 /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is 62 /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
64 /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. 63 /// required to end slice - 1 and start slice 0 before the first cycle of code is executed.
65 void Initialize(std::function<void(void)>&& on_thread_init_); 64 void Initialize(std::function<void()>&& on_thread_init_);
66 65
67 /// Tears down all timing related functionality. 66 /// Tears down all timing related functionality.
68 void Shutdown(); 67 void Shutdown();
@@ -95,10 +94,10 @@ public:
95 bool HasPendingEvents() const; 94 bool HasPendingEvents() const;
96 95
97 /// Schedules an event in core timing 96 /// Schedules an event in core timing
98 void ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type, 97 void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
99 u64 userdata = 0); 98 const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0);
100 99
101 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); 100 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data);
102 101
103 /// We only permit one event of each type in the queue at a time. 102 /// We only permit one event of each type in the queue at a time.
104 void RemoveEvent(const std::shared_ptr<EventType>& event_type); 103 void RemoveEvent(const std::shared_ptr<EventType>& event_type);
@@ -141,8 +140,6 @@ private:
141 140
142 u64 global_timer = 0; 141 u64 global_timer = 0;
143 142
144 std::chrono::nanoseconds start_point;
145
146 // The queue is a min-heap using std::make_heap/push_heap/pop_heap. 143 // The queue is a min-heap using std::make_heap/push_heap/pop_heap.
147 // We don't use std::priority_queue because we need to be able to serialize, unserialize and 144 // We don't use std::priority_queue because we need to be able to serialize, unserialize and
148 // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't 145 // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't
@@ -161,7 +158,7 @@ private:
161 std::atomic<bool> wait_set{}; 158 std::atomic<bool> wait_set{};
162 std::atomic<bool> shutting_down{}; 159 std::atomic<bool> shutting_down{};
163 std::atomic<bool> has_started{}; 160 std::atomic<bool> has_started{};
164 std::function<void(void)> on_thread_init{}; 161 std::function<void()> on_thread_init{};
165 162
166 bool is_multicore{}; 163 bool is_multicore{};
167 164
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp
index aefc63663..8ce8e602e 100644
--- a/src/core/core_timing_util.cpp
+++ b/src/core/core_timing_util.cpp
@@ -8,6 +8,7 @@
8#include <limits> 8#include <limits>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/uint128.h" 10#include "common/uint128.h"
11#include "core/hardware_properties.h"
11 12
12namespace Core::Timing { 13namespace Core::Timing {
13 14
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
index 2ed979e14..e4a046bf9 100644
--- a/src/core/core_timing_util.h
+++ b/src/core/core_timing_util.h
@@ -6,7 +6,6 @@
6 6
7#include <chrono> 7#include <chrono>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hardware_properties.h"
10 9
11namespace Core::Timing { 10namespace Core::Timing {
12 11
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 32afcf3ae..688b99eba 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -41,9 +41,9 @@ void CpuManager::Shutdown() {
41 running_mode = false; 41 running_mode = false;
42 Pause(false); 42 Pause(false);
43 if (is_multicore) { 43 if (is_multicore) {
44 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 44 for (auto& data : core_data) {
45 core_data[core].host_thread->join(); 45 data.host_thread->join();
46 core_data[core].host_thread.reset(); 46 data.host_thread.reset();
47 } 47 }
48 } else { 48 } else {
49 core_data[0].host_thread->join(); 49 core_data[0].host_thread->join();
@@ -52,15 +52,15 @@ void CpuManager::Shutdown() {
52} 52}
53 53
54std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { 54std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
55 return std::function<void(void*)>(GuestThreadFunction); 55 return GuestThreadFunction;
56} 56}
57 57
58std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { 58std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
59 return std::function<void(void*)>(IdleThreadFunction); 59 return IdleThreadFunction;
60} 60}
61 61
62std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() { 62std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() {
63 return std::function<void(void*)>(SuspendThreadFunction); 63 return SuspendThreadFunction;
64} 64}
65 65
66void CpuManager::GuestThreadFunction(void* cpu_manager_) { 66void CpuManager::GuestThreadFunction(void* cpu_manager_) {
@@ -166,25 +166,23 @@ void CpuManager::MultiCorePause(bool paused) {
166 bool all_not_barrier = false; 166 bool all_not_barrier = false;
167 while (!all_not_barrier) { 167 while (!all_not_barrier) {
168 all_not_barrier = true; 168 all_not_barrier = true;
169 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 169 for (const auto& data : core_data) {
170 all_not_barrier &= 170 all_not_barrier &= !data.is_running.load() && data.initialized.load();
171 !core_data[core].is_running.load() && core_data[core].initialized.load();
172 } 171 }
173 } 172 }
174 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 173 for (auto& data : core_data) {
175 core_data[core].enter_barrier->Set(); 174 data.enter_barrier->Set();
176 } 175 }
177 if (paused_state.load()) { 176 if (paused_state.load()) {
178 bool all_barrier = false; 177 bool all_barrier = false;
179 while (!all_barrier) { 178 while (!all_barrier) {
180 all_barrier = true; 179 all_barrier = true;
181 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 180 for (const auto& data : core_data) {
182 all_barrier &= 181 all_barrier &= data.is_paused.load() && data.initialized.load();
183 core_data[core].is_paused.load() && core_data[core].initialized.load();
184 } 182 }
185 } 183 }
186 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 184 for (auto& data : core_data) {
187 core_data[core].exit_barrier->Set(); 185 data.exit_barrier->Set();
188 } 186 }
189 } 187 }
190 } else { 188 } else {
@@ -192,9 +190,8 @@ void CpuManager::MultiCorePause(bool paused) {
192 bool all_barrier = false; 190 bool all_barrier = false;
193 while (!all_barrier) { 191 while (!all_barrier) {
194 all_barrier = true; 192 all_barrier = true;
195 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 193 for (const auto& data : core_data) {
196 all_barrier &= 194 all_barrier &= data.is_paused.load() && data.initialized.load();
197 core_data[core].is_paused.load() && core_data[core].initialized.load();
198 } 195 }
199 } 196 }
200 /// Don't release the barrier 197 /// Don't release the barrier
@@ -331,7 +328,7 @@ void CpuManager::RunThread(std::size_t core) {
331 system.RegisterCoreThread(core); 328 system.RegisterCoreThread(core);
332 std::string name; 329 std::string name;
333 if (is_multicore) { 330 if (is_multicore) {
334 name = "yuzu:CoreCPUThread_" + std::to_string(core); 331 name = "yuzu:CPUCore_" + std::to_string(core);
335 } else { 332 } else {
336 name = "yuzu:CPUThread"; 333 name = "yuzu:CPUThread";
337 } 334 }
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp
index 4be76bb43..6a9734812 100644
--- a/src/core/crypto/aes_util.cpp
+++ b/src/core/crypto/aes_util.cpp
@@ -2,6 +2,7 @@
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 <array>
5#include <mbedtls/cipher.h> 6#include <mbedtls/cipher.h>
6#include "common/assert.h" 7#include "common/assert.h"
7#include "common/logging/log.h" 8#include "common/logging/log.h"
@@ -10,8 +11,10 @@
10 11
11namespace Core::Crypto { 12namespace Core::Crypto {
12namespace { 13namespace {
13std::vector<u8> CalculateNintendoTweak(std::size_t sector_id) { 14using NintendoTweak = std::array<u8, 16>;
14 std::vector<u8> out(0x10); 15
16NintendoTweak CalculateNintendoTweak(std::size_t sector_id) {
17 NintendoTweak out{};
15 for (std::size_t i = 0xF; i <= 0xF; --i) { 18 for (std::size_t i = 0xF; i <= 0xF; --i) {
16 out[i] = sector_id & 0xFF; 19 out[i] = sector_id & 0xFF;
17 sector_id >>= 8; 20 sector_id >>= 8;
@@ -64,13 +67,6 @@ AESCipher<Key, KeySize>::~AESCipher() {
64} 67}
65 68
66template <typename Key, std::size_t KeySize> 69template <typename Key, std::size_t KeySize>
67void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) {
68 ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) ||
69 mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0,
70 "Failed to set IV on mbedtls ciphers.");
71}
72
73template <typename Key, std::size_t KeySize>
74void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const { 70void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const {
75 auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context; 71 auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
76 72
@@ -120,10 +116,17 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8*
120 116
121 for (std::size_t i = 0; i < size; i += sector_size) { 117 for (std::size_t i = 0; i < size; i += sector_size) {
122 SetIV(CalculateNintendoTweak(sector_id++)); 118 SetIV(CalculateNintendoTweak(sector_id++));
123 Transcode<u8, u8>(src + i, sector_size, dest + i, op); 119 Transcode(src + i, sector_size, dest + i, op);
124 } 120 }
125} 121}
126 122
123template <typename Key, std::size_t KeySize>
124void AESCipher<Key, KeySize>::SetIVImpl(const u8* data, std::size_t size) {
125 ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, data, size) ||
126 mbedtls_cipher_set_iv(&ctx->decryption_context, data, size)) == 0,
127 "Failed to set IV on mbedtls ciphers.");
128}
129
127template class AESCipher<Key128>; 130template class AESCipher<Key128>;
128template class AESCipher<Key256>; 131template class AESCipher<Key256>;
129} // namespace Core::Crypto 132} // namespace Core::Crypto
diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h
index edc4ab910..e2a304186 100644
--- a/src/core/crypto/aes_util.h
+++ b/src/core/crypto/aes_util.h
@@ -6,7 +6,6 @@
6 6
7#include <memory> 7#include <memory>
8#include <type_traits> 8#include <type_traits>
9#include <vector>
10#include "common/common_types.h" 9#include "common/common_types.h"
11#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs.h"
12 11
@@ -32,10 +31,12 @@ class AESCipher {
32 31
33public: 32public:
34 AESCipher(Key key, Mode mode); 33 AESCipher(Key key, Mode mode);
35
36 ~AESCipher(); 34 ~AESCipher();
37 35
38 void SetIV(std::vector<u8> iv); 36 template <typename ContiguousContainer>
37 void SetIV(const ContiguousContainer& container) {
38 SetIVImpl(std::data(container), std::size(container));
39 }
39 40
40 template <typename Source, typename Dest> 41 template <typename Source, typename Dest>
41 void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const { 42 void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const {
@@ -59,6 +60,8 @@ public:
59 std::size_t sector_size, Op op); 60 std::size_t sector_size, Op op);
60 61
61private: 62private:
63 void SetIVImpl(const u8* data, std::size_t size);
64
62 std::unique_ptr<CipherContext> ctx; 65 std::unique_ptr<CipherContext> ctx;
63}; 66};
64} // namespace Core::Crypto 67} // namespace Core::Crypto
diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp
index 902841c77..5c84bb0a4 100644
--- a/src/core/crypto/ctr_encryption_layer.cpp
+++ b/src/core/crypto/ctr_encryption_layer.cpp
@@ -2,6 +2,7 @@
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 <algorithm>
5#include <cstring> 6#include <cstring>
6#include "common/assert.h" 7#include "common/assert.h"
7#include "core/crypto/ctr_encryption_layer.h" 8#include "core/crypto/ctr_encryption_layer.h"
@@ -10,8 +11,7 @@ namespace Core::Crypto {
10 11
11CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, 12CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_,
12 std::size_t base_offset) 13 std::size_t base_offset)
13 : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR), 14 : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR) {}
14 iv(16, 0) {}
15 15
16std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const { 16std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const {
17 if (length == 0) 17 if (length == 0)
@@ -39,9 +39,8 @@ std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t o
39 return read + Read(data + read, length - read, offset + read); 39 return read + Read(data + read, length - read, offset + read);
40} 40}
41 41
42void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) { 42void CTREncryptionLayer::SetIV(const IVData& iv_) {
43 const auto length = std::min(iv_.size(), iv.size()); 43 iv = iv_;
44 iv.assign(iv_.cbegin(), iv_.cbegin() + length);
45} 44}
46 45
47void CTREncryptionLayer::UpdateIV(std::size_t offset) const { 46void CTREncryptionLayer::UpdateIV(std::size_t offset) const {
diff --git a/src/core/crypto/ctr_encryption_layer.h b/src/core/crypto/ctr_encryption_layer.h
index a7bf810f4..a2429f001 100644
--- a/src/core/crypto/ctr_encryption_layer.h
+++ b/src/core/crypto/ctr_encryption_layer.h
@@ -4,7 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <vector> 7#include <array>
8
8#include "core/crypto/aes_util.h" 9#include "core/crypto/aes_util.h"
9#include "core/crypto/encryption_layer.h" 10#include "core/crypto/encryption_layer.h"
10#include "core/crypto/key_manager.h" 11#include "core/crypto/key_manager.h"
@@ -14,18 +15,20 @@ namespace Core::Crypto {
14// Sits on top of a VirtualFile and provides CTR-mode AES decription. 15// Sits on top of a VirtualFile and provides CTR-mode AES decription.
15class CTREncryptionLayer : public EncryptionLayer { 16class CTREncryptionLayer : public EncryptionLayer {
16public: 17public:
18 using IVData = std::array<u8, 16>;
19
17 CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset); 20 CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset);
18 21
19 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; 22 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
20 23
21 void SetIV(const std::vector<u8>& iv); 24 void SetIV(const IVData& iv);
22 25
23private: 26private:
24 std::size_t base_offset; 27 std::size_t base_offset;
25 28
26 // Must be mutable as operations modify cipher contexts. 29 // Must be mutable as operations modify cipher contexts.
27 mutable AESCipher<Key128> cipher; 30 mutable AESCipher<Key128> cipher;
28 mutable std::vector<u8> iv; 31 mutable IVData iv{};
29 32
30 void UpdateIV(std::size_t offset) const; 33 void UpdateIV(std::size_t offset) const;
31}; 34};
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index f87fe0abc..65d246050 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -23,7 +23,6 @@
23#include "common/hex_util.h" 23#include "common/hex_util.h"
24#include "common/logging/log.h" 24#include "common/logging/log.h"
25#include "common/string_util.h" 25#include "common/string_util.h"
26#include "core/core.h"
27#include "core/crypto/aes_util.h" 26#include "core/crypto/aes_util.h"
28#include "core/crypto/key_manager.h" 27#include "core/crypto/key_manager.h"
29#include "core/crypto/partition_data_manager.h" 28#include "core/crypto/partition_data_manager.h"
@@ -36,18 +35,86 @@
36#include "core/settings.h" 35#include "core/settings.h"
37 36
38namespace Core::Crypto { 37namespace Core::Crypto {
38namespace {
39 39
40constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; 40constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
41constexpr u64 FULL_TICKET_SIZE = 0x400; 41constexpr u64 FULL_TICKET_SIZE = 0x400;
42 42
43using namespace Common; 43using Common::AsArray;
44 44
45const std::array<SHA256Hash, 2> eticket_source_hashes{ 45// clang-format off
46 "B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source 46constexpr std::array eticket_source_hashes{
47 "E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source 47 AsArray("B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"), // eticket_rsa_kek_source
48 AsArray("E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"), // eticket_rsa_kekek_source
48}; 49};
50// clang-format on
49 51
50const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ 52constexpr std::array<std::pair<std::string_view, KeyIndex<S128KeyType>>, 30> s128_file_id{{
53 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
54 {"eticket_rsa_kek_source",
55 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
56 {"eticket_rsa_kekek_source",
57 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
58 {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
59 {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
60 {"rsa_oaep_kek_generation_source",
61 {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
62 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
63 {"aes_kek_generation_source",
64 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
65 {"aes_key_generation_source",
66 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
67 {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
68 {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
69 {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
70 {"key_area_key_application_source",
71 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
72 static_cast<u64>(KeyAreaKeyType::Application)}},
73 {"key_area_key_ocean_source",
74 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
75 static_cast<u64>(KeyAreaKeyType::Ocean)}},
76 {"key_area_key_system_source",
77 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
78 static_cast<u64>(KeyAreaKeyType::System)}},
79 {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
80 {"keyblob_mac_key_source",
81 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},
82 {"tsec_key", {S128KeyType::TSEC, 0, 0}},
83 {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
84 {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
85 {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
86 {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
87 {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
88 {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
89 {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
90 {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
91 {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
92 {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
93 {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
94 {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
95}};
96
97auto Find128ByName(std::string_view name) {
98 return std::find_if(s128_file_id.begin(), s128_file_id.end(),
99 [&name](const auto& pair) { return pair.first == name; });
100}
101
102constexpr std::array<std::pair<std::string_view, KeyIndex<S256KeyType>>, 6> s256_file_id{{
103 {"header_key", {S256KeyType::Header, 0, 0}},
104 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
105 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
106 {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
107 {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
108 {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
109}};
110
111auto Find256ByName(std::string_view name) {
112 return std::find_if(s256_file_id.begin(), s256_file_id.end(),
113 [&name](const auto& pair) { return pair.first == name; });
114}
115
116using KeyArray = std::array<std::pair<std::pair<S128KeyType, u64>, std::string_view>, 7>;
117constexpr KeyArray KEYS_VARIABLE_LENGTH{{
51 {{S128KeyType::Master, 0}, "master_key_"}, 118 {{S128KeyType::Master, 0}, "master_key_"},
52 {{S128KeyType::Package1, 0}, "package1_key_"}, 119 {{S128KeyType::Package1, 0}, "package1_key_"},
53 {{S128KeyType::Package2, 0}, "package2_key_"}, 120 {{S128KeyType::Package2, 0}, "package2_key_"},
@@ -55,14 +122,13 @@ const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
55 {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"}, 122 {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"},
56 {{S128KeyType::Keyblob, 0}, "keyblob_key_"}, 123 {{S128KeyType::Keyblob, 0}, "keyblob_key_"},
57 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"}, 124 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
58}; 125}};
59 126
60namespace {
61template <std::size_t Size> 127template <std::size_t Size>
62bool IsAllZeroArray(const std::array<u8, Size>& array) { 128bool IsAllZeroArray(const std::array<u8, Size>& array) {
63 return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; }); 129 return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; });
64} 130}
65} // namespace 131} // Anonymous namespace
66 132
67u64 GetSignatureTypeDataSize(SignatureType type) { 133u64 GetSignatureTypeDataSize(SignatureType type) {
68 switch (type) { 134 switch (type) {
@@ -94,13 +160,13 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
94} 160}
95 161
96SignatureType Ticket::GetSignatureType() const { 162SignatureType Ticket::GetSignatureType() const {
97 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 163 if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
98 return ticket->sig_type; 164 return ticket->sig_type;
99 } 165 }
100 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 166 if (const auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
101 return ticket->sig_type; 167 return ticket->sig_type;
102 } 168 }
103 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 169 if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
104 return ticket->sig_type; 170 return ticket->sig_type;
105 } 171 }
106 172
@@ -108,13 +174,13 @@ SignatureType Ticket::GetSignatureType() const {
108} 174}
109 175
110TicketData& Ticket::GetData() { 176TicketData& Ticket::GetData() {
111 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 177 if (auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
112 return ticket->data; 178 return ticket->data;
113 } 179 }
114 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 180 if (auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
115 return ticket->data; 181 return ticket->data;
116 } 182 }
117 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 183 if (auto* ticket = std::get_if<ECDSATicket>(&data)) {
118 return ticket->data; 184 return ticket->data;
119 } 185 }
120 186
@@ -122,13 +188,13 @@ TicketData& Ticket::GetData() {
122} 188}
123 189
124const TicketData& Ticket::GetData() const { 190const TicketData& Ticket::GetData() const {
125 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 191 if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
126 return ticket->data; 192 return ticket->data;
127 } 193 }
128 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 194 if (const auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
129 return ticket->data; 195 return ticket->data;
130 } 196 }
131 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 197 if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
132 return ticket->data; 198 return ticket->data;
133 } 199 }
134 200
@@ -231,8 +297,9 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
231} 297}
232 298
233RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { 299RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const {
234 if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) 300 if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) {
235 return {}; 301 return {};
302 }
236 303
237 const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); 304 const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek);
238 305
@@ -259,27 +326,30 @@ Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source)
259} 326}
260 327
261std::optional<Key128> DeriveSDSeed() { 328std::optional<Key128> DeriveSDSeed() {
262 const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 329 const Common::FS::IOFile save_43(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
263 "/system/save/8000000000000043", 330 "/system/save/8000000000000043",
264 "rb+"); 331 "rb+");
265 if (!save_43.IsOpen()) 332 if (!save_43.IsOpen()) {
266 return {}; 333 return std::nullopt;
334 }
267 335
268 const FileUtil::IOFile sd_private( 336 const Common::FS::IOFile sd_private(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
269 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); 337 "/Nintendo/Contents/private",
270 if (!sd_private.IsOpen()) 338 "rb+");
271 return {}; 339 if (!sd_private.IsOpen()) {
340 return std::nullopt;
341 }
272 342
273 std::array<u8, 0x10> private_seed{}; 343 std::array<u8, 0x10> private_seed{};
274 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) { 344 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
275 return {}; 345 return std::nullopt;
276 } 346 }
277 347
278 std::array<u8, 0x10> buffer{}; 348 std::array<u8, 0x10> buffer{};
279 std::size_t offset = 0; 349 std::size_t offset = 0;
280 for (; offset + 0x10 < save_43.GetSize(); ++offset) { 350 for (; offset + 0x10 < save_43.GetSize(); ++offset) {
281 if (!save_43.Seek(offset, SEEK_SET)) { 351 if (!save_43.Seek(offset, SEEK_SET)) {
282 return {}; 352 return std::nullopt;
283 } 353 }
284 354
285 save_43.ReadBytes(buffer.data(), buffer.size()); 355 save_43.ReadBytes(buffer.data(), buffer.size());
@@ -289,23 +359,26 @@ std::optional<Key128> DeriveSDSeed() {
289 } 359 }
290 360
291 if (!save_43.Seek(offset + 0x10, SEEK_SET)) { 361 if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
292 return {}; 362 return std::nullopt;
293 } 363 }
294 364
295 Key128 seed{}; 365 Key128 seed{};
296 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) { 366 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
297 return {}; 367 return std::nullopt;
298 } 368 }
299 return seed; 369 return seed;
300} 370}
301 371
302Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) { 372Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) {
303 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek))) 373 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek))) {
304 return Loader::ResultStatus::ErrorMissingSDKEKSource; 374 return Loader::ResultStatus::ErrorMissingSDKEKSource;
305 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration))) 375 }
376 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration))) {
306 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource; 377 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource;
307 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) 378 }
379 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) {
308 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource; 380 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource;
381 }
309 382
310 const auto sd_kek_source = 383 const auto sd_kek_source =
311 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek)); 384 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek));
@@ -318,14 +391,17 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
318 GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen); 391 GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen);
319 keys.SetKey(S128KeyType::SDKek, sd_kek); 392 keys.SetKey(S128KeyType::SDKek, sd_kek);
320 393
321 if (!keys.HasKey(S128KeyType::SDSeed)) 394 if (!keys.HasKey(S128KeyType::SDSeed)) {
322 return Loader::ResultStatus::ErrorMissingSDSeed; 395 return Loader::ResultStatus::ErrorMissingSDSeed;
396 }
323 const auto sd_seed = keys.GetKey(S128KeyType::SDSeed); 397 const auto sd_seed = keys.GetKey(S128KeyType::SDSeed);
324 398
325 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save))) 399 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save))) {
326 return Loader::ResultStatus::ErrorMissingSDSaveKeySource; 400 return Loader::ResultStatus::ErrorMissingSDSaveKeySource;
327 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA))) 401 }
402 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA))) {
328 return Loader::ResultStatus::ErrorMissingSDNCAKeySource; 403 return Loader::ResultStatus::ErrorMissingSDNCAKeySource;
404 }
329 405
330 std::array<Key256, 2> sd_key_sources{ 406 std::array<Key256, 2> sd_key_sources{
331 keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)), 407 keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)),
@@ -334,8 +410,9 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
334 410
335 // Combine sources and seed 411 // Combine sources and seed
336 for (auto& source : sd_key_sources) { 412 for (auto& source : sd_key_sources) {
337 for (std::size_t i = 0; i < source.size(); ++i) 413 for (std::size_t i = 0; i < source.size(); ++i) {
338 source[i] ^= sd_seed[i & 0xF]; 414 source[i] ^= sd_seed[i & 0xF];
415 }
339 } 416 }
340 417
341 AESCipher<Key128> cipher(sd_kek, Mode::ECB); 418 AESCipher<Key128> cipher(sd_kek, Mode::ECB);
@@ -353,9 +430,10 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
353 return Loader::ResultStatus::Success; 430 return Loader::ResultStatus::Success;
354} 431}
355 432
356std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save) { 433std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save) {
357 if (!ticket_save.IsOpen()) 434 if (!ticket_save.IsOpen()) {
358 return {}; 435 return {};
436 }
359 437
360 std::vector<u8> buffer(ticket_save.GetSize()); 438 std::vector<u8> buffer(ticket_save.GetSize());
361 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) { 439 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
@@ -415,7 +493,7 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
415 offset = i + 1; 493 offset = i + 1;
416 break; 494 break;
417 } else if (data[i] != 0x0) { 495 } else if (data[i] != 0x0) {
418 return {}; 496 return std::nullopt;
419 } 497 }
420 } 498 }
421 499
@@ -425,16 +503,18 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
425std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, 503std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
426 const RSAKeyPair<2048>& key) { 504 const RSAKeyPair<2048>& key) {
427 const auto issuer = ticket.GetData().issuer; 505 const auto issuer = ticket.GetData().issuer;
428 if (IsAllZeroArray(issuer)) 506 if (IsAllZeroArray(issuer)) {
429 return {}; 507 return std::nullopt;
508 }
430 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { 509 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') {
431 LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); 510 LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority.");
432 } 511 }
433 512
434 Key128 rights_id = ticket.GetData().rights_id; 513 Key128 rights_id = ticket.GetData().rights_id;
435 514
436 if (rights_id == Key128{}) 515 if (rights_id == Key128{}) {
437 return {}; 516 return std::nullopt;
517 }
438 518
439 if (!std::any_of(ticket.GetData().title_key_common_pad.begin(), 519 if (!std::any_of(ticket.GetData().title_key_common_pad.begin(),
440 ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) { 520 ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) {
@@ -466,15 +546,17 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
466 std::array<u8, 0xDF> m_2; 546 std::array<u8, 0xDF> m_2;
467 std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size()); 547 std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
468 548
469 if (m_0 != 0) 549 if (m_0 != 0) {
470 return {}; 550 return std::nullopt;
551 }
471 552
472 m_1 = m_1 ^ MGF1<0x20>(m_2); 553 m_1 = m_1 ^ MGF1<0x20>(m_2);
473 m_2 = m_2 ^ MGF1<0xDF>(m_1); 554 m_2 = m_2 ^ MGF1<0xDF>(m_1);
474 555
475 const auto offset = FindTicketOffset(m_2); 556 const auto offset = FindTicketOffset(m_2);
476 if (!offset) 557 if (!offset) {
477 return {}; 558 return std::nullopt;
559 }
478 ASSERT(*offset > 0); 560 ASSERT(*offset > 0);
479 561
480 Key128 key_temp{}; 562 Key128 key_temp{};
@@ -485,8 +567,8 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
485 567
486KeyManager::KeyManager() { 568KeyManager::KeyManager() {
487 // Initialize keys 569 // Initialize keys
488 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); 570 const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
489 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 571 const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
490 if (Settings::values.use_dev_keys) { 572 if (Settings::values.use_dev_keys) {
491 dev_mode = true; 573 dev_mode = true;
492 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false); 574 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);
@@ -504,34 +586,39 @@ KeyManager::KeyManager() {
504} 586}
505 587
506static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) { 588static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
507 if (base.size() < begin + length) 589 if (base.size() < begin + length) {
508 return false; 590 return false;
591 }
509 return std::all_of(base.begin() + begin, base.begin() + begin + length, 592 return std::all_of(base.begin() + begin, base.begin() + begin + length,
510 [](u8 c) { return std::isxdigit(c); }); 593 [](u8 c) { return std::isxdigit(c); });
511} 594}
512 595
513void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { 596void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
514 std::ifstream file; 597 std::ifstream file;
515 OpenFStream(file, filename, std::ios_base::in); 598 Common::FS::OpenFStream(file, filename, std::ios_base::in);
516 if (!file.is_open()) 599 if (!file.is_open()) {
517 return; 600 return;
601 }
518 602
519 std::string line; 603 std::string line;
520 while (std::getline(file, line)) { 604 while (std::getline(file, line)) {
521 std::vector<std::string> out; 605 std::vector<std::string> out;
522 std::stringstream stream(line); 606 std::stringstream stream(line);
523 std::string item; 607 std::string item;
524 while (std::getline(stream, item, '=')) 608 while (std::getline(stream, item, '=')) {
525 out.push_back(std::move(item)); 609 out.push_back(std::move(item));
610 }
526 611
527 if (out.size() != 2) 612 if (out.size() != 2) {
528 continue; 613 continue;
614 }
529 615
530 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end()); 616 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
531 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end()); 617 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
532 618
533 if (out[0].compare(0, 1, "#") == 0) 619 if (out[0].compare(0, 1, "#") == 0) {
534 continue; 620 continue;
621 }
535 622
536 if (is_title_keys) { 623 if (is_title_keys) {
537 auto rights_id_raw = Common::HexStringToArray<16>(out[0]); 624 auto rights_id_raw = Common::HexStringToArray<16>(out[0]);
@@ -541,24 +628,26 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
541 s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key; 628 s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
542 } else { 629 } else {
543 out[0] = Common::ToLower(out[0]); 630 out[0] = Common::ToLower(out[0]);
544 if (s128_file_id.find(out[0]) != s128_file_id.end()) { 631 if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
545 const auto index = s128_file_id.at(out[0]); 632 const auto& index = iter128->second;
546 Key128 key = Common::HexStringToArray<16>(out[1]); 633 const Key128 key = Common::HexStringToArray<16>(out[1]);
547 s128_keys[{index.type, index.field1, index.field2}] = key; 634 s128_keys[{index.type, index.field1, index.field2}] = key;
548 } else if (s256_file_id.find(out[0]) != s256_file_id.end()) { 635 } else if (const auto iter256 = Find256ByName(out[0]); iter256 != s256_file_id.end()) {
549 const auto index = s256_file_id.at(out[0]); 636 const auto& index = iter256->second;
550 Key256 key = Common::HexStringToArray<32>(out[1]); 637 const Key256 key = Common::HexStringToArray<32>(out[1]);
551 s256_keys[{index.type, index.field1, index.field2}] = key; 638 s256_keys[{index.type, index.field1, index.field2}] = key;
552 } else if (out[0].compare(0, 8, "keyblob_") == 0 && 639 } else if (out[0].compare(0, 8, "keyblob_") == 0 &&
553 out[0].compare(0, 9, "keyblob_k") != 0) { 640 out[0].compare(0, 9, "keyblob_k") != 0) {
554 if (!ValidCryptoRevisionString(out[0], 8, 2)) 641 if (!ValidCryptoRevisionString(out[0], 8, 2)) {
555 continue; 642 continue;
643 }
556 644
557 const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16); 645 const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16);
558 keyblobs[index] = Common::HexStringToArray<0x90>(out[1]); 646 keyblobs[index] = Common::HexStringToArray<0x90>(out[1]);
559 } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) { 647 } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) {
560 if (!ValidCryptoRevisionString(out[0], 18, 2)) 648 if (!ValidCryptoRevisionString(out[0], 18, 2)) {
561 continue; 649 continue;
650 }
562 651
563 const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16); 652 const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16);
564 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); 653 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
@@ -566,8 +655,9 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
566 eticket_extended_kek = Common::HexStringToArray<576>(out[1]); 655 eticket_extended_kek = Common::HexStringToArray<576>(out[1]);
567 } else { 656 } else {
568 for (const auto& kv : KEYS_VARIABLE_LENGTH) { 657 for (const auto& kv : KEYS_VARIABLE_LENGTH) {
569 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) 658 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) {
570 continue; 659 continue;
660 }
571 if (out[0].compare(0, kv.second.size(), kv.second) == 0) { 661 if (out[0].compare(0, kv.second.size(), kv.second) == 0) {
572 const auto index = 662 const auto index =
573 std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16); 663 std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16);
@@ -602,10 +692,11 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
602 692
603void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, 693void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
604 const std::string& filename, bool title) { 694 const std::string& filename, bool title) {
605 if (FileUtil::Exists(dir1 + DIR_SEP + filename)) 695 if (Common::FS::Exists(dir1 + DIR_SEP + filename)) {
606 LoadFromFile(dir1 + DIR_SEP + filename, title); 696 LoadFromFile(dir1 + DIR_SEP + filename, title);
607 else if (FileUtil::Exists(dir2 + DIR_SEP + filename)) 697 } else if (Common::FS::Exists(dir2 + DIR_SEP + filename)) {
608 LoadFromFile(dir2 + DIR_SEP + filename, title); 698 LoadFromFile(dir2 + DIR_SEP + filename, title);
699 }
609} 700}
610 701
611bool KeyManager::BaseDeriveNecessary() const { 702bool KeyManager::BaseDeriveNecessary() const {
@@ -613,8 +704,9 @@ bool KeyManager::BaseDeriveNecessary() const {
613 return !HasKey(key_type, index1, index2); 704 return !HasKey(key_type, index1, index2);
614 }; 705 };
615 706
616 if (check_key_existence(S256KeyType::Header)) 707 if (check_key_existence(S256KeyType::Header)) {
617 return true; 708 return true;
709 }
618 710
619 for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) { 711 for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) {
620 if (check_key_existence(S128KeyType::Master, i) || 712 if (check_key_existence(S128KeyType::Master, i) ||
@@ -639,14 +731,16 @@ bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) const {
639} 731}
640 732
641Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const { 733Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const {
642 if (!HasKey(id, field1, field2)) 734 if (!HasKey(id, field1, field2)) {
643 return {}; 735 return {};
736 }
644 return s128_keys.at({id, field1, field2}); 737 return s128_keys.at({id, field1, field2});
645} 738}
646 739
647Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const { 740Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
648 if (!HasKey(id, field1, field2)) 741 if (!HasKey(id, field1, field2)) {
649 return {}; 742 return {};
743 }
650 return s256_keys.at({id, field1, field2}); 744 return s256_keys.at({id, field1, field2});
651} 745}
652 746
@@ -668,7 +762,7 @@ Key256 KeyManager::GetBISKey(u8 partition_id) const {
668template <size_t Size> 762template <size_t Size>
669void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname, 763void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
670 const std::array<u8, Size>& key) { 764 const std::array<u8, Size>& key) {
671 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 765 const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
672 std::string filename = "title.keys_autogenerated"; 766 std::string filename = "title.keys_autogenerated";
673 if (category == KeyCategory::Standard) { 767 if (category == KeyCategory::Standard) {
674 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated"; 768 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
@@ -677,9 +771,9 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
677 } 771 }
678 772
679 const auto path = yuzu_keys_dir + DIR_SEP + filename; 773 const auto path = yuzu_keys_dir + DIR_SEP + filename;
680 const auto add_info_text = !FileUtil::Exists(path); 774 const auto add_info_text = !Common::FS::Exists(path);
681 FileUtil::CreateFullPath(path); 775 Common::FS::CreateFullPath(path);
682 FileUtil::IOFile file{path, "a"}; 776 Common::FS::IOFile file{path, "a"};
683 if (!file.IsOpen()) { 777 if (!file.IsOpen()) {
684 return; 778 return;
685 } 779 }
@@ -712,8 +806,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
712 } 806 }
713 807
714 const auto iter2 = std::find_if( 808 const auto iter2 = std::find_if(
715 s128_file_id.begin(), s128_file_id.end(), 809 s128_file_id.begin(), s128_file_id.end(), [&id, &field1, &field2](const auto& elem) {
716 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
717 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == 810 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
718 std::tie(id, field1, field2); 811 std::tie(id, field1, field2);
719 }); 812 });
@@ -723,9 +816,11 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
723 816
724 // Variable cases 817 // Variable cases
725 if (id == S128KeyType::KeyArea) { 818 if (id == S128KeyType::KeyArea) {
726 static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}", 819 static constexpr std::array<const char*, 3> kak_names = {
727 "key_area_key_ocean_{:02X}", 820 "key_area_key_application_{:02X}",
728 "key_area_key_system_{:02X}"}; 821 "key_area_key_ocean_{:02X}",
822 "key_area_key_system_{:02X}",
823 };
729 WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key); 824 WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key);
730 } else if (id == S128KeyType::Master) { 825 } else if (id == S128KeyType::Master) {
731 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key); 826 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
@@ -751,8 +846,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
751 return; 846 return;
752 } 847 }
753 const auto iter = std::find_if( 848 const auto iter = std::find_if(
754 s256_file_id.begin(), s256_file_id.end(), 849 s256_file_id.begin(), s256_file_id.end(), [&id, &field1, &field2](const auto& elem) {
755 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
756 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == 850 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
757 std::tie(id, field1, field2); 851 std::tie(id, field1, field2);
758 }); 852 });
@@ -763,29 +857,31 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
763} 857}
764 858
765bool KeyManager::KeyFileExists(bool title) { 859bool KeyManager::KeyFileExists(bool title) {
766 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); 860 const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
767 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 861 const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
768 if (title) { 862 if (title) {
769 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "title.keys") || 863 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "title.keys") ||
770 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "title.keys"); 864 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "title.keys");
771 } 865 }
772 866
773 if (Settings::values.use_dev_keys) { 867 if (Settings::values.use_dev_keys) {
774 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") || 868 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") ||
775 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys"); 869 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys");
776 } 870 }
777 871
778 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") || 872 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") ||
779 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys"); 873 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys");
780} 874}
781 875
782void KeyManager::DeriveSDSeedLazy() { 876void KeyManager::DeriveSDSeedLazy() {
783 if (HasKey(S128KeyType::SDSeed)) 877 if (HasKey(S128KeyType::SDSeed)) {
784 return; 878 return;
879 }
785 880
786 const auto res = DeriveSDSeed(); 881 const auto res = DeriveSDSeed();
787 if (res) 882 if (res) {
788 SetKey(S128KeyType::SDSeed, *res); 883 SetKey(S128KeyType::SDSeed, *res);
884 }
789} 885}
790 886
791static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) { 887static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
@@ -797,11 +893,13 @@ static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
797} 893}
798 894
799void KeyManager::DeriveBase() { 895void KeyManager::DeriveBase() {
800 if (!BaseDeriveNecessary()) 896 if (!BaseDeriveNecessary()) {
801 return; 897 return;
898 }
802 899
803 if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC)) 900 if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC)) {
804 return; 901 return;
902 }
805 903
806 const auto has_bis = [this](u64 id) { 904 const auto has_bis = [this](u64 id) {
807 return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) && 905 return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) &&
@@ -818,10 +916,11 @@ void KeyManager::DeriveBase() {
818 static_cast<u64>(BISKeyType::Tweak)); 916 static_cast<u64>(BISKeyType::Tweak));
819 }; 917 };
820 918
821 if (has_bis(2) && !has_bis(3)) 919 if (has_bis(2) && !has_bis(3)) {
822 copy_bis(2, 3); 920 copy_bis(2, 3);
823 else if (has_bis(3) && !has_bis(2)) 921 } else if (has_bis(3) && !has_bis(2)) {
824 copy_bis(3, 2); 922 copy_bis(3, 2);
923 }
825 924
826 std::bitset<32> revisions(0xFFFFFFFF); 925 std::bitset<32> revisions(0xFFFFFFFF);
827 for (size_t i = 0; i < revisions.size(); ++i) { 926 for (size_t i = 0; i < revisions.size(); ++i) {
@@ -831,15 +930,17 @@ void KeyManager::DeriveBase() {
831 } 930 }
832 } 931 }
833 932
834 if (!revisions.any()) 933 if (!revisions.any()) {
835 return; 934 return;
935 }
836 936
837 const auto sbk = GetKey(S128KeyType::SecureBoot); 937 const auto sbk = GetKey(S128KeyType::SecureBoot);
838 const auto tsec = GetKey(S128KeyType::TSEC); 938 const auto tsec = GetKey(S128KeyType::TSEC);
839 939
840 for (size_t i = 0; i < revisions.size(); ++i) { 940 for (size_t i = 0; i < revisions.size(); ++i) {
841 if (!revisions[i]) 941 if (!revisions[i]) {
842 continue; 942 continue;
943 }
843 944
844 // Derive keyblob key 945 // Derive keyblob key
845 const auto key = DeriveKeyblobKey( 946 const auto key = DeriveKeyblobKey(
@@ -848,16 +949,18 @@ void KeyManager::DeriveBase() {
848 SetKey(S128KeyType::Keyblob, key, i); 949 SetKey(S128KeyType::Keyblob, key, i);
849 950
850 // Derive keyblob MAC key 951 // Derive keyblob MAC key
851 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))) 952 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))) {
852 continue; 953 continue;
954 }
853 955
854 const auto mac_key = DeriveKeyblobMACKey( 956 const auto mac_key = DeriveKeyblobMACKey(
855 key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))); 957 key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)));
856 SetKey(S128KeyType::KeyblobMAC, mac_key, i); 958 SetKey(S128KeyType::KeyblobMAC, mac_key, i);
857 959
858 Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key); 960 Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key);
859 if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0) 961 if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0) {
860 continue; 962 continue;
963 }
861 964
862 // Decrypt keyblob 965 // Decrypt keyblob
863 if (keyblobs[i] == std::array<u8, 0x90>{}) { 966 if (keyblobs[i] == std::array<u8, 0x90>{}) {
@@ -881,16 +984,19 @@ void KeyManager::DeriveBase() {
881 984
882 revisions.set(); 985 revisions.set();
883 for (size_t i = 0; i < revisions.size(); ++i) { 986 for (size_t i = 0; i < revisions.size(); ++i) {
884 if (!HasKey(S128KeyType::Master, i)) 987 if (!HasKey(S128KeyType::Master, i)) {
885 revisions.reset(i); 988 revisions.reset(i);
989 }
886 } 990 }
887 991
888 if (!revisions.any()) 992 if (!revisions.any()) {
889 return; 993 return;
994 }
890 995
891 for (size_t i = 0; i < revisions.size(); ++i) { 996 for (size_t i = 0; i < revisions.size(); ++i) {
892 if (!revisions[i]) 997 if (!revisions[i]) {
893 continue; 998 continue;
999 }
894 1000
895 // Derive general purpose keys 1001 // Derive general purpose keys
896 DeriveGeneralPurposeKeys(i); 1002 DeriveGeneralPurposeKeys(i);
@@ -915,21 +1021,24 @@ void KeyManager::DeriveBase() {
915 } 1021 }
916} 1022}
917 1023
918void KeyManager::DeriveETicket(PartitionDataManager& data) { 1024void KeyManager::DeriveETicket(PartitionDataManager& data,
1025 const FileSys::ContentProvider& provider) {
919 // ETicket keys 1026 // ETicket keys
920 const auto es = Core::System::GetInstance().GetContentProvider().GetEntry( 1027 const auto es = provider.GetEntry(0x0100000000000033, FileSys::ContentRecordType::Program);
921 0x0100000000000033, FileSys::ContentRecordType::Program);
922 1028
923 if (es == nullptr) 1029 if (es == nullptr) {
924 return; 1030 return;
1031 }
925 1032
926 const auto exefs = es->GetExeFS(); 1033 const auto exefs = es->GetExeFS();
927 if (exefs == nullptr) 1034 if (exefs == nullptr) {
928 return; 1035 return;
1036 }
929 1037
930 const auto main = exefs->GetFile("main"); 1038 const auto main = exefs->GetFile("main");
931 if (main == nullptr) 1039 if (main == nullptr) {
932 return; 1040 return;
1041 }
933 1042
934 const auto bytes = main->ReadAllBytes(); 1043 const auto bytes = main->ReadAllBytes();
935 1044
@@ -939,16 +1048,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
939 const auto seed3 = data.GetRSAKekSeed3(); 1048 const auto seed3 = data.GetRSAKekSeed3();
940 const auto mask0 = data.GetRSAKekMask0(); 1049 const auto mask0 = data.GetRSAKekMask0();
941 1050
942 if (eticket_kek != Key128{}) 1051 if (eticket_kek != Key128{}) {
943 SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek)); 1052 SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek));
1053 }
944 if (eticket_kekek != Key128{}) { 1054 if (eticket_kekek != Key128{}) {
945 SetKey(S128KeyType::Source, eticket_kekek, 1055 SetKey(S128KeyType::Source, eticket_kekek,
946 static_cast<size_t>(SourceKeyType::ETicketKekek)); 1056 static_cast<size_t>(SourceKeyType::ETicketKekek));
947 } 1057 }
948 if (seed3 != Key128{}) 1058 if (seed3 != Key128{}) {
949 SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3)); 1059 SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3));
950 if (mask0 != Key128{}) 1060 }
1061 if (mask0 != Key128{}) {
951 SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0)); 1062 SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0));
1063 }
952 if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} || 1064 if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} ||
953 mask0 == Key128{}) { 1065 mask0 == Key128{}) {
954 return; 1066 return;
@@ -974,8 +1086,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
974 AESCipher<Key128> es_kek(temp_kekek, Mode::ECB); 1086 AESCipher<Key128> es_kek(temp_kekek, Mode::ECB);
975 es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt); 1087 es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt);
976 1088
977 if (eticket_final == Key128{}) 1089 if (eticket_final == Key128{}) {
978 return; 1090 return;
1091 }
979 1092
980 SetKey(S128KeyType::ETicketRSAKek, eticket_final); 1093 SetKey(S128KeyType::ETicketRSAKek, eticket_final);
981 1094
@@ -990,18 +1103,20 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
990void KeyManager::PopulateTickets() { 1103void KeyManager::PopulateTickets() {
991 const auto rsa_key = GetETicketRSAKey(); 1104 const auto rsa_key = GetETicketRSAKey();
992 1105
993 if (rsa_key == RSAKeyPair<2048>{}) 1106 if (rsa_key == RSAKeyPair<2048>{}) {
994 return; 1107 return;
1108 }
995 1109
996 if (!common_tickets.empty() && !personal_tickets.empty()) 1110 if (!common_tickets.empty() && !personal_tickets.empty()) {
997 return; 1111 return;
1112 }
998 1113
999 const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1114 const Common::FS::IOFile save1(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1000 "/system/save/80000000000000e1", 1115 "/system/save/80000000000000e1",
1001 "rb+"); 1116 "rb+");
1002 const FileUtil::IOFile save2(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1117 const Common::FS::IOFile save2(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1003 "/system/save/80000000000000e2", 1118 "/system/save/80000000000000e2",
1004 "rb+"); 1119 "rb+");
1005 1120
1006 const auto blob2 = GetTicketblob(save2); 1121 const auto blob2 = GetTicketblob(save2);
1007 auto res = GetTicketblob(save1); 1122 auto res = GetTicketblob(save1);
@@ -1011,8 +1126,10 @@ void KeyManager::PopulateTickets() {
1011 for (std::size_t i = 0; i < res.size(); ++i) { 1126 for (std::size_t i = 0; i < res.size(); ++i) {
1012 const auto common = i < idx; 1127 const auto common = i < idx;
1013 const auto pair = ParseTicket(res[i], rsa_key); 1128 const auto pair = ParseTicket(res[i], rsa_key);
1014 if (!pair) 1129 if (!pair) {
1015 continue; 1130 continue;
1131 }
1132
1016 const auto& [rid, key] = *pair; 1133 const auto& [rid, key] = *pair;
1017 u128 rights_id; 1134 u128 rights_id;
1018 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1135 std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1041,27 +1158,33 @@ void KeyManager::SynthesizeTickets() {
1041} 1158}
1042 1159
1043void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) { 1160void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
1044 if (key == Key128{}) 1161 if (key == Key128{}) {
1045 return; 1162 return;
1163 }
1046 SetKey(id, key, field1, field2); 1164 SetKey(id, key, field1, field2);
1047} 1165}
1048 1166
1049void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) { 1167void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) {
1050 if (key == Key256{}) 1168 if (key == Key256{}) {
1051 return; 1169 return;
1170 }
1171
1052 SetKey(id, key, field1, field2); 1172 SetKey(id, key, field1, field2);
1053} 1173}
1054 1174
1055void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) { 1175void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1056 if (!BaseDeriveNecessary()) 1176 if (!BaseDeriveNecessary()) {
1057 return; 1177 return;
1178 }
1058 1179
1059 if (!data.HasBoot0()) 1180 if (!data.HasBoot0()) {
1060 return; 1181 return;
1182 }
1061 1183
1062 for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) { 1184 for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) {
1063 if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{}) 1185 if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{}) {
1064 continue; 1186 continue;
1187 }
1065 encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i); 1188 encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i);
1066 WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i), 1189 WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i),
1067 encrypted_keyblobs[i]); 1190 encrypted_keyblobs[i]);
@@ -1083,8 +1206,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1083 static_cast<u64>(SourceKeyType::Keyblob), i); 1206 static_cast<u64>(SourceKeyType::Keyblob), i);
1084 } 1207 }
1085 1208
1086 if (data.HasFuses()) 1209 if (data.HasFuses()) {
1087 SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey()); 1210 SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey());
1211 }
1088 1212
1089 DeriveBase(); 1213 DeriveBase();
1090 1214
@@ -1098,8 +1222,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1098 1222
1099 const auto masters = data.GetTZMasterKeys(latest_master); 1223 const auto masters = data.GetTZMasterKeys(latest_master);
1100 for (size_t i = 0; i < masters.size(); ++i) { 1224 for (size_t i = 0; i < masters.size(); ++i) {
1101 if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i)) 1225 if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i)) {
1102 SetKey(S128KeyType::Master, masters[i], i); 1226 SetKey(S128KeyType::Master, masters[i], i);
1227 }
1103 } 1228 }
1104 1229
1105 DeriveBase(); 1230 DeriveBase();
@@ -1109,8 +1234,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1109 1234
1110 std::array<Key128, 0x20> package2_keys{}; 1235 std::array<Key128, 0x20> package2_keys{};
1111 for (size_t i = 0; i < package2_keys.size(); ++i) { 1236 for (size_t i = 0; i < package2_keys.size(); ++i) {
1112 if (HasKey(S128KeyType::Package2, i)) 1237 if (HasKey(S128KeyType::Package2, i)) {
1113 package2_keys[i] = GetKey(S128KeyType::Package2, i); 1238 package2_keys[i] = GetKey(S128KeyType::Package2, i);
1239 }
1114 } 1240 }
1115 data.DecryptPackage2(package2_keys, Package2Type::NormalMain); 1241 data.DecryptPackage2(package2_keys, Package2Type::NormalMain);
1116 1242
@@ -1148,12 +1274,15 @@ const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const {
1148 1274
1149bool KeyManager::AddTicketCommon(Ticket raw) { 1275bool KeyManager::AddTicketCommon(Ticket raw) {
1150 const auto rsa_key = GetETicketRSAKey(); 1276 const auto rsa_key = GetETicketRSAKey();
1151 if (rsa_key == RSAKeyPair<2048>{}) 1277 if (rsa_key == RSAKeyPair<2048>{}) {
1152 return false; 1278 return false;
1279 }
1153 1280
1154 const auto pair = ParseTicket(raw, rsa_key); 1281 const auto pair = ParseTicket(raw, rsa_key);
1155 if (!pair) 1282 if (!pair) {
1156 return false; 1283 return false;
1284 }
1285
1157 const auto& [rid, key] = *pair; 1286 const auto& [rid, key] = *pair;
1158 u128 rights_id; 1287 u128 rights_id;
1159 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1288 std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1164,12 +1293,15 @@ bool KeyManager::AddTicketCommon(Ticket raw) {
1164 1293
1165bool KeyManager::AddTicketPersonalized(Ticket raw) { 1294bool KeyManager::AddTicketPersonalized(Ticket raw) {
1166 const auto rsa_key = GetETicketRSAKey(); 1295 const auto rsa_key = GetETicketRSAKey();
1167 if (rsa_key == RSAKeyPair<2048>{}) 1296 if (rsa_key == RSAKeyPair<2048>{}) {
1168 return false; 1297 return false;
1298 }
1169 1299
1170 const auto pair = ParseTicket(raw, rsa_key); 1300 const auto pair = ParseTicket(raw, rsa_key);
1171 if (!pair) 1301 if (!pair) {
1172 return false; 1302 return false;
1303 }
1304
1173 const auto& [rid, key] = *pair; 1305 const auto& [rid, key] = *pair;
1174 u128 rights_id; 1306 u128 rights_id;
1175 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1307 std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1177,58 +1309,4 @@ bool KeyManager::AddTicketPersonalized(Ticket raw) {
1177 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); 1309 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
1178 return true; 1310 return true;
1179} 1311}
1180
1181const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
1182 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
1183 {"eticket_rsa_kek_source",
1184 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
1185 {"eticket_rsa_kekek_source",
1186 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
1187 {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
1188 {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
1189 {"rsa_oaep_kek_generation_source",
1190 {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
1191 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
1192 {"aes_kek_generation_source",
1193 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
1194 {"aes_key_generation_source",
1195 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
1196 {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
1197 {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
1198 {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
1199 {"key_area_key_application_source",
1200 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1201 static_cast<u64>(KeyAreaKeyType::Application)}},
1202 {"key_area_key_ocean_source",
1203 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1204 static_cast<u64>(KeyAreaKeyType::Ocean)}},
1205 {"key_area_key_system_source",
1206 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1207 static_cast<u64>(KeyAreaKeyType::System)}},
1208 {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
1209 {"keyblob_mac_key_source",
1210 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},
1211 {"tsec_key", {S128KeyType::TSEC, 0, 0}},
1212 {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
1213 {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
1214 {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
1215 {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
1216 {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
1217 {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
1218 {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
1219 {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
1220 {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
1221 {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
1222 {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
1223 {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
1224};
1225
1226const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
1227 {"header_key", {S256KeyType::Header, 0, 0}},
1228 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
1229 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
1230 {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
1231 {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
1232 {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
1233};
1234} // namespace Core::Crypto 1312} // namespace Core::Crypto
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 9269a73f2..0a7220286 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -10,17 +10,20 @@
10#include <string> 10#include <string>
11 11
12#include <variant> 12#include <variant>
13#include <boost/container/flat_map.hpp>
14#include <fmt/format.h> 13#include <fmt/format.h>
15#include "common/common_funcs.h" 14#include "common/common_funcs.h"
16#include "common/common_types.h" 15#include "common/common_types.h"
17#include "core/crypto/partition_data_manager.h" 16#include "core/crypto/partition_data_manager.h"
18#include "core/file_sys/vfs_types.h" 17#include "core/file_sys/vfs_types.h"
19 18
20namespace FileUtil { 19namespace Common::FS {
21class IOFile; 20class IOFile;
22} 21}
23 22
23namespace FileSys {
24class ContentProvider;
25}
26
24namespace Loader { 27namespace Loader {
25enum class ResultStatus : u16; 28enum class ResultStatus : u16;
26} 29}
@@ -253,7 +256,7 @@ public:
253 256
254 bool BaseDeriveNecessary() const; 257 bool BaseDeriveNecessary() const;
255 void DeriveBase(); 258 void DeriveBase();
256 void DeriveETicket(PartitionDataManager& data); 259 void DeriveETicket(PartitionDataManager& data, const FileSys::ContentProvider& provider);
257 void PopulateTickets(); 260 void PopulateTickets();
258 void SynthesizeTickets(); 261 void SynthesizeTickets();
259 262
@@ -293,9 +296,6 @@ private:
293 296
294 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); 297 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
295 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); 298 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
296
297 static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
298 static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
299}; 299};
300 300
301Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); 301Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
@@ -308,7 +308,7 @@ std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblo
308std::optional<Key128> DeriveSDSeed(); 308std::optional<Key128> DeriveSDSeed();
309Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys); 309Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
310 310
311std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save); 311std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save);
312 312
313// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority 313// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority
314// (offset 0x140-0x144 is zero) 314// (offset 0x140-0x144 is zero)
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index 7ed71ac3a..5f1c86a09 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -26,8 +26,9 @@
26#include "core/file_sys/vfs.h" 26#include "core/file_sys/vfs.h"
27#include "core/file_sys/vfs_offset.h" 27#include "core/file_sys/vfs_offset.h"
28#include "core/file_sys/vfs_vector.h" 28#include "core/file_sys/vfs_vector.h"
29#include "core/loader/loader.h"
29 30
30using namespace Common; 31using Common::AsArray;
31 32
32namespace Core::Crypto { 33namespace Core::Crypto {
33 34
@@ -47,105 +48,123 @@ struct Package2Header {
47}; 48};
48static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); 49static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
49 50
50const std::array<SHA256Hash, 0x10> source_hashes{ 51// clang-format off
51 "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source 52constexpr std::array source_hashes{
52 "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source 53 AsArray("B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"), // keyblob_mac_key_source
53 "21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source 54 AsArray("7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"), // master_key_source
54 "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source 55 AsArray("21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"), // package2_key_source
55 "FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source 56 AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // aes_kek_generation_source
56 "C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source 57 AsArray("FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"), // aes_key_generation_source
57 "04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source 58 AsArray("C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"), // titlekek_source
58 "FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source 59 AsArray("04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"), // key_area_key_application_source
59 "1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source 60 AsArray("FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"), // key_area_key_ocean_source
60 "6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source 61 AsArray("1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"), // key_area_key_system_source
61 "D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source 62 AsArray("6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"), // sd_card_kek_source
62 "2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source 63 AsArray("D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"), // sd_card_save_key_source
63 "1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source 64 AsArray("2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"), // sd_card_nca_key_source
64 "8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source 65 AsArray("1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"), // header_kek_source
65 "D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3 66 AsArray("8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"), // header_key_source
66 "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0 67 AsArray("D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"), // rsa_kek_seed3
68 AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // rsa_kek_mask0
67}; 69};
68 70// clang-format on
69const std::array<SHA256Hash, 0x20> keyblob_source_hashes{ 71
70 "8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00 72// clang-format off
71 "2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01 73constexpr std::array keyblob_source_hashes{
72 "61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02 74 AsArray("8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"), // keyblob_key_source_00
73 "8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03 75 AsArray("2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"), // keyblob_key_source_01
74 "95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04 76 AsArray("61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"), // keyblob_key_source_02
75 "3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05 77 AsArray("8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"), // keyblob_key_source_03
76 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06 78 AsArray("95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"), // keyblob_key_source_04
77 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07 79 AsArray("3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"), // keyblob_key_source_05
78 80 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_06
79 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08 81 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_07
80 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09 82
81 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A 83 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_08
82 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B 84 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_09
83 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C 85 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0A
84 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D 86 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0B
85 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E 87 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0C
86 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F 88 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0D
87 89 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0E
88 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10 90 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0F
89 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11 91
90 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12 92 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_10
91 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13 93 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_11
92 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14 94 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_12
93 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15 95 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_13
94 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16 96 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_14
95 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17 97 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_15
96 98 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_16
97 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18 99 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_17
98 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19 100
99 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A 101 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_18
100 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B 102 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_19
101 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C 103 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1A
102 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D 104 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1B
103 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E 105 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1C
104 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F 106 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1D
107 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1E
108 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1F
105}; 109};
106 110// clang-format on
107const std::array<SHA256Hash, 0x20> master_key_hashes{ 111
108 "0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00 112// clang-format off
109 "4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01 113constexpr std::array master_key_hashes{
110 "79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02 114 AsArray("0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"), // master_key_00
111 "4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03 115 AsArray("4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"), // master_key_01
112 "75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04 116 AsArray("79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"), // master_key_02
113 "EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05 117 AsArray("4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"), // master_key_03
114 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06 118 AsArray("75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"), // master_key_04
115 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07 119 AsArray("EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"), // master_key_05
116 120 AsArray("9497E6779F5D840F2BBA1DE4E95BA1D6F21EFC94717D5AE5CA37D7EC5BD37A19"), // master_key_06
117 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08 121 AsArray("4EC96B8CB01B8DCE382149443430B2B6EBCB2983348AFA04A25E53609DABEDF6"), // master_key_07
118 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09 122
119 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A 123 AsArray("2998E2E23609BC2675FF062A2D64AF5B1B78DFF463B24119D64A1B64F01B2D51"), // master_key_08
120 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B 124 AsArray("9D486A98067C44B37CF173D3BF577891EB6081FF6B4A166347D9DBBF7025076B"), // master_key_09
121 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C 125 AsArray("4EC5A237A75A083A9C5F6CF615601522A7F822D06BD4BA32612C9CEBBB29BD45"), // master_key_0A
122 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D 126 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0B
123 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E 127 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0C
124 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F 128 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0D
125 129 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0E
126 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10 130 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0F
127 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11 131
128 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12 132 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_10
129 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13 133 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_11
130 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14 134 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_12
131 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15 135 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_13
132 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16 136 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_14
133 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17 137 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_15
134 138 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_16
135 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18 139 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_17
136 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19 140
137 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A 141 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_18
138 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B 142 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_19
139 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C 143 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1A
140 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D 144 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1B
141 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E 145 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1C
142 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F 146 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1D
147 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1E
148 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1F
143}; 149};
150// clang-format on
151
152static constexpr u8 CalculateMaxKeyblobSourceHash() {
153 const auto is_zero = [](const auto& data) {
154 // TODO: Replace with std::all_of whenever mingw decides to update their
155 // libraries to include the constexpr variant of it.
156 for (const auto element : data) {
157 if (element != 0) {
158 return false;
159 }
160 }
161 return true;
162 };
144 163
145static u8 CalculateMaxKeyblobSourceHash() {
146 for (s8 i = 0x1F; i >= 0; --i) { 164 for (s8 i = 0x1F; i >= 0; --i) {
147 if (keyblob_source_hashes[i] != SHA256Hash{}) 165 if (!is_zero(keyblob_source_hashes[i])) {
148 return static_cast<u8>(i + 1); 166 return static_cast<u8>(i + 1);
167 }
149 } 168 }
150 169
151 return 0; 170 return 0;
@@ -346,12 +365,11 @@ FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) con
346} 365}
347 366
348static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) { 367static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
349 const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end());
350 Package2Header temp = header; 368 Package2Header temp = header;
351 AESCipher<Key128> cipher(key, Mode::CTR); 369 AESCipher<Key128> cipher(key, Mode::CTR);
352 cipher.SetIV(iv); 370 cipher.SetIV(header.header_ctr);
353 cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr, 371 cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - sizeof(Package2Header::signature),
354 Op::Decrypt); 372 &temp.header_ctr, Op::Decrypt);
355 if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) { 373 if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
356 header = temp; 374 header = temp;
357 return true; 375 return true;
@@ -388,7 +406,7 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
388 auto c = a->ReadAllBytes(); 406 auto c = a->ReadAllBytes();
389 407
390 AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR); 408 AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR);
391 cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()}); 409 cipher.SetIV(header.section_ctr[1]);
392 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); 410 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
393 411
394 const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c); 412 const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c);
diff --git a/src/core/device_memory.cpp b/src/core/device_memory.cpp
index 51097ced3..0c4b440ed 100644
--- a/src/core/device_memory.cpp
+++ b/src/core/device_memory.cpp
@@ -2,14 +2,11 @@
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 "core/core.h"
6#include "core/device_memory.h" 5#include "core/device_memory.h"
7#include "core/memory.h"
8 6
9namespace Core { 7namespace Core {
10 8
11DeviceMemory::DeviceMemory(System& system) : buffer{DramMemoryMap::Size}, system{system} {} 9DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size} {}
12
13DeviceMemory::~DeviceMemory() = default; 10DeviceMemory::~DeviceMemory() = default;
14 11
15} // namespace Core 12} // namespace Core
diff --git a/src/core/device_memory.h b/src/core/device_memory.h
index 9efa088d0..5b1ae28f3 100644
--- a/src/core/device_memory.h
+++ b/src/core/device_memory.h
@@ -4,14 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/assert.h" 7#include "common/common_types.h"
8#include "common/common_funcs.h"
9#include "common/virtual_buffer.h" 8#include "common/virtual_buffer.h"
10 9
11namespace Core { 10namespace Core {
12 11
13class System;
14
15namespace DramMemoryMap { 12namespace DramMemoryMap {
16enum : u64 { 13enum : u64 {
17 Base = 0x80000000ULL, 14 Base = 0x80000000ULL,
@@ -26,7 +23,7 @@ enum : u64 {
26 23
27class DeviceMemory : NonCopyable { 24class DeviceMemory : NonCopyable {
28public: 25public:
29 explicit DeviceMemory(Core::System& system); 26 explicit DeviceMemory();
30 ~DeviceMemory(); 27 ~DeviceMemory();
31 28
32 template <typename T> 29 template <typename T>
@@ -45,7 +42,6 @@ public:
45 42
46private: 43private:
47 Common::VirtualBuffer<u8> buffer; 44 Common::VirtualBuffer<u8> buffer;
48 Core::System& system;
49}; 45};
50 46
51} // namespace Core 47} // namespace Core
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 285277ef8..7c6304ff0 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -4,11 +4,10 @@
4 4
5#include <fmt/format.h> 5#include <fmt/format.h>
6#include "common/file_util.h" 6#include "common/file_util.h"
7#include "core/core.h"
8#include "core/file_sys/bis_factory.h" 7#include "core/file_sys/bis_factory.h"
9#include "core/file_sys/mode.h" 8#include "core/file_sys/mode.h"
10#include "core/file_sys/registered_cache.h" 9#include "core/file_sys/registered_cache.h"
11#include "core/settings.h" 10#include "core/file_sys/vfs.h"
12 11
13namespace FileSys { 12namespace FileSys {
14 13
@@ -82,11 +81,11 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {
82 } 81 }
83} 82}
84 83
85VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { 84VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id,
85 VirtualFilesystem file_system) const {
86 auto& keys = Core::Crypto::KeyManager::Instance(); 86 auto& keys = Core::Crypto::KeyManager::Instance();
87 Core::Crypto::PartitionDataManager pdm{ 87 Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory(
88 Core::System::GetInstance().GetFilesystem()->OpenDirectory( 88 Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), Mode::Read)};
89 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)};
90 keys.PopulateFromPartitionData(pdm); 89 keys.PopulateFromPartitionData(pdm);
91 90
92 switch (id) { 91 switch (id) {
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 8f0451c98..136485881 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -6,7 +6,8 @@
6 6
7#include <memory> 7#include <memory>
8 8
9#include "core/file_sys/vfs.h" 9#include "common/common_types.h"
10#include "core/file_sys/vfs_types.h"
10 11
11namespace FileSys { 12namespace FileSys {
12 13
@@ -51,7 +52,7 @@ public:
51 VirtualDir GetModificationDumpRoot(u64 title_id) const; 52 VirtualDir GetModificationDumpRoot(u64 title_id) const;
52 53
53 VirtualDir OpenPartition(BisPartitionId id) const; 54 VirtualDir OpenPartition(BisPartitionId id) const;
54 VirtualFile OpenPartitionStorage(BisPartitionId id) const; 55 VirtualFile OpenPartitionStorage(BisPartitionId id, VirtualFilesystem file_system) const;
55 56
56 VirtualDir GetImageDirectory() const; 57 VirtualDir GetImageDirectory() const;
57 58
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 664a47e7f..956da68f7 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -8,11 +8,11 @@
8#include <fmt/ostream.h> 8#include <fmt/ostream.h>
9 9
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "core/crypto/key_manager.h"
11#include "core/file_sys/card_image.h" 12#include "core/file_sys/card_image.h"
12#include "core/file_sys/content_archive.h" 13#include "core/file_sys/content_archive.h"
13#include "core/file_sys/nca_metadata.h" 14#include "core/file_sys/nca_metadata.h"
14#include "core/file_sys/partition_filesystem.h" 15#include "core/file_sys/partition_filesystem.h"
15#include "core/file_sys/romfs.h"
16#include "core/file_sys/submission_package.h" 16#include "core/file_sys/submission_package.h"
17#include "core/file_sys/vfs_concat.h" 17#include "core/file_sys/vfs_concat.h"
18#include "core/file_sys/vfs_offset.h" 18#include "core/file_sys/vfs_offset.h"
@@ -31,7 +31,8 @@ constexpr std::array partition_names{
31 31
32XCI::XCI(VirtualFile file_) 32XCI::XCI(VirtualFile file_)
33 : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, 33 : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
34 partitions(partition_names.size()), partitions_raw(partition_names.size()) { 34 partitions(partition_names.size()),
35 partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} {
35 if (file->ReadObject(&header) != sizeof(GamecardHeader)) { 36 if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
36 status = Loader::ResultStatus::ErrorBadXCIHeader; 37 status = Loader::ResultStatus::ErrorBadXCIHeader;
37 return; 38 return;
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index e1b136426..2d0a0f285 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -9,9 +9,12 @@
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/crypto/key_manager.h"
13#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
14 13
14namespace Core::Crypto {
15class KeyManager;
16}
17
15namespace Loader { 18namespace Loader {
16enum class ResultStatus : u16; 19enum class ResultStatus : u16;
17} 20}
@@ -140,6 +143,6 @@ private:
140 143
141 u64 update_normal_partition_end; 144 u64 update_normal_partition_end;
142 145
143 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); 146 Core::Crypto::KeyManager& keys;
144}; 147};
145} // namespace FileSys 148} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 473245d5a..76af47ff9 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -10,10 +10,10 @@
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "core/crypto/aes_util.h" 11#include "core/crypto/aes_util.h"
12#include "core/crypto/ctr_encryption_layer.h" 12#include "core/crypto/ctr_encryption_layer.h"
13#include "core/crypto/key_manager.h"
13#include "core/file_sys/content_archive.h" 14#include "core/file_sys/content_archive.h"
14#include "core/file_sys/nca_patch.h" 15#include "core/file_sys/nca_patch.h"
15#include "core/file_sys/partition_filesystem.h" 16#include "core/file_sys/partition_filesystem.h"
16#include "core/file_sys/romfs.h"
17#include "core/file_sys/vfs_offset.h" 17#include "core/file_sys/vfs_offset.h"
18#include "core/loader/loader.h" 18#include "core/loader/loader.h"
19 19
@@ -119,7 +119,8 @@ static bool IsValidNCA(const NCAHeader& header) {
119} 119}
120 120
121NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) 121NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
122 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) { 122 : file(std::move(file_)),
123 bktr_base_romfs(std::move(bktr_base_romfs_)), keys{Core::Crypto::KeyManager::Instance()} {
123 if (file == nullptr) { 124 if (file == nullptr) {
124 status = Loader::ResultStatus::ErrorNullFile; 125 status = Loader::ResultStatus::ErrorNullFile;
125 return; 126 return;
@@ -322,7 +323,7 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl
322 subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low}); 323 subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
323 subsection_buckets.back().entries.push_back({size, {0}, 0}); 324 subsection_buckets.back().entries.push_back({size, {0}, 0});
324 325
325 std::optional<Core::Crypto::Key128> key = {}; 326 std::optional<Core::Crypto::Key128> key;
326 if (encrypted) { 327 if (encrypted) {
327 if (has_rights_id) { 328 if (has_rights_id) {
328 status = Loader::ResultStatus::Success; 329 status = Loader::ResultStatus::Success;
@@ -441,18 +442,18 @@ std::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
441 memcpy(rights_id.data(), header.rights_id.data(), 16); 442 memcpy(rights_id.data(), header.rights_id.data(), 16);
442 if (rights_id == u128{}) { 443 if (rights_id == u128{}) {
443 status = Loader::ResultStatus::ErrorInvalidRightsID; 444 status = Loader::ResultStatus::ErrorInvalidRightsID;
444 return {}; 445 return std::nullopt;
445 } 446 }
446 447
447 auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); 448 auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
448 if (titlekey == Core::Crypto::Key128{}) { 449 if (titlekey == Core::Crypto::Key128{}) {
449 status = Loader::ResultStatus::ErrorMissingTitlekey; 450 status = Loader::ResultStatus::ErrorMissingTitlekey;
450 return {}; 451 return std::nullopt;
451 } 452 }
452 453
453 if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { 454 if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
454 status = Loader::ResultStatus::ErrorMissingTitlekek; 455 status = Loader::ResultStatus::ErrorMissingTitlekek;
455 return {}; 456 return std::nullopt;
456 } 457 }
457 458
458 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( 459 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
@@ -476,7 +477,7 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
476 case NCASectionCryptoType::BKTR: 477 case NCASectionCryptoType::BKTR:
477 LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); 478 LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
478 { 479 {
479 std::optional<Core::Crypto::Key128> key = {}; 480 std::optional<Core::Crypto::Key128> key;
480 if (has_rights_id) { 481 if (has_rights_id) {
481 status = Loader::ResultStatus::Success; 482 status = Loader::ResultStatus::Success;
482 key = GetTitlekey(); 483 key = GetTitlekey();
@@ -495,9 +496,10 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
495 496
496 auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key, 497 auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key,
497 starting_offset); 498 starting_offset);
498 std::vector<u8> iv(16); 499 Core::Crypto::CTREncryptionLayer::IVData iv{};
499 for (u8 i = 0; i < 8; ++i) 500 for (std::size_t i = 0; i < 8; ++i) {
500 iv[i] = s_header.raw.section_ctr[0x8 - i - 1]; 501 iv[i] = s_header.raw.section_ctr[8 - i - 1];
502 }
501 out->SetIV(iv); 503 out->SetIV(iv);
502 return std::static_pointer_cast<VfsFile>(out); 504 return std::static_pointer_cast<VfsFile>(out);
503 } 505 }
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index d25cbcf91..69292232a 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -158,7 +158,7 @@ private:
158 bool encrypted = false; 158 bool encrypted = false;
159 bool is_update = false; 159 bool is_update = false;
160 160
161 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); 161 Core::Crypto::KeyManager& keys;
162}; 162};
163 163
164} // namespace FileSys 164} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 63cd2eead..b0a130345 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -5,6 +5,7 @@
5#include "common/string_util.h" 5#include "common/string_util.h"
6#include "common/swap.h" 6#include "common/swap.h"
7#include "core/file_sys/control_metadata.h" 7#include "core/file_sys/control_metadata.h"
8#include "core/file_sys/vfs.h"
8 9
9namespace FileSys { 10namespace FileSys {
10 11
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index e37b2fadf..403c4219a 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -10,7 +10,7 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/swap.h" 12#include "common/swap.h"
13#include "core/file_sys/vfs.h" 13#include "core/file_sys/vfs_types.h"
14 14
15namespace FileSys { 15namespace FileSys {
16 16
@@ -83,7 +83,7 @@ enum class Language : u8 {
83 Italian = 7, 83 Italian = 7,
84 Dutch = 8, 84 Dutch = 8,
85 CanadianFrench = 9, 85 CanadianFrench = 9,
86 Portugese = 10, 86 Portuguese = 10,
87 Russian = 11, 87 Russian = 11,
88 Korean = 12, 88 Korean = 12,
89 Taiwanese = 13, 89 Taiwanese = 13,
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index a08a70efd..dd779310f 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -245,9 +245,11 @@ void IPSwitchCompiler::Parse() {
245 245
246 // Read rest of patch 246 // Read rest of patch
247 while (true) { 247 while (true) {
248 if (i + 1 >= lines.size()) 248 if (i + 1 >= lines.size()) {
249 break; 249 break;
250 const auto patch_line = lines[++i]; 250 }
251
252 const auto& patch_line = lines[++i];
251 253
252 // Start of new patch 254 // Start of new patch
253 if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) { 255 if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) {
diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp
index 76313679d..ef93ef3ed 100644
--- a/src/core/file_sys/kernel_executable.cpp
+++ b/src/core/file_sys/kernel_executable.cpp
@@ -2,9 +2,12 @@
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 <cstring>
6
5#include "common/string_util.h" 7#include "common/string_util.h"
6#include "core/file_sys/kernel_executable.h" 8#include "core/file_sys/kernel_executable.h"
7#include "core/file_sys/vfs_offset.h" 9#include "core/file_sys/vfs_offset.h"
10#include "core/loader/loader.h"
8 11
9namespace FileSys { 12namespace FileSys {
10 13
diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h
index 324a57384..044c554d3 100644
--- a/src/core/file_sys/kernel_executable.h
+++ b/src/core/file_sys/kernel_executable.h
@@ -4,10 +4,17 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <vector>
9
7#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h"
8#include "common/swap.h" 12#include "common/swap.h"
9#include "core/file_sys/vfs_types.h" 13#include "core/file_sys/vfs_types.h"
10#include "core/loader/loader.h" 14
15namespace Loader {
16enum class ResultStatus : u16;
17}
11 18
12namespace FileSys { 19namespace FileSys {
13 20
diff --git a/src/core/file_sys/mode.h b/src/core/file_sys/mode.h
index c95205668..2b4f21073 100644
--- a/src/core/file_sys/mode.h
+++ b/src/core/file_sys/mode.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_funcs.h"
7#include "common/common_types.h" 8#include "common/common_types.h"
8 9
9namespace FileSys { 10namespace FileSys {
@@ -11,13 +12,11 @@ namespace FileSys {
11enum class Mode : u32 { 12enum class Mode : u32 {
12 Read = 1, 13 Read = 1,
13 Write = 2, 14 Write = 2,
14 ReadWrite = 3, 15 ReadWrite = Read | Write,
15 Append = 4, 16 Append = 4,
16 WriteAppend = 6, 17 WriteAppend = Write | Append,
17}; 18};
18 19
19inline u32 operator&(Mode lhs, Mode rhs) { 20DECLARE_ENUM_FLAG_OPERATORS(Mode)
20 return static_cast<u32>(lhs) & static_cast<u32>(rhs);
21}
22 21
23} // namespace FileSys 22} // namespace FileSys
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp
index 93d0df6b9..2d1476e3a 100644
--- a/src/core/file_sys/nca_metadata.cpp
+++ b/src/core/file_sys/nca_metadata.cpp
@@ -7,6 +7,7 @@
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/swap.h" 8#include "common/swap.h"
9#include "core/file_sys/nca_metadata.h" 9#include "core/file_sys/nca_metadata.h"
10#include "core/file_sys/vfs.h"
10 11
11namespace FileSys { 12namespace FileSys {
12 13
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index 1f82fff0a..53535e5f5 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -10,7 +10,7 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/swap.h" 12#include "common/swap.h"
13#include "core/file_sys/vfs.h" 13#include "core/file_sys/vfs_types.h"
14 14
15namespace FileSys { 15namespace FileSys {
16class CNMT; 16class CNMT;
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp
index 0090cc6c4..5990a2fd5 100644
--- a/src/core/file_sys/nca_patch.cpp
+++ b/src/core/file_sys/nca_patch.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array>
6#include <cstddef> 7#include <cstddef>
7#include <cstring> 8#include <cstring>
8 9
@@ -11,6 +12,49 @@
11#include "core/file_sys/nca_patch.h" 12#include "core/file_sys/nca_patch.h"
12 13
13namespace FileSys { 14namespace FileSys {
15namespace {
16template <bool Subsection, typename BlockType, typename BucketType>
17std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, const BlockType& block,
18 const BucketType& buckets) {
19 if constexpr (Subsection) {
20 const auto& last_bucket = buckets[block.number_buckets - 1];
21 if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch) {
22 return {block.number_buckets - 1, last_bucket.number_entries};
23 }
24 } else {
25 ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block.");
26 }
27
28 std::size_t bucket_id = std::count_if(
29 block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets,
30 [&offset](u64 base_offset) { return base_offset <= offset; });
31
32 const auto& bucket = buckets[bucket_id];
33
34 if (bucket.number_entries == 1) {
35 return {bucket_id, 0};
36 }
37
38 std::size_t low = 0;
39 std::size_t mid = 0;
40 std::size_t high = bucket.number_entries - 1;
41 while (low <= high) {
42 mid = (low + high) / 2;
43 if (bucket.entries[mid].address_patch > offset) {
44 high = mid - 1;
45 } else {
46 if (mid == bucket.number_entries - 1 ||
47 bucket.entries[mid + 1].address_patch > offset) {
48 return {bucket_id, mid};
49 }
50
51 low = mid + 1;
52 }
53 }
54
55 UNREACHABLE_MSG("Offset could not be found in BKTR block.");
56}
57} // Anonymous namespace
14 58
15BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_, 59BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_,
16 std::vector<RelocationBucket> relocation_buckets_, SubsectionBlock subsection_, 60 std::vector<RelocationBucket> relocation_buckets_, SubsectionBlock subsection_,
@@ -66,7 +110,7 @@ std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const {
66 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR); 110 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR);
67 111
68 // Calculate AES IV 112 // Calculate AES IV
69 std::vector<u8> iv(16); 113 std::array<u8, 16> iv{};
70 auto subsection_ctr = subsection.ctr; 114 auto subsection_ctr = subsection.ctr;
71 auto offset_iv = section_offset + base_offset; 115 auto offset_iv = section_offset + base_offset;
72 for (std::size_t i = 0; i < section_ctr.size(); ++i) 116 for (std::size_t i = 0; i < section_ctr.size(); ++i)
@@ -109,46 +153,6 @@ std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const {
109 return raw_read; 153 return raw_read;
110} 154}
111 155
112template <bool Subsection, typename BlockType, typename BucketType>
113std::pair<std::size_t, std::size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block,
114 BucketType buckets) const {
115 if constexpr (Subsection) {
116 const auto last_bucket = buckets[block.number_buckets - 1];
117 if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch)
118 return {block.number_buckets - 1, last_bucket.number_entries};
119 } else {
120 ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block.");
121 }
122
123 std::size_t bucket_id = std::count_if(
124 block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets,
125 [&offset](u64 base_offset) { return base_offset <= offset; });
126
127 const auto bucket = buckets[bucket_id];
128
129 if (bucket.number_entries == 1)
130 return {bucket_id, 0};
131
132 std::size_t low = 0;
133 std::size_t mid = 0;
134 std::size_t high = bucket.number_entries - 1;
135 while (low <= high) {
136 mid = (low + high) / 2;
137 if (bucket.entries[mid].address_patch > offset) {
138 high = mid - 1;
139 } else {
140 if (mid == bucket.number_entries - 1 ||
141 bucket.entries[mid + 1].address_patch > offset) {
142 return {bucket_id, mid};
143 }
144
145 low = mid + 1;
146 }
147 }
148
149 UNREACHABLE_MSG("Offset could not be found in BKTR block.");
150}
151
152RelocationEntry BKTR::GetRelocationEntry(u64 offset) const { 156RelocationEntry BKTR::GetRelocationEntry(u64 offset) const {
153 const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets); 157 const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets);
154 return relocation_buckets[res.first].entries[res.second]; 158 return relocation_buckets[res.first].entries[res.second];
diff --git a/src/core/file_sys/nca_patch.h b/src/core/file_sys/nca_patch.h
index 8e64e8378..60c544f8e 100644
--- a/src/core/file_sys/nca_patch.h
+++ b/src/core/file_sys/nca_patch.h
@@ -117,10 +117,6 @@ public:
117 bool Rename(std::string_view name) override; 117 bool Rename(std::string_view name) override;
118 118
119private: 119private:
120 template <bool Subsection, typename BlockType, typename BucketType>
121 std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, BlockType block,
122 BucketType buckets) const;
123
124 RelocationEntry GetRelocationEntry(u64 offset) const; 120 RelocationEntry GetRelocationEntry(u64 offset) const;
125 RelocationEntry GetNextRelocationEntry(u64 offset) const; 121 RelocationEntry GetNextRelocationEntry(u64 offset) const;
126 122
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp
index 846986736..48a2ed4d4 100644
--- a/src/core/file_sys/partition_filesystem.cpp
+++ b/src/core/file_sys/partition_filesystem.cpp
@@ -21,7 +21,7 @@ bool PartitionFilesystem::Header::HasValidMagicValue() const {
21 magic == Common::MakeMagic('P', 'F', 'S', '0'); 21 magic == Common::MakeMagic('P', 'F', 'S', '0');
22} 22}
23 23
24PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { 24PartitionFilesystem::PartitionFilesystem(VirtualFile file) {
25 // At least be as large as the header 25 // At least be as large as the header
26 if (file->GetSize() < sizeof(Header)) { 26 if (file->GetSize() < sizeof(Header)) {
27 status = Loader::ResultStatus::ErrorBadPFSHeader; 27 status = Loader::ResultStatus::ErrorBadPFSHeader;
@@ -89,11 +89,11 @@ std::map<std::string, u64> PartitionFilesystem::GetFileSizes() const {
89 return sizes; 89 return sizes;
90} 90}
91 91
92std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const { 92std::vector<VirtualFile> PartitionFilesystem::GetFiles() const {
93 return pfs_files; 93 return pfs_files;
94} 94}
95 95
96std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const { 96std::vector<VirtualDir> PartitionFilesystem::GetSubdirectories() const {
97 return {}; 97 return {};
98} 98}
99 99
@@ -101,7 +101,7 @@ std::string PartitionFilesystem::GetName() const {
101 return is_hfs ? "HFS0" : "PFS0"; 101 return is_hfs ? "HFS0" : "PFS0";
102} 102}
103 103
104std::shared_ptr<VfsDirectory> PartitionFilesystem::GetParentDirectory() const { 104VirtualDir PartitionFilesystem::GetParentDirectory() const {
105 // TODO(DarkLordZach): Add support for nested containers. 105 // TODO(DarkLordZach): Add support for nested containers.
106 return nullptr; 106 return nullptr;
107} 107}
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h
index 279193b19..0f831148e 100644
--- a/src/core/file_sys/partition_filesystem.h
+++ b/src/core/file_sys/partition_filesystem.h
@@ -24,7 +24,7 @@ namespace FileSys {
24 */ 24 */
25class PartitionFilesystem : public ReadOnlyVfsDirectory { 25class PartitionFilesystem : public ReadOnlyVfsDirectory {
26public: 26public:
27 explicit PartitionFilesystem(std::shared_ptr<VfsFile> file); 27 explicit PartitionFilesystem(VirtualFile file);
28 ~PartitionFilesystem() override; 28 ~PartitionFilesystem() override;
29 29
30 Loader::ResultStatus GetStatus() const; 30 Loader::ResultStatus GetStatus() const;
@@ -32,10 +32,10 @@ public:
32 std::map<std::string, u64> GetFileOffsets() const; 32 std::map<std::string, u64> GetFileOffsets() const;
33 std::map<std::string, u64> GetFileSizes() const; 33 std::map<std::string, u64> GetFileSizes() const;
34 34
35 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; 35 std::vector<VirtualFile> GetFiles() const override;
36 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; 36 std::vector<VirtualDir> GetSubdirectories() const override;
37 std::string GetName() const override; 37 std::string GetName() const override;
38 std::shared_ptr<VfsDirectory> GetParentDirectory() const override; 38 VirtualDir GetParentDirectory() const override;
39 void PrintDebugInfo() const; 39 void PrintDebugInfo() const;
40 40
41private: 41private:
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index c47ff863e..b9c09b456 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -27,6 +27,7 @@
27#include "core/settings.h" 27#include "core/settings.h"
28 28
29namespace FileSys { 29namespace FileSys {
30namespace {
30 31
31constexpr u64 SINGLE_BYTE_MODULUS = 0x100; 32constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
32constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; 33constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
@@ -36,21 +37,29 @@ constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
36 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9", 37 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
37}; 38};
38 39
39std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { 40enum class TitleVersionFormat : u8 {
41 ThreeElements, ///< vX.Y.Z
42 FourElements, ///< vX.Y.Z.W
43};
44
45std::string FormatTitleVersion(u32 version,
46 TitleVersionFormat format = TitleVersionFormat::ThreeElements) {
40 std::array<u8, sizeof(u32)> bytes{}; 47 std::array<u8, sizeof(u32)> bytes{};
41 bytes[0] = version % SINGLE_BYTE_MODULUS; 48 bytes[0] = static_cast<u8>(version % SINGLE_BYTE_MODULUS);
42 for (std::size_t i = 1; i < bytes.size(); ++i) { 49 for (std::size_t i = 1; i < bytes.size(); ++i) {
43 version /= SINGLE_BYTE_MODULUS; 50 version /= SINGLE_BYTE_MODULUS;
44 bytes[i] = version % SINGLE_BYTE_MODULUS; 51 bytes[i] = static_cast<u8>(version % SINGLE_BYTE_MODULUS);
45 } 52 }
46 53
47 if (format == TitleVersionFormat::FourElements) 54 if (format == TitleVersionFormat::FourElements) {
48 return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); 55 return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
56 }
49 return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); 57 return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
50} 58}
51 59
52std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<VfsDirectory> dir, 60// Returns a directory with name matching name case-insensitive. Returns nullptr if directory
53 std::string_view name) { 61// doesn't have a directory with name.
62VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) {
54#ifdef _WIN32 63#ifdef _WIN32
55 return dir->GetSubdirectory(name); 64 return dir->GetSubdirectory(name);
56#else 65#else
@@ -66,6 +75,43 @@ std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<Vfs
66#endif 75#endif
67} 76}
68 77
78std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder(
79 u64 title_id, const PatchManager::BuildID& build_id_, const VirtualDir& base_path, bool upper) {
80 const auto build_id_raw = Common::HexToString(build_id_, upper);
81 const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
82 const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
83
84 if (file == nullptr) {
85 LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
86 title_id, build_id);
87 return std::nullopt;
88 }
89
90 std::vector<u8> data(file->GetSize());
91 if (file->Read(data.data(), data.size()) != data.size()) {
92 LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
93 title_id, build_id);
94 return std::nullopt;
95 }
96
97 const Core::Memory::TextCheatParser parser;
98 return parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
99}
100
101void AppendCommaIfNotEmpty(std::string& to, std::string_view with) {
102 if (to.empty()) {
103 to += with;
104 } else {
105 to += ", ";
106 to += with;
107 }
108}
109
110bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
111 return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
112}
113} // Anonymous namespace
114
69PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} 115PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
70 116
71PatchManager::~PatchManager() = default; 117PatchManager::~PatchManager() = default;
@@ -246,7 +292,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
246 return out; 292 return out;
247} 293}
248 294
249bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { 295bool PatchManager::HasNSOPatch(const BuildID& build_id_) const {
250 const auto build_id_raw = Common::HexToString(build_id_); 296 const auto build_id_raw = Common::HexToString(build_id_);
251 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); 297 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
252 298
@@ -266,36 +312,8 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
266 return !CollectPatches(patch_dirs, build_id).empty(); 312 return !CollectPatches(patch_dirs, build_id).empty();
267} 313}
268 314
269namespace {
270std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder(
271 const Core::System& system, u64 title_id, const std::array<u8, 0x20>& build_id_,
272 const VirtualDir& base_path, bool upper) {
273 const auto build_id_raw = Common::HexToString(build_id_, upper);
274 const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
275 const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
276
277 if (file == nullptr) {
278 LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
279 title_id, build_id);
280 return std::nullopt;
281 }
282
283 std::vector<u8> data(file->GetSize());
284 if (file->Read(data.data(), data.size()) != data.size()) {
285 LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
286 title_id, build_id);
287 return std::nullopt;
288 }
289
290 Core::Memory::TextCheatParser parser;
291 return parser.Parse(
292 system, std::string_view(reinterpret_cast<const char* const>(data.data()), data.size()));
293}
294
295} // Anonymous namespace
296
297std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( 315std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
298 const Core::System& system, const std::array<u8, 32>& build_id_) const { 316 const Core::System& system, const BuildID& build_id_) const {
299 const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id); 317 const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id);
300 if (load_dir == nullptr) { 318 if (load_dir == nullptr) {
301 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); 319 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
@@ -315,14 +333,12 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
315 333
316 auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats"); 334 auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats");
317 if (cheats_dir != nullptr) { 335 if (cheats_dir != nullptr) {
318 auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true); 336 if (const auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true)) {
319 if (res.has_value()) {
320 std::copy(res->begin(), res->end(), std::back_inserter(out)); 337 std::copy(res->begin(), res->end(), std::back_inserter(out));
321 continue; 338 continue;
322 } 339 }
323 340
324 res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false); 341 if (const auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false)) {
325 if (res.has_value()) {
326 std::copy(res->begin(), res->end(), std::back_inserter(out)); 342 std::copy(res->begin(), res->end(), std::back_inserter(out));
327 } 343 }
328 } 344 }
@@ -436,21 +452,11 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
436 return romfs; 452 return romfs;
437} 453}
438 454
439static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) { 455PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile update_raw) const {
440 if (to.empty()) 456 if (title_id == 0) {
441 to += with;
442 else
443 to += ", " + with;
444}
445
446static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
447 return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
448}
449
450std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
451 VirtualFile update_raw) const {
452 if (title_id == 0)
453 return {}; 457 return {};
458 }
459
454 std::map<std::string, std::string, std::less<>> out; 460 std::map<std::string, std::string, std::less<>> out;
455 const auto& installed = Core::System::GetInstance().GetContentProvider(); 461 const auto& installed = Core::System::GetInstance().GetContentProvider();
456 const auto& disabled = Settings::values.disabled_addons[title_id]; 462 const auto& disabled = Settings::values.disabled_addons[title_id];
@@ -473,8 +479,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
473 if (meta_ver.value_or(0) == 0) { 479 if (meta_ver.value_or(0) == 0) {
474 out.insert_or_assign(update_label, ""); 480 out.insert_or_assign(update_label, "");
475 } else { 481 } else {
476 out.insert_or_assign( 482 out.insert_or_assign(update_label, FormatTitleVersion(*meta_ver));
477 update_label, FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements));
478 } 483 }
479 } else if (update_raw != nullptr) { 484 } else if (update_raw != nullptr) {
480 out.insert_or_assign(update_label, "PACKED"); 485 out.insert_or_assign(update_label, "PACKED");
@@ -563,40 +568,46 @@ std::optional<u32> PatchManager::GetGameVersion() const {
563 return installed.GetEntryVersion(title_id); 568 return installed.GetEntryVersion(title_id);
564} 569}
565 570
566std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { 571PatchManager::Metadata PatchManager::GetControlMetadata() const {
567 const auto& installed = Core::System::GetInstance().GetContentProvider(); 572 const auto& installed = Core::System::GetInstance().GetContentProvider();
568 573
569 const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); 574 const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control);
570 if (base_control_nca == nullptr) 575 if (base_control_nca == nullptr) {
571 return {}; 576 return {};
577 }
572 578
573 return ParseControlNCA(*base_control_nca); 579 return ParseControlNCA(*base_control_nca);
574} 580}
575 581
576std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const { 582PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const {
577 const auto base_romfs = nca.GetRomFS(); 583 const auto base_romfs = nca.GetRomFS();
578 if (base_romfs == nullptr) 584 if (base_romfs == nullptr) {
579 return {}; 585 return {};
586 }
580 587
581 const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control); 588 const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control);
582 if (romfs == nullptr) 589 if (romfs == nullptr) {
583 return {}; 590 return {};
591 }
584 592
585 const auto extracted = ExtractRomFS(romfs); 593 const auto extracted = ExtractRomFS(romfs);
586 if (extracted == nullptr) 594 if (extracted == nullptr) {
587 return {}; 595 return {};
596 }
588 597
589 auto nacp_file = extracted->GetFile("control.nacp"); 598 auto nacp_file = extracted->GetFile("control.nacp");
590 if (nacp_file == nullptr) 599 if (nacp_file == nullptr) {
591 nacp_file = extracted->GetFile("Control.nacp"); 600 nacp_file = extracted->GetFile("Control.nacp");
601 }
592 602
593 auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file); 603 auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file);
594 604
595 VirtualFile icon_file; 605 VirtualFile icon_file;
596 for (const auto& language : FileSys::LANGUAGE_NAMES) { 606 for (const auto& language : FileSys::LANGUAGE_NAMES) {
597 icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat"); 607 icon_file = extracted->GetFile(std::string("icon_").append(language).append(".dat"));
598 if (icon_file != nullptr) 608 if (icon_file != nullptr) {
599 break; 609 break;
610 }
600 } 611 }
601 612
602 return {std::move(nacp), icon_file}; 613 return {std::move(nacp), icon_file};
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index f4cb918dd..1f28c6241 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -6,10 +6,11 @@
6 6
7#include <map> 7#include <map>
8#include <memory> 8#include <memory>
9#include <optional>
9#include <string> 10#include <string>
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "core/file_sys/nca_metadata.h" 12#include "core/file_sys/nca_metadata.h"
12#include "core/file_sys/vfs.h" 13#include "core/file_sys/vfs_types.h"
13#include "core/memory/dmnt_cheat_types.h" 14#include "core/memory/dmnt_cheat_types.h"
14 15
15namespace Core { 16namespace Core {
@@ -21,71 +22,62 @@ namespace FileSys {
21class NCA; 22class NCA;
22class NACP; 23class NACP;
23 24
24enum class TitleVersionFormat : u8 {
25 ThreeElements, ///< vX.Y.Z
26 FourElements, ///< vX.Y.Z.W
27};
28
29std::string FormatTitleVersion(u32 version,
30 TitleVersionFormat format = TitleVersionFormat::ThreeElements);
31
32// Returns a directory with name matching name case-insensitive. Returns nullptr if directory
33// doesn't have a directory with name.
34std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<VfsDirectory> dir,
35 std::string_view name);
36
37// A centralized class to manage patches to games. 25// A centralized class to manage patches to games.
38class PatchManager { 26class PatchManager {
39public: 27public:
28 using BuildID = std::array<u8, 0x20>;
29 using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>;
30 using PatchVersionNames = std::map<std::string, std::string, std::less<>>;
31
40 explicit PatchManager(u64 title_id); 32 explicit PatchManager(u64 title_id);
41 ~PatchManager(); 33 ~PatchManager();
42 34
43 u64 GetTitleID() const; 35 [[nodiscard]] u64 GetTitleID() const;
44 36
45 // Currently tracked ExeFS patches: 37 // Currently tracked ExeFS patches:
46 // - Game Updates 38 // - Game Updates
47 VirtualDir PatchExeFS(VirtualDir exefs) const; 39 [[nodiscard]] VirtualDir PatchExeFS(VirtualDir exefs) const;
48 40
49 // Currently tracked NSO patches: 41 // Currently tracked NSO patches:
50 // - IPS 42 // - IPS
51 // - IPSwitch 43 // - IPSwitch
52 std::vector<u8> PatchNSO(const std::vector<u8>& nso, const std::string& name) const; 44 [[nodiscard]] std::vector<u8> PatchNSO(const std::vector<u8>& nso,
45 const std::string& name) const;
53 46
54 // Checks to see if PatchNSO() will have any effect given the NSO's build ID. 47 // Checks to see if PatchNSO() will have any effect given the NSO's build ID.
55 // Used to prevent expensive copies in NSO loader. 48 // Used to prevent expensive copies in NSO loader.
56 bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; 49 [[nodiscard]] bool HasNSOPatch(const BuildID& build_id) const;
57 50
58 // Creates a CheatList object with all 51 // Creates a CheatList object with all
59 std::vector<Core::Memory::CheatEntry> CreateCheatList( 52 [[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList(
60 const Core::System& system, const std::array<u8, 0x20>& build_id) const; 53 const Core::System& system, const BuildID& build_id) const;
61 54
62 // Currently tracked RomFS patches: 55 // Currently tracked RomFS patches:
63 // - Game Updates 56 // - Game Updates
64 // - LayeredFS 57 // - LayeredFS
65 VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, 58 [[nodiscard]] VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
66 ContentRecordType type = ContentRecordType::Program, 59 ContentRecordType type = ContentRecordType::Program,
67 VirtualFile update_raw = nullptr) const; 60 VirtualFile update_raw = nullptr) const;
68 61
69 // Returns a vector of pairs between patch names and patch versions. 62 // Returns a vector of pairs between patch names and patch versions.
70 // i.e. Update 3.2.2 will return {"Update", "3.2.2"} 63 // i.e. Update 3.2.2 will return {"Update", "3.2.2"}
71 std::map<std::string, std::string, std::less<>> GetPatchVersionNames( 64 [[nodiscard]] PatchVersionNames GetPatchVersionNames(VirtualFile update_raw = nullptr) const;
72 VirtualFile update_raw = nullptr) const;
73 65
74 // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails, 66 // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails,
75 // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be 67 // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be
76 // std::nullopt 68 // std::nullopt
77 std::optional<u32> GetGameVersion() const; 69 [[nodiscard]] std::optional<u32> GetGameVersion() const;
78 70
79 // Given title_id of the program, attempts to get the control data of the update and parse 71 // Given title_id of the program, attempts to get the control data of the update and parse
80 // it, falling back to the base control data. 72 // it, falling back to the base control data.
81 std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const; 73 [[nodiscard]] Metadata GetControlMetadata() const;
82 74
83 // Version of GetControlMetadata that takes an arbitrary NCA 75 // Version of GetControlMetadata that takes an arbitrary NCA
84 std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const; 76 [[nodiscard]] Metadata ParseControlNCA(const NCA& nca) const;
85 77
86private: 78private:
87 std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, 79 [[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
88 const std::string& build_id) const; 80 const std::string& build_id) const;
89 81
90 u64 title_id; 82 u64 title_id;
91}; 83};
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 43169bf9f..9cf49bf44 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -7,6 +7,7 @@
7 7
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/file_sys/program_metadata.h" 9#include "core/file_sys/program_metadata.h"
10#include "core/file_sys/vfs.h"
10#include "core/loader/loader.h" 11#include "core/loader/loader.h"
11 12
12namespace FileSys { 13namespace FileSys {
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 35069972b..455532567 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -9,7 +9,7 @@
9#include "common/bit_field.h" 9#include "common/bit_field.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs_types.h"
13 13
14namespace Loader { 14namespace Loader {
15enum class ResultStatus : u16; 15enum class ResultStatus : u16;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 37351c561..da01002d5 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -257,8 +257,7 @@ std::vector<NcaID> PlaceholderCache::List() const {
257 for (const auto& sdir : dir->GetSubdirectories()) { 257 for (const auto& sdir : dir->GetSubdirectories()) {
258 for (const auto& file : sdir->GetFiles()) { 258 for (const auto& file : sdir->GetFiles()) {
259 const auto name = file->GetName(); 259 const auto name = file->GetName();
260 if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' && 260 if (name.length() == 36 && name.ends_with(".nca")) {
261 name[35] == 'a') {
262 out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32))); 261 out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32)));
263 } 262 }
264 } 263 }
@@ -344,15 +343,18 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
344 343
345static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id, 344static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id,
346 ContentRecordType type) { 345 ContentRecordType type) {
347 if (map.find(title_id) == map.end()) 346 const auto cmnt_iter = map.find(title_id);
348 return {}; 347 if (cmnt_iter == map.cend()) {
349 348 return std::nullopt;
350 const auto& cnmt = map.at(title_id); 349 }
351 350
352 const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(), 351 const auto& cnmt = cmnt_iter->second;
352 const auto& content_records = cnmt.GetContentRecords();
353 const auto iter = std::find_if(content_records.cbegin(), content_records.cend(),
353 [type](const ContentRecord& rec) { return rec.type == type; }); 354 [type](const ContentRecord& rec) { return rec.type == type; });
354 if (iter == cnmt.GetContentRecords().end()) 355 if (iter == content_records.cend()) {
355 return {}; 356 return std::nullopt;
357 }
356 358
357 return std::make_optional(iter->nca_id); 359 return std::make_optional(iter->nca_id);
358} 360}
@@ -467,14 +469,16 @@ VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType ty
467 469
468std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { 470std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
469 const auto meta_iter = meta.find(title_id); 471 const auto meta_iter = meta.find(title_id);
470 if (meta_iter != meta.end()) 472 if (meta_iter != meta.cend()) {
471 return meta_iter->second.GetTitleVersion(); 473 return meta_iter->second.GetTitleVersion();
474 }
472 475
473 const auto yuzu_meta_iter = yuzu_meta.find(title_id); 476 const auto yuzu_meta_iter = yuzu_meta.find(title_id);
474 if (yuzu_meta_iter != yuzu_meta.end()) 477 if (yuzu_meta_iter != yuzu_meta.cend()) {
475 return yuzu_meta_iter->second.GetTitleVersion(); 478 return yuzu_meta_iter->second.GetTitleVersion();
479 }
476 480
477 return {}; 481 return std::nullopt;
478} 482}
479 483
480VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const { 484VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const {
@@ -547,56 +551,6 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex
547 return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy); 551 return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy);
548} 552}
549 553
550bool RegisteredCache::RemoveExistingEntry(u64 title_id) {
551 const auto delete_nca = [this](const NcaID& id) {
552 const auto path = GetRelativePathFromNcaID(id, false, true, false);
553
554 if (dir->GetFileRelative(path) == nullptr) {
555 return false;
556 }
557
558 Core::Crypto::SHA256Hash hash{};
559 mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
560 const auto dirname = fmt::format("000000{:02X}", hash[0]);
561
562 const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
563
564 const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
565
566 return res;
567 };
568
569 // If an entry exists in the registered cache, remove it
570 if (HasEntry(title_id, ContentRecordType::Meta)) {
571 LOG_INFO(Loader,
572 "Previously installed entry (v{}) for title_id={:016X} detected! "
573 "Attempting to remove...",
574 GetEntryVersion(title_id).value_or(0), title_id);
575 // Get all the ncas associated with the current CNMT and delete them
576 const auto meta_old_id =
577 GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
578 const auto program_id =
579 GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
580 const auto data_id =
581 GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
582 const auto control_id =
583 GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
584 const auto html_id =
585 GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
586 const auto legal_id =
587 GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
588
589 delete_nca(meta_old_id);
590 delete_nca(program_id);
591 delete_nca(data_id);
592 delete_nca(control_id);
593 delete_nca(html_id);
594 delete_nca(legal_id);
595 return true;
596 }
597 return false;
598}
599
600InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists, 554InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists,
601 const VfsCopyFunction& copy) { 555 const VfsCopyFunction& copy) {
602 const auto ncas = nsp.GetNCAsCollapsed(); 556 const auto ncas = nsp.GetNCAsCollapsed();
@@ -666,25 +620,25 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
666 620
667InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, 621InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
668 bool overwrite_if_exists, const VfsCopyFunction& copy) { 622 bool overwrite_if_exists, const VfsCopyFunction& copy) {
669 CNMTHeader header{ 623 const CNMTHeader header{
670 nca.GetTitleId(), // Title ID 624 .title_id = nca.GetTitleId(),
671 0, // Ignore/Default title version 625 .title_version = 0,
672 type, // Type 626 .type = type,
673 {}, // Padding 627 .reserved = {},
674 0x10, // Default table offset 628 .table_offset = 0x10,
675 1, // 1 Content Entry 629 .number_content_entries = 1,
676 0, // No Meta Entries 630 .number_meta_entries = 0,
677 {}, // Padding 631 .attributes = 0,
678 {}, // Reserved 1 632 .reserved2 = {},
679 0, // Is committed 633 .is_committed = 0,
680 0, // Required download system version 634 .required_download_system_version = 0,
681 {}, // Reserved 2 635 .reserved3 = {},
682 }; 636 };
683 OptionalHeader opt_header{0, 0}; 637 const OptionalHeader opt_header{0, 0};
684 ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}}; 638 ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}};
685 const auto& data = nca.GetBaseFile()->ReadBytes(0x100000); 639 const auto& data = nca.GetBaseFile()->ReadBytes(0x100000);
686 mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0); 640 mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0);
687 memcpy(&c_rec.nca_id, &c_rec.hash, 16); 641 std::memcpy(&c_rec.nca_id, &c_rec.hash, 16);
688 const CNMT new_cnmt(header, opt_header, {c_rec}, {}); 642 const CNMT new_cnmt(header, opt_header, {c_rec}, {});
689 if (!RawInstallYuzuMeta(new_cnmt)) { 643 if (!RawInstallYuzuMeta(new_cnmt)) {
690 return InstallResult::ErrorMetaFailed; 644 return InstallResult::ErrorMetaFailed;
@@ -692,6 +646,57 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
692 return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); 646 return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
693} 647}
694 648
649bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
650 const auto delete_nca = [this](const NcaID& id) {
651 const auto path = GetRelativePathFromNcaID(id, false, true, false);
652
653 const bool isFile = dir->GetFileRelative(path) != nullptr;
654 const bool isDir = dir->GetDirectoryRelative(path) != nullptr;
655
656 if (isFile) {
657 return dir->DeleteFile(path);
658 } else if (isDir) {
659 return dir->DeleteSubdirectoryRecursive(path);
660 }
661
662 return false;
663 };
664
665 // If an entry exists in the registered cache, remove it
666 if (HasEntry(title_id, ContentRecordType::Meta)) {
667 LOG_INFO(Loader,
668 "Previously installed entry (v{}) for title_id={:016X} detected! "
669 "Attempting to remove...",
670 GetEntryVersion(title_id).value_or(0), title_id);
671
672 // Get all the ncas associated with the current CNMT and delete them
673 const auto meta_old_id =
674 GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
675 const auto program_id =
676 GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
677 const auto data_id =
678 GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
679 const auto control_id =
680 GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
681 const auto html_id =
682 GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
683 const auto legal_id =
684 GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
685
686 const auto deleted_meta = delete_nca(meta_old_id);
687 const auto deleted_program = delete_nca(program_id);
688 const auto deleted_data = delete_nca(data_id);
689 const auto deleted_control = delete_nca(control_id);
690 const auto deleted_html = delete_nca(html_id);
691 const auto deleted_legal = delete_nca(legal_id);
692
693 return deleted_meta && (deleted_meta || deleted_program || deleted_data ||
694 deleted_control || deleted_html || deleted_legal);
695 }
696
697 return false;
698}
699
695InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy, 700InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
696 bool overwrite_if_exists, 701 bool overwrite_if_exists,
697 std::optional<NcaID> override_id) { 702 std::optional<NcaID> override_id) {
@@ -722,7 +727,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
722 LOG_WARNING(Loader, "Overwriting existing NCA..."); 727 LOG_WARNING(Loader, "Overwriting existing NCA...");
723 VirtualDir c_dir; 728 VirtualDir c_dir;
724 { c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); } 729 { c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); }
725 c_dir->DeleteFile(FileUtil::GetFilename(path)); 730 c_dir->DeleteFile(Common::FS::GetFilename(path));
726 } 731 }
727 732
728 auto out = dir->CreateFileRelative(path); 733 auto out = dir->CreateFileRelative(path);
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 29cf0d40c..5b414b0f0 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -133,9 +133,9 @@ public:
133 // Parsing function defines the conversion from raw file to NCA. If there are other steps 133 // Parsing function defines the conversion from raw file to NCA. If there are other steps
134 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom 134 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
135 // parsing function. 135 // parsing function.
136 explicit RegisteredCache(VirtualDir dir, 136 explicit RegisteredCache(
137 ContentProviderParsingFunction parsing_function = 137 VirtualDir dir, ContentProviderParsingFunction parsing_function =
138 [](const VirtualFile& file, const NcaID& id) { return file; }); 138 [](const VirtualFile& file, const NcaID& id) { return file; });
139 ~RegisteredCache() override; 139 ~RegisteredCache() override;
140 140
141 void Refresh() override; 141 void Refresh() override;
@@ -155,9 +155,6 @@ public:
155 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, 155 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
156 std::optional<u64> title_id = {}) const override; 156 std::optional<u64> title_id = {}) const override;
157 157
158 // Removes an existing entry based on title id
159 bool RemoveExistingEntry(u64 title_id);
160
161 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure 158 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
162 // there is a meta NCA and all of them are accessible. 159 // there is a meta NCA and all of them are accessible.
163 InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false, 160 InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,
@@ -172,6 +169,9 @@ public:
172 InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, 169 InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false,
173 const VfsCopyFunction& copy = &VfsRawCopy); 170 const VfsCopyFunction& copy = &VfsRawCopy);
174 171
172 // Removes an existing entry based on title id
173 bool RemoveExistingEntry(u64 title_id) const;
174
175private: 175private:
176 template <typename T> 176 template <typename T>
177 void IterateAllMetadata(std::vector<T>& out, 177 void IterateAllMetadata(std::vector<T>& out,
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h
index 2fd07ed04..82e683782 100644
--- a/src/core/file_sys/romfs.h
+++ b/src/core/file_sys/romfs.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include "core/file_sys/vfs.h" 7#include "core/file_sys/vfs.h"
9 8
10namespace FileSys { 9namespace FileSys {
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index 418a39a7e..e967a254e 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -6,7 +6,6 @@
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/core.h"
10#include "core/file_sys/card_image.h" 9#include "core/file_sys/card_image.h"
11#include "core/file_sys/content_archive.h" 10#include "core/file_sys/content_archive.h"
12#include "core/file_sys/nca_metadata.h" 11#include "core/file_sys/nca_metadata.h"
@@ -19,7 +18,9 @@
19 18
20namespace FileSys { 19namespace FileSys {
21 20
22RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) { 21RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader, ContentProvider& provider,
22 Service::FileSystem::FileSystemController& controller)
23 : content_provider{provider}, filesystem_controller{controller} {
23 // Load the RomFS from the app 24 // Load the RomFS from the app
24 if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) { 25 if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) {
25 LOG_ERROR(Service_FS, "Unable to read RomFS!"); 26 LOG_ERROR(Service_FS, "Unable to read RomFS!");
@@ -46,39 +47,38 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_titl
46 47
47ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, 48ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
48 ContentRecordType type) const { 49 ContentRecordType type) const {
49 std::shared_ptr<NCA> res; 50 const std::shared_ptr<NCA> res = GetEntry(title_id, storage, type);
50
51 switch (storage) {
52 case StorageId::None:
53 res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
54 break;
55 case StorageId::NandSystem:
56 res =
57 Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry(
58 title_id, type);
59 break;
60 case StorageId::NandUser:
61 res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry(
62 title_id, type);
63 break;
64 case StorageId::SdCard:
65 res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry(
66 title_id, type);
67 break;
68 default:
69 UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));
70 }
71
72 if (res == nullptr) { 51 if (res == nullptr) {
73 // TODO(DarkLordZach): Find the right error code to use here 52 // TODO(DarkLordZach): Find the right error code to use here
74 return RESULT_UNKNOWN; 53 return RESULT_UNKNOWN;
75 } 54 }
55
76 const auto romfs = res->GetRomFS(); 56 const auto romfs = res->GetRomFS();
77 if (romfs == nullptr) { 57 if (romfs == nullptr) {
78 // TODO(DarkLordZach): Find the right error code to use here 58 // TODO(DarkLordZach): Find the right error code to use here
79 return RESULT_UNKNOWN; 59 return RESULT_UNKNOWN;
80 } 60 }
61
81 return MakeResult<VirtualFile>(romfs); 62 return MakeResult<VirtualFile>(romfs);
82} 63}
83 64
65std::shared_ptr<NCA> RomFSFactory::GetEntry(u64 title_id, StorageId storage,
66 ContentRecordType type) const {
67 switch (storage) {
68 case StorageId::None:
69 return content_provider.GetEntry(title_id, type);
70 case StorageId::NandSystem:
71 return filesystem_controller.GetSystemNANDContents()->GetEntry(title_id, type);
72 case StorageId::NandUser:
73 return filesystem_controller.GetUserNANDContents()->GetEntry(title_id, type);
74 case StorageId::SdCard:
75 return filesystem_controller.GetSDMCContents()->GetEntry(title_id, type);
76 case StorageId::Host:
77 case StorageId::GameCard:
78 default:
79 UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));
80 return nullptr;
81 }
82}
83
84} // namespace FileSys 84} // namespace FileSys
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
index c5d40285c..ec704dfa8 100644
--- a/src/core/file_sys/romfs_factory.h
+++ b/src/core/file_sys/romfs_factory.h
@@ -13,8 +13,15 @@ namespace Loader {
13class AppLoader; 13class AppLoader;
14} // namespace Loader 14} // namespace Loader
15 15
16namespace Service::FileSystem {
17class FileSystemController;
18}
19
16namespace FileSys { 20namespace FileSys {
17 21
22class ContentProvider;
23class NCA;
24
18enum class ContentRecordType : u8; 25enum class ContentRecordType : u8;
19 26
20enum class StorageId : u8 { 27enum class StorageId : u8 {
@@ -29,18 +36,26 @@ enum class StorageId : u8 {
29/// File system interface to the RomFS archive 36/// File system interface to the RomFS archive
30class RomFSFactory { 37class RomFSFactory {
31public: 38public:
32 explicit RomFSFactory(Loader::AppLoader& app_loader); 39 explicit RomFSFactory(Loader::AppLoader& app_loader, ContentProvider& provider,
40 Service::FileSystem::FileSystemController& controller);
33 ~RomFSFactory(); 41 ~RomFSFactory();
34 42
35 void SetPackedUpdate(VirtualFile update_raw); 43 void SetPackedUpdate(VirtualFile update_raw);
36 ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const; 44 [[nodiscard]] ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const;
37 ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const; 45 [[nodiscard]] ResultVal<VirtualFile> Open(u64 title_id, StorageId storage,
46 ContentRecordType type) const;
38 47
39private: 48private:
49 [[nodiscard]] std::shared_ptr<NCA> GetEntry(u64 title_id, StorageId storage,
50 ContentRecordType type) const;
51
40 VirtualFile file; 52 VirtualFile file;
41 VirtualFile update_raw; 53 VirtualFile update_raw;
42 bool updatable; 54 bool updatable;
43 u64 ivfc_offset; 55 u64 ivfc_offset;
56
57 ContentProvider& content_provider;
58 Service::FileSystem::FileSystemController& filesystem_controller;
44}; 59};
45 60
46} // namespace FileSys 61} // namespace FileSys
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index adfd2c1a4..ba4efee3a 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -17,23 +17,23 @@ constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
17 17
18namespace { 18namespace {
19 19
20void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) { 20void PrintSaveDataAttributeWarnings(SaveDataAttribute meta) {
21 if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { 21 if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
22 if (meta.zero_1 != 0) { 22 if (meta.zero_1 != 0) {
23 LOG_WARNING(Service_FS, 23 LOG_WARNING(Service_FS,
24 "Possibly incorrect SaveDataDescriptor, type is " 24 "Possibly incorrect SaveDataAttribute, type is "
25 "SystemSaveData||SaveData but offset 0x28 is non-zero ({:016X}).", 25 "SystemSaveData||SaveData but offset 0x28 is non-zero ({:016X}).",
26 meta.zero_1); 26 meta.zero_1);
27 } 27 }
28 if (meta.zero_2 != 0) { 28 if (meta.zero_2 != 0) {
29 LOG_WARNING(Service_FS, 29 LOG_WARNING(Service_FS,
30 "Possibly incorrect SaveDataDescriptor, type is " 30 "Possibly incorrect SaveDataAttribute, type is "
31 "SystemSaveData||SaveData but offset 0x30 is non-zero ({:016X}).", 31 "SystemSaveData||SaveData but offset 0x30 is non-zero ({:016X}).",
32 meta.zero_2); 32 meta.zero_2);
33 } 33 }
34 if (meta.zero_3 != 0) { 34 if (meta.zero_3 != 0) {
35 LOG_WARNING(Service_FS, 35 LOG_WARNING(Service_FS,
36 "Possibly incorrect SaveDataDescriptor, type is " 36 "Possibly incorrect SaveDataAttribute, type is "
37 "SystemSaveData||SaveData but offset 0x38 is non-zero ({:016X}).", 37 "SystemSaveData||SaveData but offset 0x38 is non-zero ({:016X}).",
38 meta.zero_3); 38 meta.zero_3);
39 } 39 }
@@ -41,33 +41,32 @@ void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
41 41
42 if (meta.type == SaveDataType::SystemSaveData && meta.title_id != 0) { 42 if (meta.type == SaveDataType::SystemSaveData && meta.title_id != 0) {
43 LOG_WARNING(Service_FS, 43 LOG_WARNING(Service_FS,
44 "Possibly incorrect SaveDataDescriptor, type is SystemSaveData but title_id is " 44 "Possibly incorrect SaveDataAttribute, type is SystemSaveData but title_id is "
45 "non-zero ({:016X}).", 45 "non-zero ({:016X}).",
46 meta.title_id); 46 meta.title_id);
47 } 47 }
48 48
49 if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) { 49 if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) {
50 LOG_WARNING(Service_FS, 50 LOG_WARNING(Service_FS,
51 "Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is " 51 "Possibly incorrect SaveDataAttribute, type is DeviceSaveData but user_id is "
52 "non-zero ({:016X}{:016X})", 52 "non-zero ({:016X}{:016X})",
53 meta.user_id[1], meta.user_id[0]); 53 meta.user_id[1], meta.user_id[0]);
54 } 54 }
55} 55}
56 56
57bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataDescriptor& desc) { 57bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataAttribute& attr) {
58 return desc.type == SaveDataType::CacheStorage || desc.type == SaveDataType::TemporaryStorage || 58 return attr.type == SaveDataType::CacheStorage || attr.type == SaveDataType::TemporaryStorage ||
59 (space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User 59 (space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User
60 (desc.type == SaveDataType::SaveData || desc.type == SaveDataType::DeviceSaveData) && 60 (attr.type == SaveDataType::SaveData || attr.type == SaveDataType::DeviceSaveData) &&
61 desc.title_id == 0 && desc.save_id == 0); 61 attr.title_id == 0 && attr.save_id == 0);
62} 62}
63 63
64} // Anonymous namespace 64} // Anonymous namespace
65 65
66std::string SaveDataDescriptor::DebugInfo() const { 66std::string SaveDataAttribute::DebugInfo() const {
67 return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, " 67 return fmt::format("[title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, type={:02X}, "
68 "save_id={:016X}, "
69 "rank={}, index={}]", 68 "rank={}, index={}]",
70 static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id, 69 title_id, user_id[1], user_id[0], save_id, static_cast<u8>(type),
71 static_cast<u8>(rank), index); 70 static_cast<u8>(rank), index);
72} 71}
73 72
@@ -80,8 +79,8 @@ SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save
80SaveDataFactory::~SaveDataFactory() = default; 79SaveDataFactory::~SaveDataFactory() = default;
81 80
82ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space, 81ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
83 const SaveDataDescriptor& meta) const { 82 const SaveDataAttribute& meta) const {
84 PrintSaveDataDescriptorWarnings(meta); 83 PrintSaveDataAttributeWarnings(meta);
85 84
86 const auto save_directory = 85 const auto save_directory =
87 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); 86 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
@@ -98,7 +97,7 @@ ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
98} 97}
99 98
100ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, 99ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
101 const SaveDataDescriptor& meta) const { 100 const SaveDataAttribute& meta) const {
102 101
103 const auto save_directory = 102 const auto save_directory =
104 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); 103 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index 991e57aa1..6625bbbd8 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -21,6 +21,7 @@ enum class SaveDataSpaceId : u8 {
21 TemporaryStorage = 3, 21 TemporaryStorage = 3,
22 SdCardUser = 4, 22 SdCardUser = 4,
23 ProperSystem = 100, 23 ProperSystem = 100,
24 SafeMode = 101,
24}; 25};
25 26
26enum class SaveDataType : u8 { 27enum class SaveDataType : u8 {
@@ -30,28 +31,50 @@ enum class SaveDataType : u8 {
30 DeviceSaveData = 3, 31 DeviceSaveData = 3,
31 TemporaryStorage = 4, 32 TemporaryStorage = 4,
32 CacheStorage = 5, 33 CacheStorage = 5,
34 SystemBcat = 6,
33}; 35};
34 36
35enum class SaveDataRank : u8 { 37enum class SaveDataRank : u8 {
36 Primary, 38 Primary = 0,
37 Secondary, 39 Secondary = 1,
38}; 40};
39 41
40struct SaveDataDescriptor { 42enum class SaveDataFlags : u32 {
41 u64_le title_id; 43 None = (0 << 0),
44 KeepAfterResettingSystemSaveData = (1 << 0),
45 KeepAfterRefurbishment = (1 << 1),
46 KeepAfterResettingSystemSaveDataWithoutUserSaveData = (1 << 2),
47 NeedsSecureDelete = (1 << 3),
48};
49
50struct SaveDataAttribute {
51 u64 title_id;
42 u128 user_id; 52 u128 user_id;
43 u64_le save_id; 53 u64 save_id;
44 SaveDataType type; 54 SaveDataType type;
45 SaveDataRank rank; 55 SaveDataRank rank;
46 u16_le index; 56 u16 index;
47 INSERT_PADDING_BYTES(4); 57 INSERT_PADDING_BYTES(4);
48 u64_le zero_1; 58 u64 zero_1;
49 u64_le zero_2; 59 u64 zero_2;
50 u64_le zero_3; 60 u64 zero_3;
51 61
52 std::string DebugInfo() const; 62 std::string DebugInfo() const;
53}; 63};
54static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size."); 64static_assert(sizeof(SaveDataAttribute) == 0x40, "SaveDataAttribute has incorrect size.");
65
66struct SaveDataExtraData {
67 SaveDataAttribute attr;
68 u64 owner_id;
69 s64 timestamp;
70 SaveDataFlags flags;
71 INSERT_PADDING_BYTES(4);
72 s64 available_size;
73 s64 journal_size;
74 s64 commit_id;
75 std::array<u8, 0x190> unused;
76};
77static_assert(sizeof(SaveDataExtraData) == 0x200, "SaveDataExtraData has incorrect size.");
55 78
56struct SaveDataSize { 79struct SaveDataSize {
57 u64 normal; 80 u64 normal;
@@ -64,8 +87,8 @@ public:
64 explicit SaveDataFactory(VirtualDir dir); 87 explicit SaveDataFactory(VirtualDir dir);
65 ~SaveDataFactory(); 88 ~SaveDataFactory();
66 89
67 ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const; 90 ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
68 ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const; 91 ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
69 92
70 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; 93 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
71 94
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index 6f732e4d8..cb56d8f2d 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -5,8 +5,8 @@
5#include <memory> 5#include <memory>
6#include "core/file_sys/registered_cache.h" 6#include "core/file_sys/registered_cache.h"
7#include "core/file_sys/sdmc_factory.h" 7#include "core/file_sys/sdmc_factory.h"
8#include "core/file_sys/vfs.h"
8#include "core/file_sys/xts_archive.h" 9#include "core/file_sys/xts_archive.h"
9#include "core/settings.h"
10 10
11namespace FileSys { 11namespace FileSys {
12 12
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index 42dc4e08a..2bb92ba93 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -5,7 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include "core/file_sys/vfs.h" 8#include "core/file_sys/vfs_types.h"
9#include "core/hle/result.h" 9#include "core/hle/result.h"
10 10
11namespace FileSys { 11namespace FileSys {
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index a6637fa39..90641d23b 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -22,7 +22,7 @@ namespace FileSys {
22 22
23NSP::NSP(VirtualFile file_) 23NSP::NSP(VirtualFile file_)
24 : file(std::move(file_)), status{Loader::ResultStatus::Success}, 24 : file(std::move(file_)), status{Loader::ResultStatus::Success},
25 pfs(std::make_shared<PartitionFilesystem>(file)) { 25 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} {
26 if (pfs->GetStatus() != Loader::ResultStatus::Success) { 26 if (pfs->GetStatus() != Loader::ResultStatus::Success) {
27 status = pfs->GetStatus(); 27 status = pfs->GetStatus();
28 return; 28 return;
@@ -264,9 +264,9 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
264 } 264 }
265 265
266 const CNMT cnmt(inner_file); 266 const CNMT cnmt(inner_file);
267 auto& ncas_title = ncas[cnmt.GetTitleID()];
268 267
269 ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca; 268 ncas[cnmt.GetTitleID()][{cnmt.GetType(), ContentRecordType::Meta}] = nca;
269
270 for (const auto& rec : cnmt.GetContentRecords()) { 270 for (const auto& rec : cnmt.GetContentRecords()) {
271 const auto id_string = Common::HexToString(rec.nca_id, false); 271 const auto id_string = Common::HexToString(rec.nca_id, false);
272 auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); 272 auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
@@ -283,13 +283,32 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
283 } 283 }
284 284
285 auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0); 285 auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0);
286
286 if (next_nca->GetType() == NCAContentType::Program) { 287 if (next_nca->GetType() == NCAContentType::Program) {
287 program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); 288 program_status[next_nca->GetTitleId()] = next_nca->GetStatus();
288 } 289 }
289 if (next_nca->GetStatus() == Loader::ResultStatus::Success || 290
290 (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && 291 if (next_nca->GetStatus() != Loader::ResultStatus::Success &&
291 (cnmt.GetTitleID() & 0x800) != 0)) { 292 next_nca->GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
292 ncas_title[{cnmt.GetType(), rec.type}] = std::move(next_nca); 293 continue;
294 }
295
296 // If the last 3 hexadecimal digits of the CNMT TitleID is 0x800 or is missing the
297 // BKTRBaseRomFS, this is an update NCA. Otherwise, this is a base NCA.
298 if ((cnmt.GetTitleID() & 0x800) != 0 ||
299 next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
300 // If the last 3 hexadecimal digits of the NCA's TitleID is between 0x1 and
301 // 0x7FF, this is a multi-program update NCA. Otherwise, this is a regular
302 // update NCA.
303 if ((next_nca->GetTitleId() & 0x7FF) != 0 &&
304 (next_nca->GetTitleId() & 0x800) == 0) {
305 ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] =
306 std::move(next_nca);
307 } else {
308 ncas[cnmt.GetTitleID()][{cnmt.GetType(), rec.type}] = std::move(next_nca);
309 }
310 } else {
311 ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] = std::move(next_nca);
293 } 312 }
294 } 313 }
295 314
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index 6d54bd807..c70a11b5b 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -10,6 +10,10 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs.h"
12 12
13namespace Core::Crypto {
14class KeyManager;
15}
16
13namespace Loader { 17namespace Loader {
14enum class ResultStatus : u16; 18enum class ResultStatus : u16;
15} 19}
@@ -74,7 +78,7 @@ private:
74 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; 78 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;
75 std::vector<VirtualFile> ticket_files; 79 std::vector<VirtualFile> ticket_files;
76 80
77 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); 81 Core::Crypto::KeyManager& keys;
78 82
79 VirtualFile romfs; 83 VirtualFile romfs;
80 VirtualDir exefs; 84 VirtualDir exefs;
diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp
index 61bb67945..d65c7d234 100644
--- a/src/core/file_sys/system_archive/mii_model.cpp
+++ b/src/core/file_sys/system_archive/mii_model.cpp
@@ -27,18 +27,12 @@ VirtualDir MiiModel() {
27 auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, 27 auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{},
28 std::vector<VirtualDir>{}, "data"); 28 std::vector<VirtualDir>{}, "data");
29 29
30 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_LINEAR.size()>>( 30 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat"));
31 MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat")); 31 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat"));
32 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_SRGB.size()>>( 32 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat"));
33 MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat")); 33 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat"));
34 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_LINEAR.size()>>( 34 out->AddFile(MakeArrayFile(MiiModelData::SHAPE_HIGH, "ShapeHigh.dat"));
35 MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat")); 35 out->AddFile(MakeArrayFile(MiiModelData::SHAPE_MID, "ShapeMid.dat"));
36 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_SRGB.size()>>(
37 MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat"));
38 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_HIGH.size()>>(
39 MiiModelData::SHAPE_HIGH, "ShapeHigh.dat"));
40 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>(
41 MiiModelData::SHAPE_MID, "ShapeMid.dat"));
42 36
43 return out; 37 return out;
44} 38}
diff --git a/src/core/file_sys/system_archive/ng_word.cpp b/src/core/file_sys/system_archive/ng_word.cpp
index f4443784d..100d3c5db 100644
--- a/src/core/file_sys/system_archive/ng_word.cpp
+++ b/src/core/file_sys/system_archive/ng_word.cpp
@@ -24,19 +24,18 @@ constexpr std::array<u8, 30> WORD_TXT{
24} // namespace NgWord1Data 24} // namespace NgWord1Data
25 25
26VirtualDir NgWord1() { 26VirtualDir NgWord1() {
27 std::vector<VirtualFile> files(NgWord1Data::NUMBER_WORD_TXT_FILES); 27 std::vector<VirtualFile> files;
28 files.reserve(NgWord1Data::NUMBER_WORD_TXT_FILES);
28 29
29 for (std::size_t i = 0; i < files.size(); ++i) { 30 for (std::size_t i = 0; i < files.size(); ++i) {
30 files[i] = std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( 31 files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, fmt::format("{}.txt", i)));
31 NgWord1Data::WORD_TXT, fmt::format("{}.txt", i));
32 } 32 }
33 33
34 files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( 34 files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, "common.txt"));
35 NgWord1Data::WORD_TXT, "common.txt")); 35 files.push_back(MakeArrayFile(NgWord1Data::VERSION_DAT, "version.dat"));
36 files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::VERSION_DAT.size()>>(
37 NgWord1Data::VERSION_DAT, "version.dat"));
38 36
39 return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); 37 return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{},
38 "data");
40} 39}
41 40
42namespace NgWord2Data { 41namespace NgWord2Data {
@@ -55,27 +54,22 @@ constexpr std::array<u8, 0x2C> AC_NX_DATA{
55} // namespace NgWord2Data 54} // namespace NgWord2Data
56 55
57VirtualDir NgWord2() { 56VirtualDir NgWord2() {
58 std::vector<VirtualFile> files(NgWord2Data::NUMBER_AC_NX_FILES * 3); 57 std::vector<VirtualFile> files;
58 files.reserve(NgWord2Data::NUMBER_AC_NX_FILES * 3);
59 59
60 for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) { 60 for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) {
61 files[3 * i] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 61 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i)));
62 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i)); 62 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i)));
63 files[3 * i + 1] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 63 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i)));
64 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i));
65 files[3 * i + 2] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
66 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i));
67 } 64 }
68 65
69 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 66 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b1_nx"));
70 NgWord2Data::AC_NX_DATA, "ac_common_b1_nx")); 67 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b2_nx"));
71 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 68 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx"));
72 NgWord2Data::AC_NX_DATA, "ac_common_b2_nx")); 69 files.push_back(MakeArrayFile(NgWord2Data::VERSION_DAT, "version.dat"));
73 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
74 NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx"));
75 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::VERSION_DAT.size()>>(
76 NgWord2Data::VERSION_DAT, "version.dat"));
77 70
78 return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); 71 return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{},
72 "data");
79} 73}
80 74
81} // namespace FileSys::SystemArchive 75} // namespace FileSys::SystemArchive
diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp
index 9806bd197..8fd005012 100644
--- a/src/core/file_sys/system_archive/time_zone_binary.cpp
+++ b/src/core/file_sys/system_archive/time_zone_binary.cpp
@@ -2,6 +2,9 @@
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 <array>
6#include <vector>
7
5#include "common/swap.h" 8#include "common/swap.h"
6#include "core/file_sys/system_archive/time_zone_binary.h" 9#include "core/file_sys/system_archive/time_zone_binary.h"
7#include "core/file_sys/vfs_vector.h" 10#include "core/file_sys/vfs_vector.h"
@@ -615,43 +618,49 @@ static constexpr std::array<u8, 9633> LOCATION_NAMES{
615 0x0a}; 618 0x0a};
616 619
617static VirtualFile GenerateDefaultTimeZoneFile() { 620static VirtualFile GenerateDefaultTimeZoneFile() {
618 struct { 621 struct TimeZoneInfo {
619 s64_be at; 622 s64_be at;
620 INSERT_PADDING_BYTES(7); 623 std::array<u8, 7> padding1;
621 std::array<char, 4> time_zone_chars; 624 std::array<char, 4> time_zone_chars;
622 INSERT_PADDING_BYTES(2); 625 std::array<u8, 2> padding2;
623 std::array<char, 6> time_zone_name; 626 std::array<char, 6> time_zone_name;
624 } time_zone_info{}; 627 };
625 628
626 const VirtualFile file{std::make_shared<VectorVfsFile>( 629 VirtualFile file{std::make_shared<VectorVfsFile>(
627 std::vector<u8>(sizeof(Service::Time::TimeZone::TzifHeader) + sizeof(time_zone_info)), 630 std::vector<u8>(sizeof(Service::Time::TimeZone::TzifHeader) + sizeof(TimeZoneInfo)),
628 "GMT")}; 631 "GMT")};
629 632
630 Service::Time::TimeZone::TzifHeader header{}; 633 const Service::Time::TimeZone::TzifHeader header{
631 header.magic = 0x545a6966; 634 .magic = 0x545a6966,
632 header.version = 0x32; 635 .version = 0x32,
633 header.ttis_gmt_count = 0x1; 636 .ttis_gmt_count = 1,
634 header.ttis_std_count = 0x1; 637 .ttis_std_count = 1,
635 header.time_count = 0x1; 638 .time_count = 1,
636 header.type_count = 0x1; 639 .type_count = 1,
637 header.char_count = 0x4; 640 .char_count = 4,
641 };
638 file->WriteObject(header, 0); 642 file->WriteObject(header, 0);
639 643
640 time_zone_info.at = 0xf8; 644 const TimeZoneInfo time_zone_info{
641 time_zone_info.time_zone_chars = {'G', 'M', 'T', '\0'}; 645 .at = 0xf8,
642 time_zone_info.time_zone_name = {'\n', 'G', 'M', 'T', '0', '\n'}; 646 .padding1 = {},
647 .time_zone_chars = {'G', 'M', 'T', '\0'},
648 .padding2 = {},
649 .time_zone_name = {'\n', 'G', 'M', 'T', '0', '\n'},
650 };
643 file->WriteObject(time_zone_info, sizeof(Service::Time::TimeZone::TzifHeader)); 651 file->WriteObject(time_zone_info, sizeof(Service::Time::TimeZone::TzifHeader));
644 652
645 return file; 653 return file;
646} 654}
647 655
648VirtualDir TimeZoneBinary() { 656VirtualDir TimeZoneBinary() {
649 const std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>( 657 std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>(
650 std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{}, 658 std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{},
651 "zoneinfo")}; 659 "zoneinfo")};
652 const std::vector<VirtualFile> root_files{ 660 std::vector<VirtualFile> root_files{MakeArrayFile(LOCATION_NAMES, "binaryList.txt")};
653 std::make_shared<ArrayVfsFile<LOCATION_NAMES.size()>>(LOCATION_NAMES, "binaryList.txt")}; 661
654 return std::make_shared<VectorVfsDirectory>(root_files, root_dirs, "data"); 662 return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(root_dirs),
663 "data");
655} 664}
656 665
657} // namespace FileSys::SystemArchive 666} // namespace FileSys::SystemArchive
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index e33327ef0..b2f026b6d 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -30,7 +30,7 @@ bool VfsFilesystem::IsWritable() const {
30} 30}
31 31
32VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { 32VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
33 const auto path = FileUtil::SanitizePath(path_); 33 const auto path = Common::FS::SanitizePath(path_);
34 if (root->GetFileRelative(path) != nullptr) 34 if (root->GetFileRelative(path) != nullptr)
35 return VfsEntryType::File; 35 return VfsEntryType::File;
36 if (root->GetDirectoryRelative(path) != nullptr) 36 if (root->GetDirectoryRelative(path) != nullptr)
@@ -40,22 +40,22 @@ VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
40} 40}
41 41
42VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { 42VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
43 const auto path = FileUtil::SanitizePath(path_); 43 const auto path = Common::FS::SanitizePath(path_);
44 return root->GetFileRelative(path); 44 return root->GetFileRelative(path);
45} 45}
46 46
47VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { 47VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
48 const auto path = FileUtil::SanitizePath(path_); 48 const auto path = Common::FS::SanitizePath(path_);
49 return root->CreateFileRelative(path); 49 return root->CreateFileRelative(path);
50} 50}
51 51
52VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { 52VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
53 const auto old_path = FileUtil::SanitizePath(old_path_); 53 const auto old_path = Common::FS::SanitizePath(old_path_);
54 const auto new_path = FileUtil::SanitizePath(new_path_); 54 const auto new_path = Common::FS::SanitizePath(new_path_);
55 55
56 // VfsDirectory impls are only required to implement copy across the current directory. 56 // VfsDirectory impls are only required to implement copy across the current directory.
57 if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) { 57 if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) {
58 if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path))) 58 if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path)))
59 return nullptr; 59 return nullptr;
60 return OpenFile(new_path, Mode::ReadWrite); 60 return OpenFile(new_path, Mode::ReadWrite);
61 } 61 }
@@ -76,8 +76,8 @@ VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view
76} 76}
77 77
78VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) { 78VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) {
79 const auto sanitized_old_path = FileUtil::SanitizePath(old_path); 79 const auto sanitized_old_path = Common::FS::SanitizePath(old_path);
80 const auto sanitized_new_path = FileUtil::SanitizePath(new_path); 80 const auto sanitized_new_path = Common::FS::SanitizePath(new_path);
81 81
82 // Again, non-default impls are highly encouraged to provide a more optimized version of this. 82 // Again, non-default impls are highly encouraged to provide a more optimized version of this.
83 auto out = CopyFile(sanitized_old_path, sanitized_new_path); 83 auto out = CopyFile(sanitized_old_path, sanitized_new_path);
@@ -89,26 +89,26 @@ VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view
89} 89}
90 90
91bool VfsFilesystem::DeleteFile(std::string_view path_) { 91bool VfsFilesystem::DeleteFile(std::string_view path_) {
92 const auto path = FileUtil::SanitizePath(path_); 92 const auto path = Common::FS::SanitizePath(path_);
93 auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); 93 auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write);
94 if (parent == nullptr) 94 if (parent == nullptr)
95 return false; 95 return false;
96 return parent->DeleteFile(FileUtil::GetFilename(path)); 96 return parent->DeleteFile(Common::FS::GetFilename(path));
97} 97}
98 98
99VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { 99VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
100 const auto path = FileUtil::SanitizePath(path_); 100 const auto path = Common::FS::SanitizePath(path_);
101 return root->GetDirectoryRelative(path); 101 return root->GetDirectoryRelative(path);
102} 102}
103 103
104VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { 104VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
105 const auto path = FileUtil::SanitizePath(path_); 105 const auto path = Common::FS::SanitizePath(path_);
106 return root->CreateDirectoryRelative(path); 106 return root->CreateDirectoryRelative(path);
107} 107}
108 108
109VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { 109VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) {
110 const auto old_path = FileUtil::SanitizePath(old_path_); 110 const auto old_path = Common::FS::SanitizePath(old_path_);
111 const auto new_path = FileUtil::SanitizePath(new_path_); 111 const auto new_path = Common::FS::SanitizePath(new_path_);
112 112
113 // Non-default impls are highly encouraged to provide a more optimized version of this. 113 // Non-default impls are highly encouraged to provide a more optimized version of this.
114 auto old_dir = OpenDirectory(old_path, Mode::Read); 114 auto old_dir = OpenDirectory(old_path, Mode::Read);
@@ -139,8 +139,8 @@ VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_
139} 139}
140 140
141VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) { 141VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) {
142 const auto sanitized_old_path = FileUtil::SanitizePath(old_path); 142 const auto sanitized_old_path = Common::FS::SanitizePath(old_path);
143 const auto sanitized_new_path = FileUtil::SanitizePath(new_path); 143 const auto sanitized_new_path = Common::FS::SanitizePath(new_path);
144 144
145 // Non-default impls are highly encouraged to provide a more optimized version of this. 145 // Non-default impls are highly encouraged to provide a more optimized version of this.
146 auto out = CopyDirectory(sanitized_old_path, sanitized_new_path); 146 auto out = CopyDirectory(sanitized_old_path, sanitized_new_path);
@@ -152,28 +152,29 @@ VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_v
152} 152}
153 153
154bool VfsFilesystem::DeleteDirectory(std::string_view path_) { 154bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
155 const auto path = FileUtil::SanitizePath(path_); 155 const auto path = Common::FS::SanitizePath(path_);
156 auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); 156 auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write);
157 if (parent == nullptr) 157 if (parent == nullptr)
158 return false; 158 return false;
159 return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path)); 159 return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path));
160} 160}
161 161
162VfsFile::~VfsFile() = default; 162VfsFile::~VfsFile() = default;
163 163
164std::string VfsFile::GetExtension() const { 164std::string VfsFile::GetExtension() const {
165 return std::string(FileUtil::GetExtensionFromFilename(GetName())); 165 return std::string(Common::FS::GetExtensionFromFilename(GetName()));
166} 166}
167 167
168VfsDirectory::~VfsDirectory() = default; 168VfsDirectory::~VfsDirectory() = default;
169 169
170std::optional<u8> VfsFile::ReadByte(std::size_t offset) const { 170std::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
171 u8 out{}; 171 u8 out{};
172 std::size_t size = Read(&out, 1, offset); 172 const std::size_t size = Read(&out, sizeof(u8), offset);
173 if (size == 1) 173 if (size == 1) {
174 return out; 174 return out;
175 }
175 176
176 return {}; 177 return std::nullopt;
177} 178}
178 179
179std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { 180std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
@@ -203,7 +204,7 @@ std::string VfsFile::GetFullPath() const {
203} 204}
204 205
205std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const { 206std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const {
206 auto vec = FileUtil::SplitPathComponents(path); 207 auto vec = Common::FS::SplitPathComponents(path);
207 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 208 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
208 vec.end()); 209 vec.end());
209 if (vec.empty()) { 210 if (vec.empty()) {
@@ -239,7 +240,7 @@ std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(std::string_view path) co
239} 240}
240 241
241std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const { 242std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const {
242 auto vec = FileUtil::SplitPathComponents(path); 243 auto vec = Common::FS::SplitPathComponents(path);
243 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 244 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
244 vec.end()); 245 vec.end());
245 if (vec.empty()) { 246 if (vec.empty()) {
@@ -301,7 +302,7 @@ std::size_t VfsDirectory::GetSize() const {
301} 302}
302 303
303std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) { 304std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) {
304 auto vec = FileUtil::SplitPathComponents(path); 305 auto vec = Common::FS::SplitPathComponents(path);
305 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 306 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
306 vec.end()); 307 vec.end());
307 if (vec.empty()) { 308 if (vec.empty()) {
@@ -320,7 +321,7 @@ std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path)
320 } 321 }
321 } 322 }
322 323
323 return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path)); 324 return dir->CreateFileRelative(Common::FS::GetPathWithoutTop(path));
324} 325}
325 326
326std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) { 327std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) {
@@ -332,7 +333,7 @@ std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path)
332} 333}
333 334
334std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) { 335std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) {
335 auto vec = FileUtil::SplitPathComponents(path); 336 auto vec = Common::FS::SplitPathComponents(path);
336 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 337 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
337 vec.end()); 338 vec.end());
338 if (vec.empty()) { 339 if (vec.empty()) {
@@ -351,7 +352,7 @@ std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_
351 } 352 }
352 } 353 }
353 354
354 return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path)); 355 return dir->CreateDirectoryRelative(Common::FS::GetPathWithoutTop(path));
355} 356}
356 357
357std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(std::string_view path) { 358std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(std::string_view path) {
diff --git a/src/core/file_sys/vfs_libzip.cpp b/src/core/file_sys/vfs_libzip.cpp
index d69952940..429d7bc8b 100644
--- a/src/core/file_sys/vfs_libzip.cpp
+++ b/src/core/file_sys/vfs_libzip.cpp
@@ -49,7 +49,7 @@ VirtualDir ExtractZIP(VirtualFile file) {
49 if (zip_fread(file2.get(), buf.data(), buf.size()) != s64(buf.size())) 49 if (zip_fread(file2.get(), buf.data(), buf.size()) != s64(buf.size()))
50 return nullptr; 50 return nullptr;
51 51
52 const auto parts = FileUtil::SplitPathComponents(stat.name); 52 const auto parts = Common::FS::SplitPathComponents(stat.name);
53 const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back()); 53 const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back());
54 54
55 std::shared_ptr<VectorVfsDirectory> dtrv = out; 55 std::shared_ptr<VectorVfsDirectory> dtrv = out;
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index c96f88488..7714d3de5 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -58,10 +58,11 @@ std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t
58} 58}
59 59
60std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { 60std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
61 if (r_offset < size) 61 if (r_offset >= size) {
62 return file->ReadByte(offset + r_offset); 62 return std::nullopt;
63 }
63 64
64 return {}; 65 return file->ReadByte(offset + r_offset);
65} 66}
66 67
67std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { 68std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const {
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 96ce5957c..488687ba9 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -14,24 +14,28 @@
14 14
15namespace FileSys { 15namespace FileSys {
16 16
17namespace FS = Common::FS;
18
17static std::string ModeFlagsToString(Mode mode) { 19static std::string ModeFlagsToString(Mode mode) {
18 std::string mode_str; 20 std::string mode_str;
19 21
20 // Calculate the correct open mode for the file. 22 // Calculate the correct open mode for the file.
21 if (mode & Mode::Read && mode & Mode::Write) { 23 if (True(mode & Mode::Read) && True(mode & Mode::Write)) {
22 if (mode & Mode::Append) 24 if (True(mode & Mode::Append)) {
23 mode_str = "a+"; 25 mode_str = "a+";
24 else 26 } else {
25 mode_str = "r+"; 27 mode_str = "r+";
28 }
26 } else { 29 } else {
27 if (mode & Mode::Read) 30 if (True(mode & Mode::Read)) {
28 mode_str = "r"; 31 mode_str = "r";
29 else if (mode & Mode::Append) 32 } else if (True(mode & Mode::Append)) {
30 mode_str = "a"; 33 mode_str = "a";
31 else if (mode & Mode::Write) 34 } else if (True(mode & Mode::Write)) {
32 mode_str = "w"; 35 mode_str = "w";
33 else 36 } else {
34 UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode)); 37 UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode));
38 }
35 } 39 }
36 40
37 mode_str += "b"; 41 mode_str += "b";
@@ -55,78 +59,82 @@ bool RealVfsFilesystem::IsWritable() const {
55} 59}
56 60
57VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { 61VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
58 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 62 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
59 if (!FileUtil::Exists(path)) 63 if (!FS::Exists(path)) {
60 return VfsEntryType::None; 64 return VfsEntryType::None;
61 if (FileUtil::IsDirectory(path)) 65 }
66 if (FS::IsDirectory(path)) {
62 return VfsEntryType::Directory; 67 return VfsEntryType::Directory;
68 }
63 69
64 return VfsEntryType::File; 70 return VfsEntryType::File;
65} 71}
66 72
67VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { 73VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
68 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 74 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
69 if (cache.find(path) != cache.end()) { 75
70 auto weak = cache[path]; 76 if (const auto weak_iter = cache.find(path); weak_iter != cache.cend()) {
77 const auto& weak = weak_iter->second;
78
71 if (!weak.expired()) { 79 if (!weak.expired()) {
72 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms)); 80 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms));
73 } 81 }
74 } 82 }
75 83
76 if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0) 84 if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) {
77 FileUtil::CreateEmptyFile(path); 85 FS::CreateEmptyFile(path);
86 }
78 87
79 auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str()); 88 auto backing = std::make_shared<FS::IOFile>(path, ModeFlagsToString(perms).c_str());
80 cache[path] = backing; 89 cache.insert_or_assign(path, backing);
81 90
82 // Cannot use make_shared as RealVfsFile constructor is private 91 // Cannot use make_shared as RealVfsFile constructor is private
83 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); 92 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms));
84} 93}
85 94
86VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { 95VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
87 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 96 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
88 const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash); 97 const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash);
89 if (!FileUtil::Exists(path)) { 98 if (!FS::Exists(path)) {
90 FileUtil::CreateFullPath(path_fwd); 99 FS::CreateFullPath(path_fwd);
91 if (!FileUtil::CreateEmptyFile(path)) 100 if (!FS::CreateEmptyFile(path)) {
92 return nullptr; 101 return nullptr;
102 }
93 } 103 }
94 return OpenFile(path, perms); 104 return OpenFile(path, perms);
95} 105}
96 106
97VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { 107VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
98 const auto old_path = 108 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
99 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 109 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
100 const auto new_path =
101 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
102 110
103 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || 111 if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
104 FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path)) 112 !FS::Copy(old_path, new_path)) {
105 return nullptr; 113 return nullptr;
114 }
106 return OpenFile(new_path, Mode::ReadWrite); 115 return OpenFile(new_path, Mode::ReadWrite);
107} 116}
108 117
109VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { 118VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
110 const auto old_path = 119 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
111 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 120 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
112 const auto new_path = 121 const auto cached_file_iter = cache.find(old_path);
113 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
114 122
115 if (cache.find(old_path) != cache.end()) { 123 if (cached_file_iter != cache.cend()) {
116 auto file = cache[old_path].lock(); 124 auto file = cached_file_iter->second.lock();
117 125
118 if (!cache[old_path].expired()) { 126 if (!cached_file_iter->second.expired()) {
119 file->Close(); 127 file->Close();
120 } 128 }
121 129
122 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || 130 if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
123 FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) { 131 !FS::Rename(old_path, new_path)) {
124 return nullptr; 132 return nullptr;
125 } 133 }
126 134
127 cache.erase(old_path); 135 cache.erase(old_path);
128 file->Open(new_path, "r+b"); 136 file->Open(new_path, "r+b");
129 cache[new_path] = file; 137 cache.insert_or_assign(new_path, std::move(file));
130 } else { 138 } else {
131 UNREACHABLE(); 139 UNREACHABLE();
132 return nullptr; 140 return nullptr;
@@ -136,28 +144,33 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
136} 144}
137 145
138bool RealVfsFilesystem::DeleteFile(std::string_view path_) { 146bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
139 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 147 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
140 if (cache.find(path) != cache.end()) { 148 const auto cached_iter = cache.find(path);
141 if (!cache[path].expired()) 149
142 cache[path].lock()->Close(); 150 if (cached_iter != cache.cend()) {
151 if (!cached_iter->second.expired()) {
152 cached_iter->second.lock()->Close();
153 }
143 cache.erase(path); 154 cache.erase(path);
144 } 155 }
145 return FileUtil::Delete(path); 156
157 return FS::Delete(path);
146} 158}
147 159
148VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { 160VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
149 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 161 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
150 // Cannot use make_shared as RealVfsDirectory constructor is private 162 // Cannot use make_shared as RealVfsDirectory constructor is private
151 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); 163 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
152} 164}
153 165
154VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { 166VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
155 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 167 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
156 const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash); 168 const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash);
157 if (!FileUtil::Exists(path)) { 169 if (!FS::Exists(path)) {
158 FileUtil::CreateFullPath(path_fwd); 170 FS::CreateFullPath(path_fwd);
159 if (!FileUtil::CreateDir(path)) 171 if (!FS::CreateDir(path)) {
160 return nullptr; 172 return nullptr;
173 }
161 } 174 }
162 // Cannot use make_shared as RealVfsDirectory constructor is private 175 // Cannot use make_shared as RealVfsDirectory constructor is private
163 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); 176 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
@@ -165,67 +178,75 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms
165 178
166VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, 179VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
167 std::string_view new_path_) { 180 std::string_view new_path_) {
168 const auto old_path = 181 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
169 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 182 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
170 const auto new_path = 183 if (!FS::Exists(old_path) || FS::Exists(new_path) || !FS::IsDirectory(old_path)) {
171 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
172 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
173 !FileUtil::IsDirectory(old_path))
174 return nullptr; 184 return nullptr;
175 FileUtil::CopyDir(old_path, new_path); 185 }
186 FS::CopyDir(old_path, new_path);
176 return OpenDirectory(new_path, Mode::ReadWrite); 187 return OpenDirectory(new_path, Mode::ReadWrite);
177} 188}
178 189
179VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, 190VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
180 std::string_view new_path_) { 191 std::string_view new_path_) {
181 const auto old_path = 192 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
182 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 193 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
183 const auto new_path = 194
184 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); 195 if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
185 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || 196 !FS::Rename(old_path, new_path)) {
186 FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
187 return nullptr; 197 return nullptr;
198 }
188 199
189 for (auto& kv : cache) { 200 for (auto& kv : cache) {
190 // Path in cache starts with old_path 201 // If the path in the cache doesn't start with old_path, then bail on this file.
191 if (kv.first.rfind(old_path, 0) == 0) { 202 if (kv.first.rfind(old_path, 0) != 0) {
192 const auto file_old_path = 203 continue;
193 FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault); 204 }
194 const auto file_new_path = 205
195 FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), 206 const auto file_old_path =
196 FileUtil::DirectorySeparator::PlatformDefault); 207 FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault);
197 auto cached = cache[file_old_path]; 208 auto file_new_path = FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
198 if (!cached.expired()) { 209 FS::DirectorySeparator::PlatformDefault);
199 auto file = cached.lock(); 210 const auto& cached = cache[file_old_path];
200 file->Open(file_new_path, "r+b"); 211
201 cache.erase(file_old_path); 212 if (cached.expired()) {
202 cache[file_new_path] = file; 213 continue;
203 }
204 } 214 }
215
216 auto file = cached.lock();
217 file->Open(file_new_path, "r+b");
218 cache.erase(file_old_path);
219 cache.insert_or_assign(std::move(file_new_path), std::move(file));
205 } 220 }
206 221
207 return OpenDirectory(new_path, Mode::ReadWrite); 222 return OpenDirectory(new_path, Mode::ReadWrite);
208} 223}
209 224
210bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { 225bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
211 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 226 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
227
212 for (auto& kv : cache) { 228 for (auto& kv : cache) {
213 // Path in cache starts with old_path 229 // If the path in the cache doesn't start with path, then bail on this file.
214 if (kv.first.rfind(path, 0) == 0) { 230 if (kv.first.rfind(path, 0) != 0) {
215 if (!cache[kv.first].expired()) 231 continue;
216 cache[kv.first].lock()->Close(); 232 }
217 cache.erase(kv.first); 233
234 const auto& entry = cache[kv.first];
235 if (!entry.expired()) {
236 entry.lock()->Close();
218 } 237 }
238
239 cache.erase(kv.first);
219 } 240 }
220 return FileUtil::DeleteDirRecursively(path); 241
242 return FS::DeleteDirRecursively(path);
221} 243}
222 244
223RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_, 245RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FS::IOFile> backing_,
224 const std::string& path_, Mode perms_) 246 const std::string& path_, Mode perms_)
225 : base(base_), backing(std::move(backing_)), path(path_), 247 : base(base_), backing(std::move(backing_)), path(path_), parent_path(FS::GetParentPath(path_)),
226 parent_path(FileUtil::GetParentPath(path_)), 248 path_components(FS::SplitPathComponents(path_)),
227 path_components(FileUtil::SplitPathComponents(path_)), 249 parent_components(FS::SliceVector(path_components, 0, path_components.size() - 1)),
228 parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
229 perms(perms_) {} 250 perms(perms_) {}
230 251
231RealVfsFile::~RealVfsFile() = default; 252RealVfsFile::~RealVfsFile() = default;
@@ -247,22 +268,24 @@ std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
247} 268}
248 269
249bool RealVfsFile::IsWritable() const { 270bool RealVfsFile::IsWritable() const {
250 return (perms & Mode::WriteAppend) != 0; 271 return True(perms & Mode::WriteAppend);
251} 272}
252 273
253bool RealVfsFile::IsReadable() const { 274bool RealVfsFile::IsReadable() const {
254 return (perms & Mode::ReadWrite) != 0; 275 return True(perms & Mode::ReadWrite);
255} 276}
256 277
257std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { 278std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
258 if (!backing->Seek(offset, SEEK_SET)) 279 if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) {
259 return 0; 280 return 0;
281 }
260 return backing->ReadBytes(data, length); 282 return backing->ReadBytes(data, length);
261} 283}
262 284
263std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { 285std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
264 if (!backing->Seek(offset, SEEK_SET)) 286 if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) {
265 return 0; 287 return 0;
288 }
266 return backing->WriteBytes(data, length); 289 return backing->WriteBytes(data, length);
267} 290}
268 291
@@ -279,16 +302,18 @@ bool RealVfsFile::Close() {
279 302
280template <> 303template <>
281std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const { 304std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const {
282 if (perms == Mode::Append) 305 if (perms == Mode::Append) {
283 return {}; 306 return {};
307 }
284 308
285 std::vector<VirtualFile> out; 309 std::vector<VirtualFile> out;
286 FileUtil::ForeachDirectoryEntry( 310 FS::ForeachDirectoryEntry(
287 nullptr, path, 311 nullptr, path,
288 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { 312 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
289 const std::string full_path = directory + DIR_SEP + filename; 313 const std::string full_path = directory + DIR_SEP + filename;
290 if (!FileUtil::IsDirectory(full_path)) 314 if (!FS::IsDirectory(full_path)) {
291 out.emplace_back(base.OpenFile(full_path, perms)); 315 out.emplace_back(base.OpenFile(full_path, perms));
316 }
292 return true; 317 return true;
293 }); 318 });
294 319
@@ -297,16 +322,18 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>(
297 322
298template <> 323template <>
299std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const { 324std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const {
300 if (perms == Mode::Append) 325 if (perms == Mode::Append) {
301 return {}; 326 return {};
327 }
302 328
303 std::vector<VirtualDir> out; 329 std::vector<VirtualDir> out;
304 FileUtil::ForeachDirectoryEntry( 330 FS::ForeachDirectoryEntry(
305 nullptr, path, 331 nullptr, path,
306 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { 332 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
307 const std::string full_path = directory + DIR_SEP + filename; 333 const std::string full_path = directory + DIR_SEP + filename;
308 if (FileUtil::IsDirectory(full_path)) 334 if (FS::IsDirectory(full_path)) {
309 out.emplace_back(base.OpenDirectory(full_path, perms)); 335 out.emplace_back(base.OpenDirectory(full_path, perms));
336 }
310 return true; 337 return true;
311 }); 338 });
312 339
@@ -314,28 +341,30 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi
314} 341}
315 342
316RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) 343RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
317 : base(base_), path(FileUtil::RemoveTrailingSlash(path_)), 344 : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)),
318 parent_path(FileUtil::GetParentPath(path)), 345 path_components(FS::SplitPathComponents(path)),
319 path_components(FileUtil::SplitPathComponents(path)), 346 parent_components(FS::SliceVector(path_components, 0, path_components.size() - 1)),
320 parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
321 perms(perms_) { 347 perms(perms_) {
322 if (!FileUtil::Exists(path) && perms & Mode::WriteAppend) 348 if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) {
323 FileUtil::CreateDir(path); 349 FS::CreateDir(path);
350 }
324} 351}
325 352
326RealVfsDirectory::~RealVfsDirectory() = default; 353RealVfsDirectory::~RealVfsDirectory() = default;
327 354
328std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const { 355std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
329 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 356 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
330 if (!FileUtil::Exists(full_path) || FileUtil::IsDirectory(full_path)) 357 if (!FS::Exists(full_path) || FS::IsDirectory(full_path)) {
331 return nullptr; 358 return nullptr;
359 }
332 return base.OpenFile(full_path, perms); 360 return base.OpenFile(full_path, perms);
333} 361}
334 362
335std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const { 363std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const {
336 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 364 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
337 if (!FileUtil::Exists(full_path) || !FileUtil::IsDirectory(full_path)) 365 if (!FS::Exists(full_path) || !FS::IsDirectory(full_path)) {
338 return nullptr; 366 return nullptr;
367 }
339 return base.OpenDirectory(full_path, perms); 368 return base.OpenDirectory(full_path, perms);
340} 369}
341 370
@@ -348,17 +377,17 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view
348} 377}
349 378
350std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) { 379std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) {
351 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 380 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
352 return base.CreateFile(full_path, perms); 381 return base.CreateFile(full_path, perms);
353} 382}
354 383
355std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) { 384std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) {
356 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 385 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
357 return base.CreateDirectory(full_path, perms); 386 return base.CreateDirectory(full_path, perms);
358} 387}
359 388
360bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { 389bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
361 auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name)); 390 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(name));
362 return base.DeleteDirectory(full_path); 391 return base.DeleteDirectory(full_path);
363} 392}
364 393
@@ -371,11 +400,11 @@ std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories()
371} 400}
372 401
373bool RealVfsDirectory::IsWritable() const { 402bool RealVfsDirectory::IsWritable() const {
374 return (perms & Mode::WriteAppend) != 0; 403 return True(perms & Mode::WriteAppend);
375} 404}
376 405
377bool RealVfsDirectory::IsReadable() const { 406bool RealVfsDirectory::IsReadable() const {
378 return (perms & Mode::ReadWrite) != 0; 407 return True(perms & Mode::ReadWrite);
379} 408}
380 409
381std::string RealVfsDirectory::GetName() const { 410std::string RealVfsDirectory::GetName() const {
@@ -383,8 +412,9 @@ std::string RealVfsDirectory::GetName() const {
383} 412}
384 413
385std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const { 414std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
386 if (path_components.size() <= 1) 415 if (path_components.size() <= 1) {
387 return nullptr; 416 return nullptr;
417 }
388 418
389 return base.OpenDirectory(parent_path, perms); 419 return base.OpenDirectory(parent_path, perms);
390} 420}
@@ -421,16 +451,17 @@ std::string RealVfsDirectory::GetFullPath() const {
421} 451}
422 452
423std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const { 453std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const {
424 if (perms == Mode::Append) 454 if (perms == Mode::Append) {
425 return {}; 455 return {};
456 }
426 457
427 std::map<std::string, VfsEntryType, std::less<>> out; 458 std::map<std::string, VfsEntryType, std::less<>> out;
428 FileUtil::ForeachDirectoryEntry( 459 FS::ForeachDirectoryEntry(
429 nullptr, path, 460 nullptr, path,
430 [&out](u64* entries_out, const std::string& directory, const std::string& filename) { 461 [&out](u64* entries_out, const std::string& directory, const std::string& filename) {
431 const std::string full_path = directory + DIR_SEP + filename; 462 const std::string full_path = directory + DIR_SEP + filename;
432 out.emplace(filename, FileUtil::IsDirectory(full_path) ? VfsEntryType::Directory 463 out.emplace(filename,
433 : VfsEntryType::File); 464 FS::IsDirectory(full_path) ? VfsEntryType::Directory : VfsEntryType::File);
434 return true; 465 return true;
435 }); 466 });
436 467
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index a0a857a31..0b537b22c 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -9,7 +9,7 @@
9#include "core/file_sys/mode.h" 9#include "core/file_sys/mode.h"
10#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs.h"
11 11
12namespace FileUtil { 12namespace Common::FS {
13class IOFile; 13class IOFile;
14} 14}
15 15
@@ -36,7 +36,7 @@ public:
36 bool DeleteDirectory(std::string_view path) override; 36 bool DeleteDirectory(std::string_view path) override;
37 37
38private: 38private:
39 boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache; 39 boost::container::flat_map<std::string, std::weak_ptr<Common::FS::IOFile>> cache;
40}; 40};
41 41
42// An implmentation of VfsFile that represents a file on the user's computer. 42// An implmentation of VfsFile that represents a file on the user's computer.
@@ -58,13 +58,13 @@ public:
58 bool Rename(std::string_view name) override; 58 bool Rename(std::string_view name) override;
59 59
60private: 60private:
61 RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing, 61 RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<Common::FS::IOFile> backing,
62 const std::string& path, Mode perms = Mode::Read); 62 const std::string& path, Mode perms = Mode::Read);
63 63
64 bool Close(); 64 bool Close();
65 65
66 RealVfsFilesystem& base; 66 RealVfsFilesystem& base;
67 std::shared_ptr<FileUtil::IOFile> backing; 67 std::shared_ptr<Common::FS::IOFile> backing;
68 std::string path; 68 std::string path;
69 std::string parent_path; 69 std::string parent_path;
70 std::vector<std::string> path_components; 70 std::vector<std::string> path_components;
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h
index 9f5a90b1b..8b27c30fa 100644
--- a/src/core/file_sys/vfs_static.h
+++ b/src/core/file_sys/vfs_static.h
@@ -54,9 +54,11 @@ public:
54 } 54 }
55 55
56 std::optional<u8> ReadByte(std::size_t offset) const override { 56 std::optional<u8> ReadByte(std::size_t offset) const override {
57 if (offset < size) 57 if (offset >= size) {
58 return value; 58 return std::nullopt;
59 return {}; 59 }
60
61 return value;
60 } 62 }
61 63
62 std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override { 64 std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
index ac36cb2ee..95d3da2f2 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs_vector.h
@@ -4,7 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <cstring> 8#include <cstring>
9#include <memory>
10#include <string>
11#include <vector>
8#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
9 13
10namespace FileSys { 14namespace FileSys {
@@ -13,7 +17,8 @@ namespace FileSys {
13template <std::size_t size> 17template <std::size_t size>
14class ArrayVfsFile : public VfsFile { 18class ArrayVfsFile : public VfsFile {
15public: 19public:
16 ArrayVfsFile(std::array<u8, size> data, std::string name = "", VirtualDir parent = nullptr) 20 explicit ArrayVfsFile(const std::array<u8, size>& data, std::string name = "",
21 VirtualDir parent = nullptr)
17 : data(data), name(std::move(name)), parent(std::move(parent)) {} 22 : data(data), name(std::move(name)), parent(std::move(parent)) {}
18 23
19 std::string GetName() const override { 24 std::string GetName() const override {
@@ -61,6 +66,12 @@ private:
61 VirtualDir parent; 66 VirtualDir parent;
62}; 67};
63 68
69template <std::size_t Size, typename... Args>
70std::shared_ptr<ArrayVfsFile<Size>> MakeArrayFile(const std::array<u8, Size>& data,
71 Args&&... args) {
72 return std::make_shared<ArrayVfsFile<Size>>(data, std::forward<Args>(args)...);
73}
74
64// An implementation of VfsFile that is backed by a vector optionally supplied upon construction 75// An implementation of VfsFile that is backed by a vector optionally supplied upon construction
65class VectorVfsFile : public VfsFile { 76class VectorVfsFile : public VfsFile {
66public: 77public:
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index 86e06ccb9..24c58e7ae 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -15,8 +15,9 @@
15#include "common/hex_util.h" 15#include "common/hex_util.h"
16#include "common/string_util.h" 16#include "common/string_util.h"
17#include "core/crypto/aes_util.h" 17#include "core/crypto/aes_util.h"
18#include "core/crypto/key_manager.h"
18#include "core/crypto/xts_encryption_layer.h" 19#include "core/crypto/xts_encryption_layer.h"
19#include "core/file_sys/partition_filesystem.h" 20#include "core/file_sys/content_archive.h"
20#include "core/file_sys/vfs_offset.h" 21#include "core/file_sys/vfs_offset.h"
21#include "core/file_sys/xts_archive.h" 22#include "core/file_sys/xts_archive.h"
22#include "core/loader/loader.h" 23#include "core/loader/loader.h"
@@ -43,8 +44,10 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t
43 return true; 44 return true;
44} 45}
45 46
46NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) { 47NAX::NAX(VirtualFile file_)
47 std::string path = FileUtil::SanitizePath(file->GetFullPath()); 48 : header(std::make_unique<NAXHeader>()),
49 file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
50 std::string path = Common::FS::SanitizePath(file->GetFullPath());
48 static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca", 51 static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca",
49 std::regex_constants::ECMAScript | 52 std::regex_constants::ECMAScript |
50 std::regex_constants::icase); 53 std::regex_constants::icase);
@@ -60,7 +63,8 @@ NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::m
60} 63}
61 64
62NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id) 65NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
63 : header(std::make_unique<NAXHeader>()), file(std::move(file_)) { 66 : header(std::make_unique<NAXHeader>()),
67 file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
64 Core::Crypto::SHA256Hash hash{}; 68 Core::Crypto::SHA256Hash hash{};
65 mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0); 69 mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0);
66 status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0], 70 status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
@@ -70,14 +74,18 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
70NAX::~NAX() = default; 74NAX::~NAX() = default;
71 75
72Loader::ResultStatus NAX::Parse(std::string_view path) { 76Loader::ResultStatus NAX::Parse(std::string_view path) {
73 if (file->ReadObject(header.get()) != sizeof(NAXHeader)) 77 if (file == nullptr) {
78 return Loader::ResultStatus::ErrorNullFile;
79 }
80 if (file->ReadObject(header.get()) != sizeof(NAXHeader)) {
74 return Loader::ResultStatus::ErrorBadNAXHeader; 81 return Loader::ResultStatus::ErrorBadNAXHeader;
75 82 }
76 if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) 83 if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) {
77 return Loader::ResultStatus::ErrorBadNAXHeader; 84 return Loader::ResultStatus::ErrorBadNAXHeader;
78 85 }
79 if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) 86 if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) {
80 return Loader::ResultStatus::ErrorIncorrectNAXFileSize; 87 return Loader::ResultStatus::ErrorIncorrectNAXFileSize;
88 }
81 89
82 keys.DeriveSDSeedLazy(); 90 keys.DeriveSDSeedLazy();
83 std::array<Core::Crypto::Key256, 2> sd_keys{}; 91 std::array<Core::Crypto::Key256, 2> sd_keys{};
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h
index 563531bb6..c472e226e 100644
--- a/src/core/file_sys/xts_archive.h
+++ b/src/core/file_sys/xts_archive.h
@@ -9,12 +9,16 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/crypto/key_manager.h" 11#include "core/crypto/key_manager.h"
12#include "core/file_sys/content_archive.h"
13#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
14#include "core/loader/loader.h" 13
14namespace Loader {
15enum class ResultStatus : u16;
16}
15 17
16namespace FileSys { 18namespace FileSys {
17 19
20class NCA;
21
18struct NAXHeader { 22struct NAXHeader {
19 std::array<u8, 0x20> hmac; 23 std::array<u8, 0x20> hmac;
20 u64_le magic; 24 u64_le magic;
@@ -62,6 +66,6 @@ private:
62 66
63 VirtualFile dec_file; 67 VirtualFile dec_file;
64 68
65 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); 69 Core::Crypto::KeyManager& keys;
66}; 70};
67} // namespace FileSys 71} // namespace FileSys
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
new file mode 100644
index 000000000..c5d65f2d0
--- /dev/null
+++ b/src/core/frontend/applets/controller.cpp
@@ -0,0 +1,81 @@
1// Copyright 2020 yuzu Emulator Project
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 "core/frontend/applets/controller.h"
8#include "core/hle/service/hid/controllers/npad.h"
9#include "core/hle/service/hid/hid.h"
10#include "core/hle/service/sm/sm.h"
11
12namespace Core::Frontend {
13
14ControllerApplet::~ControllerApplet() = default;
15
16DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& service_manager_)
17 : service_manager{service_manager_} {}
18
19DefaultControllerApplet::~DefaultControllerApplet() = default;
20
21void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback,
22 ControllerParameters parameters) const {
23 LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
24
25 auto& npad =
26 service_manager.GetService<Service::HID::Hid>("hid")
27 ->GetAppletResource()
28 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
29
30 auto& players = Settings::values.players;
31
32 const std::size_t min_supported_players =
33 parameters.enable_single_mode ? 1 : parameters.min_players;
34
35 // Disconnect Handheld first.
36 npad.DisconnectNPadAtIndex(8);
37
38 // Deduce the best configuration based on the input parameters.
39 for (std::size_t index = 0; index < players.size() - 2; ++index) {
40 // First, disconnect all controllers regardless of the value of keep_controllers_connected.
41 // This makes it easy to connect the desired controllers.
42 npad.DisconnectNPadAtIndex(index);
43
44 // Only connect the minimum number of required players.
45 if (index >= min_supported_players) {
46 continue;
47 }
48
49 // Connect controllers based on the following priority list from highest to lowest priority:
50 // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
51 if (parameters.allow_pro_controller) {
52 npad.AddNewControllerAt(
53 npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index);
54 } else if (parameters.allow_dual_joycons) {
55 npad.AddNewControllerAt(
56 npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index);
57 } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
58 // Assign left joycons to even player indices and right joycons to odd player indices.
59 // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
60 // a right Joycon for Player 2 in 2 Player Assist mode.
61 if (index % 2 == 0) {
62 npad.AddNewControllerAt(
63 npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index);
64 } else {
65 npad.AddNewControllerAt(
66 npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index);
67 }
68 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
69 !Settings::values.use_docked_mode) {
70 // We should *never* reach here under any normal circumstances.
71 npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld),
72 index);
73 } else {
74 UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
75 }
76 }
77
78 callback();
79}
80
81} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h
new file mode 100644
index 000000000..3e49cdbb9
--- /dev/null
+++ b/src/core/frontend/applets/controller.h
@@ -0,0 +1,56 @@
1// Copyright 2020 yuzu Emulator Project
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
9#include "common/common_types.h"
10
11namespace Service::SM {
12class ServiceManager;
13}
14
15namespace Core::Frontend {
16
17using BorderColor = std::array<u8, 4>;
18using ExplainText = std::array<char, 0x81>;
19
20struct ControllerParameters {
21 s8 min_players{};
22 s8 max_players{};
23 bool keep_controllers_connected{};
24 bool enable_single_mode{};
25 bool enable_border_color{};
26 std::vector<BorderColor> border_colors{};
27 bool enable_explain_text{};
28 std::vector<ExplainText> explain_text{};
29 bool allow_pro_controller{};
30 bool allow_handheld{};
31 bool allow_dual_joycons{};
32 bool allow_left_joycon{};
33 bool allow_right_joycon{};
34};
35
36class ControllerApplet {
37public:
38 virtual ~ControllerApplet();
39
40 virtual void ReconfigureControllers(std::function<void()> callback,
41 ControllerParameters parameters) const = 0;
42};
43
44class DefaultControllerApplet final : public ControllerApplet {
45public:
46 explicit DefaultControllerApplet(Service::SM::ServiceManager& service_manager_);
47 ~DefaultControllerApplet() override;
48
49 void ReconfigureControllers(std::function<void()> callback,
50 ControllerParameters parameters) const override;
51
52private:
53 Service::SM::ServiceManager& service_manager;
54};
55
56} // namespace Core::Frontend
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 13aa14934..3e8780243 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -39,7 +39,7 @@ public:
39 39
40 class Scoped { 40 class Scoped {
41 public: 41 public:
42 explicit Scoped(GraphicsContext& context_) : context(context_) { 42 [[nodiscard]] explicit Scoped(GraphicsContext& context_) : context(context_) {
43 context.MakeCurrent(); 43 context.MakeCurrent();
44 } 44 }
45 ~Scoped() { 45 ~Scoped() {
@@ -52,7 +52,7 @@ public:
52 52
53 /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value 53 /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
54 /// ends 54 /// ends
55 Scoped Acquire() { 55 [[nodiscard]] Scoped Acquire() {
56 return Scoped{*this}; 56 return Scoped{*this};
57 } 57 }
58}; 58};
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 91ecc30ab..e2e3bbbb3 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h"
7#include "common/math_util.h" 8#include "common/math_util.h"
8 9
9namespace Layout { 10namespace Layout {
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 2b098b7c6..277b70e53 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -33,6 +33,9 @@ public:
33 virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { 33 virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const {
34 return {}; 34 return {};
35 } 35 }
36 virtual bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const {
37 return {};
38 }
36}; 39};
37 40
38/// An abstract class template for a factory that can create input devices. 41/// An abstract class template for a factory that can create input devices.
@@ -119,11 +122,11 @@ using ButtonDevice = InputDevice<bool>;
119using AnalogDevice = InputDevice<std::tuple<float, float>>; 122using AnalogDevice = InputDevice<std::tuple<float, float>>;
120 123
121/** 124/**
122 * A motion device is an input device that returns a tuple of accelerometer state vector and 125 * A motion status is an object that returns a tuple of accelerometer state vector,
123 * gyroscope state vector. 126 * gyroscope state vector, rotation state vector and orientation state matrix.
124 * 127 *
125 * For both vectors: 128 * For both vectors:
126 * x+ is the same direction as LEFT on D-pad. 129 * x+ is the same direction as RIGHT on D-pad.
127 * y+ is normal to the touch screen, pointing outward. 130 * y+ is normal to the touch screen, pointing outward.
128 * z+ is the same direction as UP on D-pad. 131 * z+ is the same direction as UP on D-pad.
129 * 132 *
@@ -133,8 +136,22 @@ using AnalogDevice = InputDevice<std::tuple<float, float>>;
133 * For gyroscope state vector: 136 * For gyroscope state vector:
134 * Orientation is determined by right-hand rule. 137 * Orientation is determined by right-hand rule.
135 * Units: deg/sec 138 * Units: deg/sec
139 *
140 * For rotation state vector
141 * Units: rotations
142 *
143 * For orientation state matrix
144 * x vector
145 * y vector
146 * z vector
147 */
148using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>,
149 std::array<Common::Vec3f, 3>>;
150
151/**
152 * A motion device is an input device that returns a motion status object
136 */ 153 */
137using MotionDevice = InputDevice<std::tuple<Common::Vec3<float>, Common::Vec3<float>>>; 154using MotionDevice = InputDevice<MotionStatus>;
138 155
139/** 156/**
140 * A touch device is an input device that returns a tuple of two floats and a bool. The floats are 157 * A touch device is an input device that returns a tuple of two floats and a bool. The floats are
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp
index c629d9fa1..645f26e91 100644
--- a/src/core/hardware_interrupt_manager.cpp
+++ b/src/core/hardware_interrupt_manager.cpp
@@ -11,19 +11,20 @@
11namespace Core::Hardware { 11namespace Core::Hardware {
12 12
13InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { 13InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
14 gpu_interrupt_event = Core::Timing::CreateEvent("GPUInterrupt", [this](u64 message, s64) { 14 gpu_interrupt_event = Core::Timing::CreateEvent(
15 auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); 15 "GPUInterrupt", [this](std::uintptr_t message, std::chrono::nanoseconds) {
16 const u32 syncpt = static_cast<u32>(message >> 32); 16 auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
17 const u32 value = static_cast<u32>(message); 17 const u32 syncpt = static_cast<u32>(message >> 32);
18 nvdrv->SignalGPUInterruptSyncpt(syncpt, value); 18 const u32 value = static_cast<u32>(message);
19 }); 19 nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
20 });
20} 21}
21 22
22InterruptManager::~InterruptManager() = default; 23InterruptManager::~InterruptManager() = default;
23 24
24void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { 25void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
25 const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value; 26 const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value;
26 system.CoreTiming().ScheduleEvent(10, gpu_interrupt_event, msg); 27 system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{10}, gpu_interrupt_event, msg);
27} 28}
28 29
29} // namespace Core::Hardware 30} // namespace Core::Hardware
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 0dc6a4a43..1b503331f 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -229,6 +229,8 @@ inline void ResponseBuilder::Push(u32 value) {
229 229
230template <typename T> 230template <typename T>
231void ResponseBuilder::PushRaw(const T& value) { 231void ResponseBuilder::PushRaw(const T& value) {
232 static_assert(std::is_trivially_copyable_v<T>,
233 "It's undefined behavior to use memcpy with non-trivially copyable objects");
232 std::memcpy(cmdbuf + index, &value, sizeof(T)); 234 std::memcpy(cmdbuf + index, &value, sizeof(T));
233 index += (sizeof(T) + 3) / 4; // round up to word length 235 index += (sizeof(T) + 3) / 4; // round up to word length
234} 236}
@@ -384,6 +386,8 @@ inline s32 RequestParser::Pop() {
384 386
385template <typename T> 387template <typename T>
386void RequestParser::PopRaw(T& value) { 388void RequestParser::PopRaw(T& value) {
389 static_assert(std::is_trivially_copyable_v<T>,
390 "It's undefined behavior to use memcpy with non-trivially copyable objects");
387 std::memcpy(&value, cmdbuf + index, sizeof(T)); 391 std::memcpy(&value, cmdbuf + index, sizeof(T));
388 index += (sizeof(T) + 3) / 4; // round up to word length 392 index += (sizeof(T) + 3) / 4; // round up to word length
389} 393}
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index df0debe1b..b882eaa0f 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -81,7 +81,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32
81 do { 81 do {
82 current_value = monitor.ExclusiveRead32(current_core, address); 82 current_value = monitor.ExclusiveRead32(current_core, address);
83 83
84 if (current_value != value) { 84 if (current_value != static_cast<u32>(value)) {
85 return ERR_INVALID_STATE; 85 return ERR_INVALID_STATE;
86 } 86 }
87 current_value++; 87 current_value++;
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index 5ab204b9b..be9eba519 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -48,14 +48,15 @@ ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kern
48} 48}
49 49
50ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, 50ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread,
51 Core::Memory::Memory& memory) { 51 Core::Memory::Memory& memory,
52 Core::Timing::CoreTiming& core_timing) {
52 // Keep ServerSession alive until we're done working with it. 53 // Keep ServerSession alive until we're done working with it.
53 if (!parent->Server()) { 54 if (!parent->Server()) {
54 return ERR_SESSION_CLOSED_BY_REMOTE; 55 return ERR_SESSION_CLOSED_BY_REMOTE;
55 } 56 }
56 57
57 // Signal the server session that new data is available 58 // Signal the server session that new data is available
58 return parent->Server()->HandleSyncRequest(std::move(thread), memory); 59 return parent->Server()->HandleSyncRequest(std::move(thread), memory, core_timing);
59} 60}
60 61
61} // namespace Kernel 62} // namespace Kernel
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index c5f760d7d..e5e0690c2 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -16,6 +16,10 @@ namespace Core::Memory {
16class Memory; 16class Memory;
17} 17}
18 18
19namespace Core::Timing {
20class CoreTiming;
21}
22
19namespace Kernel { 23namespace Kernel {
20 24
21class KernelCore; 25class KernelCore;
@@ -42,7 +46,8 @@ public:
42 return HANDLE_TYPE; 46 return HANDLE_TYPE;
43 } 47 }
44 48
45 ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); 49 ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
50 Core::Timing::CoreTiming& core_timing);
46 51
47 bool ShouldWait(const Thread* thread) const override; 52 bool ShouldWait(const Thread* thread) const override;
48 53
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 9277b5d08..81f85643b 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -293,13 +293,15 @@ std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
293 BufferDescriptorA()[buffer_index].Size()}; 293 BufferDescriptorA()[buffer_index].Size()};
294 294
295 if (is_buffer_a) { 295 if (is_buffer_a) {
296 ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return buffer; }, 296 ASSERT_OR_EXECUTE_MSG(
297 "BufferDescriptorA invalid buffer_index {}", buffer_index); 297 BufferDescriptorA().size() > buffer_index, { return buffer; },
298 "BufferDescriptorA invalid buffer_index {}", buffer_index);
298 buffer.resize(BufferDescriptorA()[buffer_index].Size()); 299 buffer.resize(BufferDescriptorA()[buffer_index].Size());
299 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); 300 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
300 } else { 301 } else {
301 ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return buffer; }, 302 ASSERT_OR_EXECUTE_MSG(
302 "BufferDescriptorX invalid buffer_index {}", buffer_index); 303 BufferDescriptorX().size() > buffer_index, { return buffer; },
304 "BufferDescriptorX invalid buffer_index {}", buffer_index);
303 buffer.resize(BufferDescriptorX()[buffer_index].Size()); 305 buffer.resize(BufferDescriptorX()[buffer_index].Size());
304 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); 306 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
305 } 307 }
@@ -324,16 +326,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
324 } 326 }
325 327
326 if (is_buffer_b) { 328 if (is_buffer_b) {
327 ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index && 329 ASSERT_OR_EXECUTE_MSG(
328 BufferDescriptorB()[buffer_index].Size() >= size, 330 BufferDescriptorB().size() > buffer_index &&
329 { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", 331 BufferDescriptorB()[buffer_index].Size() >= size,
330 buffer_index, size); 332 { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size);
331 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); 333 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
332 } else { 334 } else {
333 ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index && 335 ASSERT_OR_EXECUTE_MSG(
334 BufferDescriptorC()[buffer_index].Size() >= size, 336 BufferDescriptorC().size() > buffer_index &&
335 { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", 337 BufferDescriptorC()[buffer_index].Size() >= size,
336 buffer_index, size); 338 { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size);
337 memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); 339 memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
338 } 340 }
339 341
@@ -344,12 +346,14 @@ std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const
344 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && 346 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
345 BufferDescriptorA()[buffer_index].Size()}; 347 BufferDescriptorA()[buffer_index].Size()};
346 if (is_buffer_a) { 348 if (is_buffer_a) {
347 ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return 0; }, 349 ASSERT_OR_EXECUTE_MSG(
348 "BufferDescriptorA invalid buffer_index {}", buffer_index); 350 BufferDescriptorA().size() > buffer_index, { return 0; },
351 "BufferDescriptorA invalid buffer_index {}", buffer_index);
349 return BufferDescriptorA()[buffer_index].Size(); 352 return BufferDescriptorA()[buffer_index].Size();
350 } else { 353 } else {
351 ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return 0; }, 354 ASSERT_OR_EXECUTE_MSG(
352 "BufferDescriptorX invalid buffer_index {}", buffer_index); 355 BufferDescriptorX().size() > buffer_index, { return 0; },
356 "BufferDescriptorX invalid buffer_index {}", buffer_index);
353 return BufferDescriptorX()[buffer_index].Size(); 357 return BufferDescriptorX()[buffer_index].Size();
354 } 358 }
355} 359}
@@ -358,12 +362,14 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons
358 const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && 362 const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
359 BufferDescriptorB()[buffer_index].Size()}; 363 BufferDescriptorB()[buffer_index].Size()};
360 if (is_buffer_b) { 364 if (is_buffer_b) {
361 ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index, { return 0; }, 365 ASSERT_OR_EXECUTE_MSG(
362 "BufferDescriptorB invalid buffer_index {}", buffer_index); 366 BufferDescriptorB().size() > buffer_index, { return 0; },
367 "BufferDescriptorB invalid buffer_index {}", buffer_index);
363 return BufferDescriptorB()[buffer_index].Size(); 368 return BufferDescriptorB()[buffer_index].Size();
364 } else { 369 } else {
365 ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index, { return 0; }, 370 ASSERT_OR_EXECUTE_MSG(
366 "BufferDescriptorC invalid buffer_index {}", buffer_index); 371 BufferDescriptorC().size() > buffer_index, { return 0; },
372 "BufferDescriptorC invalid buffer_index {}", buffer_index);
367 return BufferDescriptorC()[buffer_index].Size(); 373 return BufferDescriptorC()[buffer_index].Size();
368 } 374 }
369 return 0; 375 return 0;
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index b31673928..f3277b766 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -13,6 +13,7 @@
13#include <vector> 13#include <vector>
14#include <boost/container/small_vector.hpp> 14#include <boost/container/small_vector.hpp>
15#include "common/common_types.h" 15#include "common/common_types.h"
16#include "common/concepts.h"
16#include "common/swap.h" 17#include "common/swap.h"
17#include "core/hle/ipc.h" 18#include "core/hle/ipc.h"
18#include "core/hle/kernel/object.h" 19#include "core/hle/kernel/object.h"
@@ -193,23 +194,24 @@ public:
193 194
194 /* Helper function to write a buffer using the appropriate buffer descriptor 195 /* Helper function to write a buffer using the appropriate buffer descriptor
195 * 196 *
196 * @tparam ContiguousContainer an arbitrary container that satisfies the 197 * @tparam T an arbitrary container that satisfies the
197 * ContiguousContainer concept in the C++ standard library. 198 * ContiguousContainer concept in the C++ standard library or a trivially copyable type.
198 * 199 *
199 * @param container The container to write the data of into a buffer. 200 * @param data The container/data to write into a buffer.
200 * @param buffer_index The buffer in particular to write to. 201 * @param buffer_index The buffer in particular to write to.
201 */ 202 */
202 template <typename ContiguousContainer, 203 template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>>
203 typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>> 204 std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const {
204 std::size_t WriteBuffer(const ContiguousContainer& container, 205 if constexpr (Common::IsSTLContainer<T>) {
205 std::size_t buffer_index = 0) const { 206 using ContiguousType = typename T::value_type;
206 using ContiguousType = typename ContiguousContainer::value_type; 207 static_assert(std::is_trivially_copyable_v<ContiguousType>,
207 208 "Container to WriteBuffer must contain trivially copyable objects");
208 static_assert(std::is_trivially_copyable_v<ContiguousType>, 209 return WriteBuffer(std::data(data), std::size(data) * sizeof(ContiguousType),
209 "Container to WriteBuffer must contain trivially copyable objects"); 210 buffer_index);
210 211 } else {
211 return WriteBuffer(std::data(container), std::size(container) * sizeof(ContiguousType), 212 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
212 buffer_index); 213 return WriteBuffer(&data, sizeof(T), buffer_index);
214 }
213 } 215 }
214 216
215 /// Helper function to get the size of the input buffer 217 /// Helper function to get the size of the input buffer
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index e1c7a0f3b..f2b0fe2fd 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -145,16 +145,18 @@ struct KernelCore::Impl {
145 145
146 void InitializePreemption(KernelCore& kernel) { 146 void InitializePreemption(KernelCore& kernel) {
147 preemption_event = Core::Timing::CreateEvent( 147 preemption_event = Core::Timing::CreateEvent(
148 "PreemptionCallback", [this, &kernel](u64 userdata, s64 cycles_late) { 148 "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) {
149 { 149 {
150 SchedulerLock lock(kernel); 150 SchedulerLock lock(kernel);
151 global_scheduler.PreemptThreads(); 151 global_scheduler.PreemptThreads();
152 } 152 }
153 s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); 153 const auto time_interval = std::chrono::nanoseconds{
154 Core::Timing::msToCycles(std::chrono::milliseconds(10))};
154 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); 155 system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
155 }); 156 });
156 157
157 s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); 158 const auto time_interval =
159 std::chrono::nanoseconds{Core::Timing::msToCycles(std::chrono::milliseconds(10))};
158 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); 160 system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
159 } 161 }
160 162
@@ -217,6 +219,7 @@ struct KernelCore::Impl {
217 return static_cast<u32>(system.GetCpuManager().CurrentCore()); 219 return static_cast<u32>(system.GetCpuManager().CurrentCore());
218 } 220 }
219 } 221 }
222 std::unique_lock lock{register_thread_mutex};
220 const auto it = host_thread_ids.find(this_id); 223 const auto it = host_thread_ids.find(this_id);
221 if (it == host_thread_ids.end()) { 224 if (it == host_thread_ids.end()) {
222 return Core::INVALID_HOST_THREAD_ID; 225 return Core::INVALID_HOST_THREAD_ID;
@@ -322,7 +325,7 @@ struct KernelCore::Impl {
322 std::unordered_map<std::thread::id, u32> host_thread_ids; 325 std::unordered_map<std::thread::id, u32> host_thread_ids;
323 u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES}; 326 u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
324 std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads; 327 std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads;
325 std::mutex register_thread_mutex; 328 mutable std::mutex register_thread_mutex;
326 329
327 // Kernel memory management 330 // Kernel memory management
328 std::unique_ptr<Memory::MemoryManager> memory_manager; 331 std::unique_ptr<Memory::MemoryManager> memory_manager;
diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp
index 5d6aac00f..a3fadb533 100644
--- a/src/core/hle/kernel/memory/page_table.cpp
+++ b/src/core/hle/kernel/memory/page_table.cpp
@@ -604,7 +604,6 @@ ResultCode PageTable::MapPages(VAddr addr, const PageLinkedList& page_linked_lis
604 if (const auto result{ 604 if (const auto result{
605 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; 605 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())};
606 result.IsError()) { 606 result.IsError()) {
607 const MemoryInfo info{block_manager->FindBlock(cur_addr).GetMemoryInfo()};
608 const std::size_t num_pages{(addr - cur_addr) / PageSize}; 607 const std::size_t num_pages{(addr - cur_addr) / PageSize};
609 608
610 ASSERT( 609 ASSERT(
@@ -852,11 +851,12 @@ ResultCode PageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
852 return result; 851 return result;
853 } 852 }
854 853
855 block_manager->UpdateLock(addr, size / PageSize, 854 block_manager->UpdateLock(
856 [](MemoryBlockManager::iterator block, MemoryPermission perm) { 855 addr, size / PageSize,
857 block->ShareToDevice(perm); 856 [](MemoryBlockManager::iterator block, MemoryPermission perm) {
858 }, 857 block->ShareToDevice(perm);
859 perm); 858 },
859 perm);
860 860
861 return RESULT_SUCCESS; 861 return RESULT_SUCCESS;
862} 862}
@@ -874,11 +874,12 @@ ResultCode PageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
874 return result; 874 return result;
875 } 875 }
876 876
877 block_manager->UpdateLock(addr, size / PageSize, 877 block_manager->UpdateLock(
878 [](MemoryBlockManager::iterator block, MemoryPermission perm) { 878 addr, size / PageSize,
879 block->UnshareToDevice(perm); 879 [](MemoryBlockManager::iterator block, MemoryPermission perm) {
880 }, 880 block->UnshareToDevice(perm);
881 perm); 881 },
882 perm);
882 883
883 return RESULT_SUCCESS; 884 return RESULT_SUCCESS;
884} 885}
diff --git a/src/core/hle/kernel/memory/system_control.cpp b/src/core/hle/kernel/memory/system_control.cpp
index 2f98e9c4c..11d204bc2 100644
--- a/src/core/hle/kernel/memory/system_control.cpp
+++ b/src/core/hle/kernel/memory/system_control.cpp
@@ -7,22 +7,15 @@
7#include "core/hle/kernel/memory/system_control.h" 7#include "core/hle/kernel/memory/system_control.h"
8 8
9namespace Kernel::Memory::SystemControl { 9namespace Kernel::Memory::SystemControl {
10 10namespace {
11u64 GenerateRandomU64ForInit() {
12 static std::random_device device;
13 static std::mt19937 gen(device());
14 static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
15 return distribution(gen);
16}
17
18template <typename F> 11template <typename F>
19u64 GenerateUniformRange(u64 min, u64 max, F f) { 12u64 GenerateUniformRange(u64 min, u64 max, F f) {
20 /* Handle the case where the difference is too large to represent. */ 13 // Handle the case where the difference is too large to represent.
21 if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) { 14 if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
22 return f(); 15 return f();
23 } 16 }
24 17
25 /* Iterate until we get a value in range. */ 18 // Iterate until we get a value in range.
26 const u64 range_size = ((max + 1) - min); 19 const u64 range_size = ((max + 1) - min);
27 const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size; 20 const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
28 while (true) { 21 while (true) {
@@ -32,6 +25,14 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) {
32 } 25 }
33} 26}
34 27
28u64 GenerateRandomU64ForInit() {
29 static std::random_device device;
30 static std::mt19937 gen(device());
31 static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
32 return distribution(gen);
33}
34} // Anonymous namespace
35
35u64 GenerateRandomRange(u64 min, u64 max) { 36u64 GenerateRandomRange(u64 min, u64 max) {
36 return GenerateUniformRange(min, max, GenerateRandomU64ForInit); 37 return GenerateUniformRange(min, max, GenerateRandomU64ForInit);
37} 38}
diff --git a/src/core/hle/kernel/memory/system_control.h b/src/core/hle/kernel/memory/system_control.h
index 3fa93111d..19cab8cbc 100644
--- a/src/core/hle/kernel/memory/system_control.h
+++ b/src/core/hle/kernel/memory/system_control.h
@@ -8,11 +8,6 @@
8 8
9namespace Kernel::Memory::SystemControl { 9namespace Kernel::Memory::SystemControl {
10 10
11u64 GenerateRandomU64ForInit();
12
13template <typename F>
14u64 GenerateUniformRange(u64 min, u64 max, F f);
15
16u64 GenerateRandomRange(u64 min, u64 max); 11u64 GenerateRandomRange(u64 min, u64 max);
17 12
18} // namespace Kernel::Memory::SystemControl 13} // namespace Kernel::Memory::SystemControl
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index f93e5e4b0..5cbd3b912 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -131,7 +131,8 @@ u32 GlobalScheduler::SelectThreads() {
131 u32 cores_needing_context_switch{}; 131 u32 cores_needing_context_switch{};
132 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 132 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
133 Scheduler& sched = kernel.Scheduler(core); 133 Scheduler& sched = kernel.Scheduler(core);
134 ASSERT(top_threads[core] == nullptr || top_threads[core]->GetProcessorID() == core); 134 ASSERT(top_threads[core] == nullptr ||
135 static_cast<u32>(top_threads[core]->GetProcessorID()) == core);
135 if (update_thread(top_threads[core], sched)) { 136 if (update_thread(top_threads[core], sched)) {
136 cores_needing_context_switch |= (1ul << core); 137 cores_needing_context_switch |= (1ul << core);
137 } 138 }
@@ -663,32 +664,26 @@ void Scheduler::Reload() {
663} 664}
664 665
665void Scheduler::SwitchContextStep2() { 666void Scheduler::SwitchContextStep2() {
666 Thread* previous_thread = current_thread_prev.get();
667 Thread* new_thread = selected_thread.get();
668
669 // Load context of new thread 667 // Load context of new thread
670 Process* const previous_process = 668 if (selected_thread) {
671 previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr; 669 ASSERT_MSG(selected_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
672
673 if (new_thread) {
674 ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
675 "Thread must be runnable."); 670 "Thread must be runnable.");
676 671
677 // Cancel any outstanding wakeup events for this thread 672 // Cancel any outstanding wakeup events for this thread
678 new_thread->SetIsRunning(true); 673 selected_thread->SetIsRunning(true);
679 new_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); 674 selected_thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
680 new_thread->SetWasRunning(false); 675 selected_thread->SetWasRunning(false);
681 676
682 auto* const thread_owner_process = current_thread->GetOwnerProcess(); 677 auto* const thread_owner_process = current_thread->GetOwnerProcess();
683 if (thread_owner_process != nullptr) { 678 if (thread_owner_process != nullptr) {
684 system.Kernel().MakeCurrentProcess(thread_owner_process); 679 system.Kernel().MakeCurrentProcess(thread_owner_process);
685 } 680 }
686 if (!new_thread->IsHLEThread()) { 681 if (!selected_thread->IsHLEThread()) {
687 Core::ARM_Interface& cpu_core = new_thread->ArmInterface(); 682 Core::ARM_Interface& cpu_core = selected_thread->ArmInterface();
688 cpu_core.LoadContext(new_thread->GetContext32()); 683 cpu_core.LoadContext(selected_thread->GetContext32());
689 cpu_core.LoadContext(new_thread->GetContext64()); 684 cpu_core.LoadContext(selected_thread->GetContext64());
690 cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); 685 cpu_core.SetTlsAddress(selected_thread->GetTLSAddress());
691 cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); 686 cpu_core.SetTPIDR_EL0(selected_thread->GetTPIDR_EL0());
692 cpu_core.ChangeProcessorID(this->core_id); 687 cpu_core.ChangeProcessorID(this->core_id);
693 cpu_core.ClearExclusiveState(); 688 cpu_core.ClearExclusiveState();
694 } 689 }
@@ -761,7 +756,11 @@ void Scheduler::SwitchToCurrent() {
761 current_thread = selected_thread; 756 current_thread = selected_thread;
762 is_context_switch_pending = false; 757 is_context_switch_pending = false;
763 } 758 }
764 while (!is_context_switch_pending) { 759 const auto is_switch_pending = [this] {
760 std::scoped_lock lock{guard};
761 return is_context_switch_pending;
762 };
763 do {
765 if (current_thread != nullptr && !current_thread->IsHLEThread()) { 764 if (current_thread != nullptr && !current_thread->IsHLEThread()) {
766 current_thread->context_guard.lock(); 765 current_thread->context_guard.lock();
767 if (!current_thread->IsRunnable()) { 766 if (!current_thread->IsRunnable()) {
@@ -780,7 +779,7 @@ void Scheduler::SwitchToCurrent() {
780 next_context = &idle_thread->GetHostContext(); 779 next_context = &idle_thread->GetHostContext();
781 } 780 }
782 Common::Fiber::YieldTo(switch_fiber, *next_context); 781 Common::Fiber::YieldTo(switch_fiber, *next_context);
783 } 782 } while (!is_switch_pending());
784 } 783 }
785} 784}
786 785
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index b3b4b5169..b6f04dcea 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -188,7 +188,7 @@ private:
188 188
189 /// Scheduler lock mechanisms. 189 /// Scheduler lock mechanisms.
190 bool is_locked{}; 190 bool is_locked{};
191 Common::SpinLock inner_lock{}; 191 std::mutex inner_lock;
192 std::atomic<s64> scope_lock{}; 192 std::atomic<s64> scope_lock{};
193 Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; 193 Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()};
194 194
@@ -289,7 +289,7 @@ private:
289 289
290class SchedulerLock { 290class SchedulerLock {
291public: 291public:
292 explicit SchedulerLock(KernelCore& kernel); 292 [[nodiscard]] explicit SchedulerLock(KernelCore& kernel);
293 ~SchedulerLock(); 293 ~SchedulerLock();
294 294
295protected: 295protected:
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 7b23a6889..8c19f2534 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -8,7 +8,6 @@
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "core/core.h"
12#include "core/core_timing.h" 11#include "core/core_timing.h"
13#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
14#include "core/hle/kernel/client_port.h" 13#include "core/hle/kernel/client_port.h"
@@ -33,8 +32,10 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern
33 std::string name) { 32 std::string name) {
34 std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; 33 std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)};
35 34
36 session->request_event = Core::Timing::CreateEvent( 35 session->request_event =
37 name, [session](u64 userdata, s64 cycles_late) { session->CompleteSyncRequest(); }); 36 Core::Timing::CreateEvent(name, [session](std::uintptr_t, std::chrono::nanoseconds) {
37 session->CompleteSyncRequest();
38 });
38 session->name = std::move(name); 39 session->name = std::move(name);
39 session->parent = std::move(parent); 40 session->parent = std::move(parent);
40 41
@@ -183,10 +184,11 @@ ResultCode ServerSession::CompleteSyncRequest() {
183} 184}
184 185
185ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, 186ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
186 Core::Memory::Memory& memory) { 187 Core::Memory::Memory& memory,
187 ResultCode result = QueueSyncRequest(std::move(thread), memory); 188 Core::Timing::CoreTiming& core_timing) {
188 const u64 delay = kernel.IsMulticore() ? 0U : 20000U; 189 const ResultCode result = QueueSyncRequest(std::move(thread), memory);
189 Core::System::GetInstance().CoreTiming().ScheduleEvent(delay, request_event, {}); 190 const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000};
191 core_timing.ScheduleEvent(delay, request_event, {});
190 return result; 192 return result;
191} 193}
192 194
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index 403aaf10b..d23e9ec68 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -18,8 +18,9 @@ class Memory;
18} 18}
19 19
20namespace Core::Timing { 20namespace Core::Timing {
21class CoreTiming;
21struct EventType; 22struct EventType;
22} 23} // namespace Core::Timing
23 24
24namespace Kernel { 25namespace Kernel {
25 26
@@ -87,12 +88,14 @@ public:
87 /** 88 /**
88 * Handle a sync request from the emulated application. 89 * Handle a sync request from the emulated application.
89 * 90 *
90 * @param thread Thread that initiated the request. 91 * @param thread Thread that initiated the request.
91 * @param memory Memory context to handle the sync request under. 92 * @param memory Memory context to handle the sync request under.
93 * @param core_timing Core timing context to schedule the request event under.
92 * 94 *
93 * @returns ResultCode from the operation. 95 * @returns ResultCode from the operation.
94 */ 96 */
95 ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); 97 ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
98 Core::Timing::CoreTiming& core_timing);
96 99
97 bool ShouldWait(const Thread* thread) const override; 100 bool ShouldWait(const Thread* thread) const override;
98 101
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 01ae57053..bafd1ced7 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -346,7 +346,7 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
346 SchedulerLock lock(system.Kernel()); 346 SchedulerLock lock(system.Kernel());
347 thread->InvalidateHLECallback(); 347 thread->InvalidateHLECallback();
348 thread->SetStatus(ThreadStatus::WaitIPC); 348 thread->SetStatus(ThreadStatus::WaitIPC);
349 session->SendSyncRequest(SharedFrom(thread), system.Memory()); 349 session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming());
350 } 350 }
351 351
352 if (thread->HasHLECallback()) { 352 if (thread->HasHLECallback()) {
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index 941305e8e..95f2446c9 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -16,14 +16,14 @@ namespace Kernel {
16 16
17TimeManager::TimeManager(Core::System& system_) : system{system_} { 17TimeManager::TimeManager(Core::System& system_) : system{system_} {
18 time_manager_event_type = Core::Timing::CreateEvent( 18 time_manager_event_type = Core::Timing::CreateEvent(
19 "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) { 19 "Kernel::TimeManagerCallback",
20 SchedulerLock lock(system.Kernel()); 20 [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
21 Handle proper_handle = static_cast<Handle>(thread_handle); 21 const SchedulerLock lock(system.Kernel());
22 const auto proper_handle = static_cast<Handle>(thread_handle);
22 if (cancelled_events[proper_handle]) { 23 if (cancelled_events[proper_handle]) {
23 return; 24 return;
24 } 25 }
25 std::shared_ptr<Thread> thread = 26 auto thread = this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
26 this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
27 thread->OnWakeUp(); 27 thread->OnWakeUp();
28 }); 28 });
29} 29}
@@ -34,7 +34,8 @@ void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64
34 ASSERT(timetask); 34 ASSERT(timetask);
35 ASSERT(timetask->GetStatus() != ThreadStatus::Ready); 35 ASSERT(timetask->GetStatus() != ThreadStatus::Ready);
36 ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex); 36 ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex);
37 system.CoreTiming().ScheduleEvent(nanoseconds, time_manager_event_type, event_handle); 37 system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds},
38 time_manager_event_type, event_handle);
38 } else { 39 } else {
39 event_handle = InvalidHandle; 40 event_handle = InvalidHandle;
40 } 41 }
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 450f61fea..b6bdbd988 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -342,8 +342,9 @@ ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) {
342 */ 342 */
343#define CASCADE_RESULT(target, source) \ 343#define CASCADE_RESULT(target, source) \
344 auto CONCAT2(check_result_L, __LINE__) = source; \ 344 auto CONCAT2(check_result_L, __LINE__) = source; \
345 if (CONCAT2(check_result_L, __LINE__).Failed()) \ 345 if (CONCAT2(check_result_L, __LINE__).Failed()) { \
346 return CONCAT2(check_result_L, __LINE__).Code(); \ 346 return CONCAT2(check_result_L, __LINE__).Code(); \
347 } \
347 target = std::move(*CONCAT2(check_result_L, __LINE__)) 348 target = std::move(*CONCAT2(check_result_L, __LINE__))
348 349
349/** 350/**
@@ -351,6 +352,9 @@ ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) {
351 * non-success, or discarded otherwise. 352 * non-success, or discarded otherwise.
352 */ 353 */
353#define CASCADE_CODE(source) \ 354#define CASCADE_CODE(source) \
354 auto CONCAT2(check_result_L, __LINE__) = source; \ 355 do { \
355 if (CONCAT2(check_result_L, __LINE__).IsError()) \ 356 auto CONCAT2(check_result_L, __LINE__) = source; \
356 return CONCAT2(check_result_L, __LINE__); 357 if (CONCAT2(check_result_L, __LINE__).IsError()) { \
358 return CONCAT2(check_result_L, __LINE__); \
359 } \
360 } while (false)
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 8ac856ec3..6b1613510 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -35,7 +35,7 @@ constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 30};
35constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; 35constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
36 36
37static std::string GetImagePath(Common::UUID uuid) { 37static std::string GetImagePath(Common::UUID uuid) {
38 return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 38 return Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
39 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; 39 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
40} 40}
41 41
@@ -286,9 +286,7 @@ protected:
286 ProfileBase profile_base{}; 286 ProfileBase profile_base{};
287 ProfileData data{}; 287 ProfileData data{};
288 if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { 288 if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
289 std::array<u8, sizeof(ProfileData)> raw_data; 289 ctx.WriteBuffer(data);
290 std::memcpy(raw_data.data(), &data, sizeof(ProfileData));
291 ctx.WriteBuffer(raw_data);
292 IPC::ResponseBuilder rb{ctx, 16}; 290 IPC::ResponseBuilder rb{ctx, 16};
293 rb.Push(RESULT_SUCCESS); 291 rb.Push(RESULT_SUCCESS);
294 rb.PushRaw(profile_base); 292 rb.PushRaw(profile_base);
@@ -320,7 +318,7 @@ protected:
320 IPC::ResponseBuilder rb{ctx, 3}; 318 IPC::ResponseBuilder rb{ctx, 3};
321 rb.Push(RESULT_SUCCESS); 319 rb.Push(RESULT_SUCCESS);
322 320
323 const FileUtil::IOFile image(GetImagePath(user_id), "rb"); 321 const Common::FS::IOFile image(GetImagePath(user_id), "rb");
324 if (!image.IsOpen()) { 322 if (!image.IsOpen()) {
325 LOG_WARNING(Service_ACC, 323 LOG_WARNING(Service_ACC,
326 "Failed to load user provided image! Falling back to built-in backup..."); 324 "Failed to load user provided image! Falling back to built-in backup...");
@@ -333,7 +331,7 @@ protected:
333 std::vector<u8> buffer(size); 331 std::vector<u8> buffer(size);
334 image.ReadBytes(buffer.data(), buffer.size()); 332 image.ReadBytes(buffer.data(), buffer.size());
335 333
336 ctx.WriteBuffer(buffer.data(), buffer.size()); 334 ctx.WriteBuffer(buffer);
337 rb.Push<u32>(size); 335 rb.Push<u32>(size);
338 } 336 }
339 337
@@ -342,7 +340,7 @@ protected:
342 IPC::ResponseBuilder rb{ctx, 3}; 340 IPC::ResponseBuilder rb{ctx, 3};
343 rb.Push(RESULT_SUCCESS); 341 rb.Push(RESULT_SUCCESS);
344 342
345 const FileUtil::IOFile image(GetImagePath(user_id), "rb"); 343 const Common::FS::IOFile image(GetImagePath(user_id), "rb");
346 344
347 if (!image.IsOpen()) { 345 if (!image.IsOpen()) {
348 LOG_WARNING(Service_ACC, 346 LOG_WARNING(Service_ACC,
@@ -407,7 +405,7 @@ protected:
407 ProfileData data; 405 ProfileData data;
408 std::memcpy(&data, user_data.data(), sizeof(ProfileData)); 406 std::memcpy(&data, user_data.data(), sizeof(ProfileData));
409 407
410 FileUtil::IOFile image(GetImagePath(user_id), "wb"); 408 Common::FS::IOFile image(GetImagePath(user_id), "wb");
411 409
412 if (!image.IsOpen() || !image.Resize(image_data.size()) || 410 if (!image.IsOpen() || !image.Resize(image_data.size()) ||
413 image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() || 411 image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() ||
@@ -776,6 +774,17 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) {
776 rb.Push(RESULT_SUCCESS); 774 rb.Push(RESULT_SUCCESS);
777} 775}
778 776
777void Module::Interface::LoadOpenContext(Kernel::HLERequestContext& ctx) {
778 LOG_WARNING(Service_ACC, "(STUBBED) called");
779
780 // This is similar to GetBaasAccountManagerForApplication
781 // This command is used concurrently with ListOpenContextStoredUsers
782 // TODO: Find the differences between this and GetBaasAccountManagerForApplication
783 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
784 rb.Push(RESULT_SUCCESS);
785 rb.PushIpcInterface<IManagerForApplication>(profile_manager->GetLastOpenedUser());
786}
787
779void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { 788void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) {
780 LOG_WARNING(Service_ACC, "(STUBBED) called"); 789 LOG_WARNING(Service_ACC, "(STUBBED) called");
781 790
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index d4c6395c6..c611efd89 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -34,6 +34,7 @@ public:
34 void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); 34 void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
35 void GetProfileEditor(Kernel::HLERequestContext& ctx); 35 void GetProfileEditor(Kernel::HLERequestContext& ctx);
36 void ListQualifiedUsers(Kernel::HLERequestContext& ctx); 36 void ListQualifiedUsers(Kernel::HLERequestContext& ctx);
37 void LoadOpenContext(Kernel::HLERequestContext& ctx);
37 void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); 38 void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx);
38 39
39 private: 40 private:
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index cb44e06b7..75a24f8f5 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -29,7 +29,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
29 {110, nullptr, "StoreSaveDataThumbnail"}, 29 {110, nullptr, "StoreSaveDataThumbnail"},
30 {111, nullptr, "ClearSaveDataThumbnail"}, 30 {111, nullptr, "ClearSaveDataThumbnail"},
31 {120, nullptr, "CreateGuestLoginRequest"}, 31 {120, nullptr, "CreateGuestLoginRequest"},
32 {130, nullptr, "LoadOpenContext"}, // 5.0.0+ 32 {130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+
33 {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ 33 {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+
34 {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ 34 {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+
35 {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ 35 {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index eb8c81645..9b829e957 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -13,6 +13,7 @@
13 13
14namespace Service::Account { 14namespace Service::Account {
15 15
16namespace FS = Common::FS;
16using Common::UUID; 17using Common::UUID;
17 18
18struct UserRaw { 19struct UserRaw {
@@ -58,7 +59,7 @@ ProfileManager::~ProfileManager() {
58/// internal management of the users profiles 59/// internal management of the users profiles
59std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) { 60std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) {
60 if (user_count >= MAX_USERS) { 61 if (user_count >= MAX_USERS) {
61 return {}; 62 return std::nullopt;
62 } 63 }
63 profiles[user_count] = profile; 64 profiles[user_count] = profile;
64 return user_count++; 65 return user_count++;
@@ -101,13 +102,14 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern
101 [&uuid](const ProfileInfo& profile) { return uuid == profile.user_uuid; })) { 102 [&uuid](const ProfileInfo& profile) { return uuid == profile.user_uuid; })) {
102 return ERROR_USER_ALREADY_EXISTS; 103 return ERROR_USER_ALREADY_EXISTS;
103 } 104 }
104 ProfileInfo profile; 105
105 profile.user_uuid = uuid; 106 return AddUser({
106 profile.username = username; 107 .user_uuid = uuid,
107 profile.data = {}; 108 .username = username,
108 profile.creation_time = 0x0; 109 .creation_time = 0,
109 profile.is_open = false; 110 .data = {},
110 return AddUser(profile); 111 .is_open = false,
112 });
111} 113}
112 114
113/// Creates a new user on the system. This function allows a much simpler method of registration 115/// Creates a new user on the system. This function allows a much simpler method of registration
@@ -126,7 +128,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username)
126 128
127std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { 129std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
128 if (index >= MAX_USERS) { 130 if (index >= MAX_USERS) {
129 return {}; 131 return std::nullopt;
130 } 132 }
131 133
132 return profiles[index].user_uuid; 134 return profiles[index].user_uuid;
@@ -135,13 +137,13 @@ std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
135/// Returns a users profile index based on their user id. 137/// Returns a users profile index based on their user id.
136std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { 138std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
137 if (!uuid) { 139 if (!uuid) {
138 return {}; 140 return std::nullopt;
139 } 141 }
140 142
141 const auto iter = std::find_if(profiles.begin(), profiles.end(), 143 const auto iter = std::find_if(profiles.begin(), profiles.end(),
142 [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); 144 [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
143 if (iter == profiles.end()) { 145 if (iter == profiles.end()) {
144 return {}; 146 return std::nullopt;
145 } 147 }
146 148
147 return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); 149 return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
@@ -317,9 +319,8 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase&
317} 319}
318 320
319void ProfileManager::ParseUserSaveFile() { 321void ProfileManager::ParseUserSaveFile() {
320 FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 322 const FS::IOFile save(
321 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", 323 FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", "rb");
322 "rb");
323 324
324 if (!save.IsOpen()) { 325 if (!save.IsOpen()) {
325 LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new " 326 LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
@@ -339,7 +340,13 @@ void ProfileManager::ParseUserSaveFile() {
339 continue; 340 continue;
340 } 341 }
341 342
342 AddUser({user.uuid, user.username, user.timestamp, user.extra_data, false}); 343 AddUser({
344 .user_uuid = user.uuid,
345 .username = user.username,
346 .creation_time = user.timestamp,
347 .data = user.extra_data,
348 .is_open = false,
349 });
343 } 350 }
344 351
345 std::stable_partition(profiles.begin(), profiles.end(), 352 std::stable_partition(profiles.begin(), profiles.end(),
@@ -350,29 +357,31 @@ void ProfileManager::WriteUserSaveFile() {
350 ProfileDataRaw raw{}; 357 ProfileDataRaw raw{};
351 358
352 for (std::size_t i = 0; i < MAX_USERS; ++i) { 359 for (std::size_t i = 0; i < MAX_USERS; ++i) {
353 raw.users[i].username = profiles[i].username; 360 raw.users[i] = {
354 raw.users[i].uuid2 = profiles[i].user_uuid; 361 .uuid = profiles[i].user_uuid,
355 raw.users[i].uuid = profiles[i].user_uuid; 362 .uuid2 = profiles[i].user_uuid,
356 raw.users[i].timestamp = profiles[i].creation_time; 363 .timestamp = profiles[i].creation_time,
357 raw.users[i].extra_data = profiles[i].data; 364 .username = profiles[i].username,
365 .extra_data = profiles[i].data,
366 };
358 } 367 }
359 368
360 const auto raw_path = 369 const auto raw_path = FS::GetUserPath(FS::UserPath::NANDDir) + "/system/save/8000000000000010";
361 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; 370 if (FS::Exists(raw_path) && !FS::IsDirectory(raw_path)) {
362 if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) 371 FS::Delete(raw_path);
363 FileUtil::Delete(raw_path); 372 }
364 373
365 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 374 const auto path =
366 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat"; 375 FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
367 376
368 if (!FileUtil::CreateFullPath(path)) { 377 if (!FS::CreateFullPath(path)) {
369 LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory " 378 LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory "
370 "nand/system/save/8000000000000010/su/avators to mitigate this " 379 "nand/system/save/8000000000000010/su/avators to mitigate this "
371 "issue."); 380 "issue.");
372 return; 381 return;
373 } 382 }
374 383
375 FileUtil::IOFile save(path, "wb"); 384 FS::IOFile save(path, "wb");
376 385
377 if (!save.IsOpen()) { 386 if (!save.IsOpen()) {
378 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " 387 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 4e7a0bec9..d7a81f64a 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -378,7 +378,11 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext&
378} 378}
379 379
380void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { 380void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
381 LOG_WARNING(Service_AM, "(STUBBED) called"); 381 IPC::RequestParser rp{ctx};
382 const auto permission = rp.PopEnum<ScreenshotPermission>();
383 LOG_DEBUG(Service_AM, "called, permission={}", permission);
384
385 screenshot_permission = permission;
382 386
383 IPC::ResponseBuilder rb{ctx, 2}; 387 IPC::ResponseBuilder rb{ctx, 2};
384 rb.Push(RESULT_SUCCESS); 388 rb.Push(RESULT_SUCCESS);
@@ -1188,7 +1192,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1188 {120, nullptr, "ExecuteProgram"}, 1192 {120, nullptr, "ExecuteProgram"},
1189 {121, nullptr, "ClearUserChannel"}, 1193 {121, nullptr, "ClearUserChannel"},
1190 {122, nullptr, "UnpopToUserChannel"}, 1194 {122, nullptr, "UnpopToUserChannel"},
1191 {123, nullptr, "GetPreviousProgramIndex"}, 1195 {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
1192 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, 1196 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
1193 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, 1197 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
1194 {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, 1198 {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"},
@@ -1342,12 +1346,12 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
1342 1346
1343 LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); 1347 LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
1344 1348
1345 FileSys::SaveDataDescriptor descriptor{}; 1349 FileSys::SaveDataAttribute attribute{};
1346 descriptor.title_id = system.CurrentProcess()->GetTitleID(); 1350 attribute.title_id = system.CurrentProcess()->GetTitleID();
1347 descriptor.user_id = user_id; 1351 attribute.user_id = user_id;
1348 descriptor.type = FileSys::SaveDataType::SaveData; 1352 attribute.type = FileSys::SaveDataType::SaveData;
1349 const auto res = system.GetFileSystemController().CreateSaveData( 1353 const auto res = system.GetFileSystemController().CreateSaveData(
1350 FileSys::SaveDataSpaceId::NandUser, descriptor); 1354 FileSys::SaveDataSpaceId::NandUser, attribute);
1351 1355
1352 IPC::ResponseBuilder rb{ctx, 4}; 1356 IPC::ResponseBuilder rb{ctx, 4};
1353 rb.Push(res.Code()); 1357 rb.Push(res.Code());
@@ -1405,7 +1409,6 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
1405 // Get supported languages from NACP, if possible 1409 // Get supported languages from NACP, if possible
1406 // Default to 0 (all languages supported) 1410 // Default to 0 (all languages supported)
1407 u32 supported_languages = 0; 1411 u32 supported_languages = 0;
1408 FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
1409 1412
1410 const auto res = [this] { 1413 const auto res = [this] {
1411 const auto title_id = system.CurrentProcess()->GetTitleID(); 1414 const auto title_id = system.CurrentProcess()->GetTitleID();
@@ -1551,6 +1554,14 @@ void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(Kernel::HLEReque
1551 rb.Push<u32>(0); 1554 rb.Push<u32>(0);
1552} 1555}
1553 1556
1557void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) {
1558 LOG_WARNING(Service_AM, "(STUBBED) called");
1559
1560 IPC::ResponseBuilder rb{ctx, 3};
1561 rb.Push(RESULT_SUCCESS);
1562 rb.Push<s32>(previous_program_index);
1563}
1564
1554void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) { 1565void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) {
1555 LOG_WARNING(Service_AM, "(STUBBED) called"); 1566 LOG_WARNING(Service_AM, "(STUBBED) called");
1556 1567
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 6cfb11b48..bcc06affe 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -149,6 +149,12 @@ private:
149 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); 149 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
150 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); 150 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
151 151
152 enum class ScreenshotPermission : u32 {
153 Inherit = 0,
154 Enable = 1,
155 Disable = 2,
156 };
157
152 Core::System& system; 158 Core::System& system;
153 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 159 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
154 Kernel::EventPair launchable_event; 160 Kernel::EventPair launchable_event;
@@ -157,6 +163,7 @@ private:
157 u32 idle_time_detection_extension = 0; 163 u32 idle_time_detection_extension = 0;
158 u64 num_fatal_sections_entered = 0; 164 u64 num_fatal_sections_entered = 0;
159 bool is_auto_sleep_disabled = false; 165 bool is_auto_sleep_disabled = false;
166 ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit;
160}; 167};
161 168
162class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { 169class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
@@ -281,11 +288,13 @@ private:
281 void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx); 288 void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx);
282 void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx); 289 void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx);
283 void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx); 290 void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx);
291 void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx);
284 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); 292 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
285 void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx); 293 void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx);
286 294
287 bool launch_popped_application_specific = false; 295 bool launch_popped_application_specific = false;
288 bool launch_popped_account_preselect = false; 296 bool launch_popped_account_preselect = false;
297 s32 previous_program_index{-1};
289 Kernel::EventPair gpu_error_detected_event; 298 Kernel::EventPair gpu_error_detected_event;
290 Kernel::EventPair friend_invitation_storage_channel_event; 299 Kernel::EventPair friend_invitation_storage_channel_event;
291 Core::System& system; 300 Core::System& system;
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index c3261f3e6..2b626bb40 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -5,6 +5,7 @@
5#include <cstring> 5#include <cstring>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/frontend/applets/controller.h"
8#include "core/frontend/applets/error.h" 9#include "core/frontend/applets/error.h"
9#include "core/frontend/applets/general_frontend.h" 10#include "core/frontend/applets/general_frontend.h"
10#include "core/frontend/applets/profile_select.h" 11#include "core/frontend/applets/profile_select.h"
@@ -15,6 +16,7 @@
15#include "core/hle/kernel/writable_event.h" 16#include "core/hle/kernel/writable_event.h"
16#include "core/hle/service/am/am.h" 17#include "core/hle/service/am/am.h"
17#include "core/hle/service/am/applets/applets.h" 18#include "core/hle/service/am/applets/applets.h"
19#include "core/hle/service/am/applets/controller.h"
18#include "core/hle/service/am/applets/error.h" 20#include "core/hle/service/am/applets/error.h"
19#include "core/hle/service/am/applets/general_backend.h" 21#include "core/hle/service/am/applets/general_backend.h"
20#include "core/hle/service/am/applets/profile_select.h" 22#include "core/hle/service/am/applets/profile_select.h"
@@ -140,14 +142,14 @@ void Applet::Initialize() {
140 142
141AppletFrontendSet::AppletFrontendSet() = default; 143AppletFrontendSet::AppletFrontendSet() = default;
142 144
143AppletFrontendSet::AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error, 145AppletFrontendSet::AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce,
146 ErrorApplet error, ParentalControlsApplet parental_controls,
144 PhotoViewer photo_viewer, ProfileSelect profile_select, 147 PhotoViewer photo_viewer, ProfileSelect profile_select,
145 SoftwareKeyboard software_keyboard, WebBrowser web_browser, 148 SoftwareKeyboard software_keyboard, WebBrowser web_browser)
146 ECommerceApplet e_commerce) 149 : controller{std::move(controller)}, e_commerce{std::move(e_commerce)}, error{std::move(error)},
147 : parental_controls{std::move(parental_controls)}, error{std::move(error)}, 150 parental_controls{std::move(parental_controls)}, photo_viewer{std::move(photo_viewer)},
148 photo_viewer{std::move(photo_viewer)}, profile_select{std::move(profile_select)}, 151 profile_select{std::move(profile_select)}, software_keyboard{std::move(software_keyboard)},
149 software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)}, 152 web_browser{std::move(web_browser)} {}
150 e_commerce{std::move(e_commerce)} {}
151 153
152AppletFrontendSet::~AppletFrontendSet() = default; 154AppletFrontendSet::~AppletFrontendSet() = default;
153 155
@@ -164,20 +166,37 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
164} 166}
165 167
166void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { 168void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
167 if (set.parental_controls != nullptr) 169 if (set.controller != nullptr) {
168 frontend.parental_controls = std::move(set.parental_controls); 170 frontend.controller = std::move(set.controller);
169 if (set.error != nullptr) 171 }
172
173 if (set.e_commerce != nullptr) {
174 frontend.e_commerce = std::move(set.e_commerce);
175 }
176
177 if (set.error != nullptr) {
170 frontend.error = std::move(set.error); 178 frontend.error = std::move(set.error);
171 if (set.photo_viewer != nullptr) 179 }
180
181 if (set.parental_controls != nullptr) {
182 frontend.parental_controls = std::move(set.parental_controls);
183 }
184
185 if (set.photo_viewer != nullptr) {
172 frontend.photo_viewer = std::move(set.photo_viewer); 186 frontend.photo_viewer = std::move(set.photo_viewer);
173 if (set.profile_select != nullptr) 187 }
188
189 if (set.profile_select != nullptr) {
174 frontend.profile_select = std::move(set.profile_select); 190 frontend.profile_select = std::move(set.profile_select);
175 if (set.software_keyboard != nullptr) 191 }
192
193 if (set.software_keyboard != nullptr) {
176 frontend.software_keyboard = std::move(set.software_keyboard); 194 frontend.software_keyboard = std::move(set.software_keyboard);
177 if (set.web_browser != nullptr) 195 }
196
197 if (set.web_browser != nullptr) {
178 frontend.web_browser = std::move(set.web_browser); 198 frontend.web_browser = std::move(set.web_browser);
179 if (set.e_commerce != nullptr) 199 }
180 frontend.e_commerce = std::move(set.e_commerce);
181} 200}
182 201
183void AppletManager::SetDefaultAppletFrontendSet() { 202void AppletManager::SetDefaultAppletFrontendSet() {
@@ -186,15 +205,24 @@ void AppletManager::SetDefaultAppletFrontendSet() {
186} 205}
187 206
188void AppletManager::SetDefaultAppletsIfMissing() { 207void AppletManager::SetDefaultAppletsIfMissing() {
189 if (frontend.parental_controls == nullptr) { 208 if (frontend.controller == nullptr) {
190 frontend.parental_controls = 209 frontend.controller =
191 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); 210 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager());
211 }
212
213 if (frontend.e_commerce == nullptr) {
214 frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
192 } 215 }
193 216
194 if (frontend.error == nullptr) { 217 if (frontend.error == nullptr) {
195 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); 218 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
196 } 219 }
197 220
221 if (frontend.parental_controls == nullptr) {
222 frontend.parental_controls =
223 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
224 }
225
198 if (frontend.photo_viewer == nullptr) { 226 if (frontend.photo_viewer == nullptr) {
199 frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); 227 frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
200 } 228 }
@@ -211,10 +239,6 @@ void AppletManager::SetDefaultAppletsIfMissing() {
211 if (frontend.web_browser == nullptr) { 239 if (frontend.web_browser == nullptr) {
212 frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); 240 frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
213 } 241 }
214
215 if (frontend.e_commerce == nullptr) {
216 frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
217 }
218} 242}
219 243
220void AppletManager::ClearAll() { 244void AppletManager::ClearAll() {
@@ -225,6 +249,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
225 switch (id) { 249 switch (id) {
226 case AppletId::Auth: 250 case AppletId::Auth:
227 return std::make_shared<Auth>(system, *frontend.parental_controls); 251 return std::make_shared<Auth>(system, *frontend.parental_controls);
252 case AppletId::Controller:
253 return std::make_shared<Controller>(system, *frontend.controller);
228 case AppletId::Error: 254 case AppletId::Error:
229 return std::make_shared<Error>(system, *frontend.error); 255 return std::make_shared<Error>(system, *frontend.error);
230 case AppletId::ProfileSelect: 256 case AppletId::ProfileSelect:
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index e75be86a2..a1f4cf897 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -17,6 +17,7 @@ class System;
17} 17}
18 18
19namespace Core::Frontend { 19namespace Core::Frontend {
20class ControllerApplet;
20class ECommerceApplet; 21class ECommerceApplet;
21class ErrorApplet; 22class ErrorApplet;
22class ParentalControlsApplet; 23class ParentalControlsApplet;
@@ -155,19 +156,20 @@ protected:
155}; 156};
156 157
157struct AppletFrontendSet { 158struct AppletFrontendSet {
158 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; 159 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
160 using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
159 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; 161 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
162 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
160 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; 163 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
161 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; 164 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
162 using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>; 165 using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
163 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; 166 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
164 using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
165 167
166 AppletFrontendSet(); 168 AppletFrontendSet();
167 AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error, 169 AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce, ErrorApplet error,
168 PhotoViewer photo_viewer, ProfileSelect profile_select, 170 ParentalControlsApplet parental_controls, PhotoViewer photo_viewer,
169 SoftwareKeyboard software_keyboard, WebBrowser web_browser, 171 ProfileSelect profile_select, SoftwareKeyboard software_keyboard,
170 ECommerceApplet e_commerce); 172 WebBrowser web_browser);
171 ~AppletFrontendSet(); 173 ~AppletFrontendSet();
172 174
173 AppletFrontendSet(const AppletFrontendSet&) = delete; 175 AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -176,13 +178,14 @@ struct AppletFrontendSet {
176 AppletFrontendSet(AppletFrontendSet&&) noexcept; 178 AppletFrontendSet(AppletFrontendSet&&) noexcept;
177 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; 179 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
178 180
179 ParentalControlsApplet parental_controls; 181 ControllerApplet controller;
182 ECommerceApplet e_commerce;
180 ErrorApplet error; 183 ErrorApplet error;
184 ParentalControlsApplet parental_controls;
181 PhotoViewer photo_viewer; 185 PhotoViewer photo_viewer;
182 ProfileSelect profile_select; 186 ProfileSelect profile_select;
183 SoftwareKeyboard software_keyboard; 187 SoftwareKeyboard software_keyboard;
184 WebBrowser web_browser; 188 WebBrowser web_browser;
185 ECommerceApplet e_commerce;
186}; 189};
187 190
188class AppletManager { 191class AppletManager {
diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp
new file mode 100644
index 000000000..2151da783
--- /dev/null
+++ b/src/core/hle/service/am/applets/controller.cpp
@@ -0,0 +1,210 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cstring>
7
8#include "common/assert.h"
9#include "common/logging/log.h"
10#include "common/string_util.h"
11#include "core/core.h"
12#include "core/frontend/applets/controller.h"
13#include "core/hle/result.h"
14#include "core/hle/service/am/am.h"
15#include "core/hle/service/am/applets/controller.h"
16#include "core/hle/service/hid/controllers/npad.h"
17
18namespace Service::AM::Applets {
19
20// This error code (0x183ACA) is thrown when the applet fails to initialize.
21[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101};
22// This error code (0x183CCA) is thrown when the u32 result in ControllerSupportResultInfo is 2.
23[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102};
24
25static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
26 ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
27 std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
28 HID::Controller_NPad::NPadType npad_style_set;
29 npad_style_set.raw = private_arg.style_set;
30
31 return {
32 .min_players = std::max(s8(1), header.player_count_min),
33 .max_players = header.player_count_max,
34 .keep_controllers_connected = header.enable_take_over_connection,
35 .enable_single_mode = header.enable_single_mode,
36 .enable_border_color = header.enable_identification_color,
37 .border_colors = identification_colors,
38 .enable_explain_text = enable_text,
39 .explain_text = text,
40 .allow_pro_controller = npad_style_set.pro_controller == 1,
41 .allow_handheld = npad_style_set.handheld == 1,
42 .allow_dual_joycons = npad_style_set.joycon_dual == 1,
43 .allow_left_joycon = npad_style_set.joycon_left == 1,
44 .allow_right_joycon = npad_style_set.joycon_right == 1,
45 };
46}
47
48Controller::Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_)
49 : Applet{system_.Kernel()}, frontend(frontend_) {}
50
51Controller::~Controller() = default;
52
53void Controller::Initialize() {
54 Applet::Initialize();
55
56 LOG_INFO(Service_HID, "Initializing Controller Applet.");
57
58 LOG_DEBUG(Service_HID,
59 "Initializing Applet with common_args: arg_version={}, lib_version={}, "
60 "play_startup_sound={}, size={}, system_tick={}, theme_color={}",
61 common_args.arguments_version, common_args.library_version,
62 common_args.play_startup_sound, common_args.size, common_args.system_tick,
63 common_args.theme_color);
64
65 library_applet_version = LibraryAppletVersion{common_args.library_version};
66
67 const auto private_arg_storage = broker.PopNormalDataToApplet();
68 ASSERT(private_arg_storage != nullptr);
69
70 const auto& private_arg = private_arg_storage->GetData();
71 ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate));
72
73 std::memcpy(&controller_private_arg, private_arg.data(), sizeof(ControllerSupportArgPrivate));
74 ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate),
75 "Unknown ControllerSupportArgPrivate revision={} with size={}",
76 library_applet_version, controller_private_arg.arg_private_size);
77
78 switch (controller_private_arg.mode) {
79 case ControllerSupportMode::ShowControllerSupport: {
80 const auto user_arg_storage = broker.PopNormalDataToApplet();
81 ASSERT(user_arg_storage != nullptr);
82
83 const auto& user_arg = user_arg_storage->GetData();
84 switch (library_applet_version) {
85 case LibraryAppletVersion::Version3:
86 case LibraryAppletVersion::Version4:
87 case LibraryAppletVersion::Version5:
88 ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld));
89 std::memcpy(&controller_user_arg_old, user_arg.data(), sizeof(ControllerSupportArgOld));
90 break;
91 case LibraryAppletVersion::Version7:
92 ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew));
93 std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
94 break;
95 default:
96 UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}",
97 library_applet_version, controller_private_arg.arg_size);
98 ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew));
99 std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
100 break;
101 }
102 break;
103 }
104 case ControllerSupportMode::ShowControllerStrapGuide:
105 case ControllerSupportMode::ShowControllerFirmwareUpdate:
106 default: {
107 UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode);
108 break;
109 }
110 }
111}
112
113bool Controller::TransactionComplete() const {
114 return complete;
115}
116
117ResultCode Controller::GetStatus() const {
118 return status;
119}
120
121void Controller::ExecuteInteractive() {
122 UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
123}
124
125void Controller::Execute() {
126 switch (controller_private_arg.mode) {
127 case ControllerSupportMode::ShowControllerSupport: {
128 const auto parameters = [this] {
129 switch (library_applet_version) {
130 case LibraryAppletVersion::Version3:
131 case LibraryAppletVersion::Version4:
132 case LibraryAppletVersion::Version5:
133 return ConvertToFrontendParameters(
134 controller_private_arg, controller_user_arg_old.header,
135 controller_user_arg_old.enable_explain_text,
136 std::vector<IdentificationColor>(
137 controller_user_arg_old.identification_colors.begin(),
138 controller_user_arg_old.identification_colors.end()),
139 std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(),
140 controller_user_arg_old.explain_text.end()));
141 case LibraryAppletVersion::Version7:
142 default:
143 return ConvertToFrontendParameters(
144 controller_private_arg, controller_user_arg_new.header,
145 controller_user_arg_new.enable_explain_text,
146 std::vector<IdentificationColor>(
147 controller_user_arg_new.identification_colors.begin(),
148 controller_user_arg_new.identification_colors.end()),
149 std::vector<ExplainText>(controller_user_arg_new.explain_text.begin(),
150 controller_user_arg_new.explain_text.end()));
151 }
152 }();
153
154 is_single_mode = parameters.enable_single_mode;
155
156 LOG_DEBUG(Service_HID,
157 "Controller Parameters: min_players={}, max_players={}, "
158 "keep_controllers_connected={}, enable_single_mode={}, enable_border_color={}, "
159 "enable_explain_text={}, allow_pro_controller={}, allow_handheld={}, "
160 "allow_dual_joycons={}, allow_left_joycon={}, allow_right_joycon={}",
161 parameters.min_players, parameters.max_players,
162 parameters.keep_controllers_connected, parameters.enable_single_mode,
163 parameters.enable_border_color, parameters.enable_explain_text,
164 parameters.allow_pro_controller, parameters.allow_handheld,
165 parameters.allow_dual_joycons, parameters.allow_left_joycon,
166 parameters.allow_right_joycon);
167
168 frontend.ReconfigureControllers([this] { ConfigurationComplete(); }, parameters);
169 break;
170 }
171 case ControllerSupportMode::ShowControllerStrapGuide:
172 case ControllerSupportMode::ShowControllerFirmwareUpdate:
173 default: {
174 ConfigurationComplete();
175 break;
176 }
177 }
178}
179
180void Controller::ConfigurationComplete() {
181 ControllerSupportResultInfo result_info{};
182
183 const auto& players = Settings::values.players;
184
185 // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
186 // Otherwise, only count connected players from P1-P8.
187 result_info.player_count =
188 is_single_mode ? 1
189 : static_cast<s8>(std::count_if(
190 players.begin(), players.end() - 2,
191 [](Settings::PlayerInput player) { return player.connected; }));
192
193 result_info.selected_id = HID::Controller_NPad::IndexToNPad(
194 std::distance(players.begin(),
195 std::find_if(players.begin(), players.end(),
196 [](Settings::PlayerInput player) { return player.connected; })));
197
198 result_info.result = 0;
199
200 LOG_DEBUG(Service_HID, "Result Info: player_count={}, selected_id={}, result={}",
201 result_info.player_count, result_info.selected_id, result_info.result);
202
203 complete = true;
204 out_data = std::vector<u8>(sizeof(ControllerSupportResultInfo));
205 std::memcpy(out_data.data(), &result_info, out_data.size());
206 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(out_data)));
207 broker.SignalStateChanged();
208}
209
210} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/controller.h b/src/core/hle/service/am/applets/controller.h
new file mode 100644
index 000000000..f7bb3fba9
--- /dev/null
+++ b/src/core/hle/service/am/applets/controller.h
@@ -0,0 +1,123 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <vector>
9
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "core/hle/result.h"
13#include "core/hle/service/am/applets/applets.h"
14
15namespace Core {
16class System;
17}
18
19namespace Service::AM::Applets {
20
21using IdentificationColor = std::array<u8, 4>;
22using ExplainText = std::array<char, 0x81>;
23
24enum class LibraryAppletVersion : u32_le {
25 Version3 = 0x3, // 1.0.0 - 2.3.0
26 Version4 = 0x4, // 3.0.0 - 5.1.0
27 Version5 = 0x5, // 6.0.0 - 7.0.1
28 Version7 = 0x7, // 8.0.0+
29};
30
31enum class ControllerSupportMode : u8 {
32 ShowControllerSupport = 0,
33 ShowControllerStrapGuide = 1,
34 ShowControllerFirmwareUpdate = 2,
35};
36
37enum class ControllerSupportCaller : u8 {
38 Application = 0,
39 System = 1,
40};
41
42struct ControllerSupportArgPrivate {
43 u32 arg_private_size{};
44 u32 arg_size{};
45 bool flag_0{};
46 bool flag_1{};
47 ControllerSupportMode mode{};
48 ControllerSupportCaller caller{};
49 u32 style_set{};
50 u32 joy_hold_type{};
51};
52static_assert(sizeof(ControllerSupportArgPrivate) == 0x14,
53 "ControllerSupportArgPrivate has incorrect size.");
54
55struct ControllerSupportArgHeader {
56 s8 player_count_min{};
57 s8 player_count_max{};
58 bool enable_take_over_connection{};
59 bool enable_left_justify{};
60 bool enable_permit_joy_dual{};
61 bool enable_single_mode{};
62 bool enable_identification_color{};
63};
64static_assert(sizeof(ControllerSupportArgHeader) == 0x7,
65 "ControllerSupportArgHeader has incorrect size.");
66
67// LibraryAppletVersion 0x3, 0x4, 0x5
68struct ControllerSupportArgOld {
69 ControllerSupportArgHeader header{};
70 std::array<IdentificationColor, 4> identification_colors{};
71 bool enable_explain_text{};
72 std::array<ExplainText, 4> explain_text{};
73};
74static_assert(sizeof(ControllerSupportArgOld) == 0x21C,
75 "ControllerSupportArgOld has incorrect size.");
76
77// LibraryAppletVersion 0x7
78struct ControllerSupportArgNew {
79 ControllerSupportArgHeader header{};
80 std::array<IdentificationColor, 8> identification_colors{};
81 bool enable_explain_text{};
82 std::array<ExplainText, 8> explain_text{};
83};
84static_assert(sizeof(ControllerSupportArgNew) == 0x430,
85 "ControllerSupportArgNew has incorrect size.");
86
87struct ControllerSupportResultInfo {
88 s8 player_count{};
89 INSERT_PADDING_BYTES(3);
90 u32 selected_id{};
91 u32 result{};
92};
93static_assert(sizeof(ControllerSupportResultInfo) == 0xC,
94 "ControllerSupportResultInfo has incorrect size.");
95
96class Controller final : public Applet {
97public:
98 explicit Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_);
99 ~Controller() override;
100
101 void Initialize() override;
102
103 bool TransactionComplete() const override;
104 ResultCode GetStatus() const override;
105 void ExecuteInteractive() override;
106 void Execute() override;
107
108 void ConfigurationComplete();
109
110private:
111 const Core::Frontend::ControllerApplet& frontend;
112
113 LibraryAppletVersion library_applet_version;
114 ControllerSupportArgPrivate controller_private_arg;
115 ControllerSupportArgOld controller_user_arg_old;
116 ControllerSupportArgNew controller_user_arg_new;
117 bool complete{false};
118 ResultCode status{RESULT_SUCCESS};
119 bool is_single_mode{false};
120 std::vector<u8> out_data;
121};
122
123} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index fbe3686ae..bdeb0737a 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -13,11 +13,23 @@
13 13
14namespace Service::AM::Applets { 14namespace Service::AM::Applets {
15 15
16namespace {
17enum class Request : u32 {
18 Finalize = 0x4,
19 SetUserWordInfo = 0x6,
20 SetCustomizeDic = 0x7,
21 Calc = 0xa,
22 SetCustomizedDictionaries = 0xb,
23 UnsetCustomizedDictionaries = 0xc,
24 UnknownD = 0xd,
25 UnknownE = 0xe,
26};
27constexpr std::size_t SWKBD_INLINE_INIT_SIZE = 0x8;
16constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; 28constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8;
17constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4; 29constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4;
18constexpr std::size_t DEFAULT_MAX_LENGTH = 500; 30constexpr std::size_t DEFAULT_MAX_LENGTH = 500;
19constexpr bool INTERACTIVE_STATUS_OK = false; 31constexpr bool INTERACTIVE_STATUS_OK = false;
20 32} // Anonymous namespace
21static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( 33static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
22 KeyboardConfig config, std::u16string initial_text) { 34 KeyboardConfig config, std::u16string initial_text) {
23 Core::Frontend::SoftwareKeyboardParameters params{}; 35 Core::Frontend::SoftwareKeyboardParameters params{};
@@ -47,6 +59,7 @@ SoftwareKeyboard::~SoftwareKeyboard() = default;
47 59
48void SoftwareKeyboard::Initialize() { 60void SoftwareKeyboard::Initialize() {
49 complete = false; 61 complete = false;
62 is_inline = false;
50 initial_text.clear(); 63 initial_text.clear();
51 final_data.clear(); 64 final_data.clear();
52 65
@@ -56,6 +69,11 @@ void SoftwareKeyboard::Initialize() {
56 ASSERT(keyboard_config_storage != nullptr); 69 ASSERT(keyboard_config_storage != nullptr);
57 const auto& keyboard_config = keyboard_config_storage->GetData(); 70 const auto& keyboard_config = keyboard_config_storage->GetData();
58 71
72 if (keyboard_config.size() == SWKBD_INLINE_INIT_SIZE) {
73 is_inline = true;
74 return;
75 }
76
59 ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); 77 ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
60 std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); 78 std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
61 79
@@ -87,16 +105,31 @@ void SoftwareKeyboard::ExecuteInteractive() {
87 const auto storage = broker.PopInteractiveDataToApplet(); 105 const auto storage = broker.PopInteractiveDataToApplet();
88 ASSERT(storage != nullptr); 106 ASSERT(storage != nullptr);
89 const auto data = storage->GetData(); 107 const auto data = storage->GetData();
90 const auto status = static_cast<bool>(data[0]); 108 if (!is_inline) {
91 109 const auto status = static_cast<bool>(data[0]);
92 if (status == INTERACTIVE_STATUS_OK) { 110 if (status == INTERACTIVE_STATUS_OK) {
93 complete = true; 111 complete = true;
112 } else {
113 std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
114 std::memcpy(string.data(), data.data() + 4, string.size() * 2);
115 frontend.SendTextCheckDialog(
116 Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
117 [this] { broker.SignalStateChanged(); });
118 }
94 } else { 119 } else {
95 std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; 120 Request request{};
96 std::memcpy(string.data(), data.data() + 4, string.size() * 2); 121 std::memcpy(&request, data.data(), sizeof(Request));
97 frontend.SendTextCheckDialog( 122
98 Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), 123 switch (request) {
99 [this] { broker.SignalStateChanged(); }); 124 case Request::Calc: {
125 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{1}));
126 broker.SignalStateChanged();
127 break;
128 }
129 default:
130 UNIMPLEMENTED_MSG("Request {:X} is not implemented", request);
131 break;
132 }
100 } 133 }
101} 134}
102 135
@@ -108,9 +141,10 @@ void SoftwareKeyboard::Execute() {
108 } 141 }
109 142
110 const auto parameters = ConvertToFrontendParameters(config, initial_text); 143 const auto parameters = ConvertToFrontendParameters(config, initial_text);
111 144 if (!is_inline) {
112 frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, 145 frontend.RequestText(
113 parameters); 146 [this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, parameters);
147 }
114} 148}
115 149
116void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { 150void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h
index ef4801fc6..5a3824b5a 100644
--- a/src/core/hle/service/am/applets/software_keyboard.h
+++ b/src/core/hle/service/am/applets/software_keyboard.h
@@ -78,6 +78,7 @@ private:
78 KeyboardConfig config; 78 KeyboardConfig config;
79 std::u16string initial_text; 79 std::u16string initial_text;
80 bool complete = false; 80 bool complete = false;
81 bool is_inline = false;
81 std::vector<u8> final_data; 82 std::vector<u8> final_data;
82}; 83};
83 84
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 9f30e167d..efe595c4f 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -293,8 +293,8 @@ void WebBrowser::Finalize() {
293 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data))); 293 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data)));
294 broker.SignalStateChanged(); 294 broker.SignalStateChanged();
295 295
296 if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) { 296 if (!temporary_dir.empty() && Common::FS::IsDirectory(temporary_dir)) {
297 FileUtil::DeleteDirRecursively(temporary_dir); 297 Common::FS::DeleteDirRecursively(temporary_dir);
298 } 298 }
299} 299}
300 300
@@ -452,10 +452,10 @@ void WebBrowser::InitializeOffline() {
452 }; 452 };
453 453
454 temporary_dir = 454 temporary_dir =
455 FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + "web_applet_" + 455 Common::FS::SanitizePath(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
456 WEB_SOURCE_NAMES[static_cast<u32>(source) - 1], 456 "web_applet_" + WEB_SOURCE_NAMES[static_cast<u32>(source) - 1],
457 FileUtil::DirectorySeparator::PlatformDefault); 457 Common::FS::DirectorySeparator::PlatformDefault);
458 FileUtil::DeleteDirRecursively(temporary_dir); 458 Common::FS::DeleteDirRecursively(temporary_dir);
459 459
460 u64 title_id = 0; // 0 corresponds to current process 460 u64 title_id = 0; // 0 corresponds to current process
461 ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8); 461 ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8);
@@ -492,8 +492,8 @@ void WebBrowser::InitializeOffline() {
492 } 492 }
493 493
494 filename = 494 filename =
495 FileUtil::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename, 495 Common::FS::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename,
496 FileUtil::DirectorySeparator::PlatformDefault); 496 Common::FS::DirectorySeparator::PlatformDefault);
497} 497}
498 498
499void WebBrowser::ExecuteShop() { 499void WebBrowser::ExecuteShop() {
@@ -551,7 +551,8 @@ void WebBrowser::ExecuteShop() {
551} 551}
552 552
553void WebBrowser::ExecuteOffline() { 553void WebBrowser::ExecuteOffline() {
554 frontend.OpenPageLocal(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); 554 frontend.OpenPageLocal(
555 filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
555} 556}
556 557
557} // namespace Service::AM::Applets 558} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 106e89743..9b4910e53 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -71,7 +71,7 @@ public:
71 71
72 stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, 72 stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
73 audio_params.channel_count, std::move(unique_name), 73 audio_params.channel_count, std::move(unique_name),
74 [=]() { buffer_event.writable->Signal(); }); 74 [this] { buffer_event.writable->Signal(); });
75 } 75 }
76 76
77private: 77private:
@@ -206,7 +206,7 @@ private:
206 AudioCore::StreamPtr stream; 206 AudioCore::StreamPtr stream;
207 std::string device_name; 207 std::string device_name;
208 208
209 [[maybe_unused]] AudoutParams audio_params {}; 209 [[maybe_unused]] AudoutParams audio_params{};
210 210
211 /// This is the event handle used to check if the audio buffer was released 211 /// This is the event handle used to check if the audio buffer was released
212 Kernel::EventPair buffer_event; 212 Kernel::EventPair buffer_event;
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index d8359abaa..a2d3ded7b 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -26,7 +26,7 @@ namespace Service::Audio {
26 26
27class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { 27class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
28public: 28public:
29 explicit IAudioRenderer(Core::System& system, AudioCore::AudioRendererParameter audren_params, 29 explicit IAudioRenderer(Core::System& system, AudioCommon::AudioRendererParameter audren_params,
30 const std::size_t instance_number) 30 const std::size_t instance_number)
31 : ServiceFramework("IAudioRenderer") { 31 : ServiceFramework("IAudioRenderer") {
32 // clang-format off 32 // clang-format off
@@ -94,14 +94,15 @@ private:
94 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { 94 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
95 LOG_DEBUG(Service_Audio, "(STUBBED) called"); 95 LOG_DEBUG(Service_Audio, "(STUBBED) called");
96 96
97 auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer()); 97 std::vector<u8> output_params(ctx.GetWriteBufferSize());
98 auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params);
98 99
99 if (result.Succeeded()) { 100 if (result.IsSuccess()) {
100 ctx.WriteBuffer(result.Unwrap()); 101 ctx.WriteBuffer(output_params);
101 } 102 }
102 103
103 IPC::ResponseBuilder rb{ctx, 2}; 104 IPC::ResponseBuilder rb{ctx, 2};
104 rb.Push(result.Code()); 105 rb.Push(result);
105 } 106 }
106 107
107 void Start(Kernel::HLERequestContext& ctx) { 108 void Start(Kernel::HLERequestContext& ctx) {
@@ -346,7 +347,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
346 OpenAudioRendererImpl(ctx); 347 OpenAudioRendererImpl(ctx);
347} 348}
348 349
349static u64 CalculateNumPerformanceEntries(const AudioCore::AudioRendererParameter& params) { 350static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) {
350 // +1 represents the final mix. 351 // +1 represents the final mix.
351 return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count + 352 return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
352 1; 353 1;
@@ -375,7 +376,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
375 constexpr u64 upsampler_manager_size = 0x48; 376 constexpr u64 upsampler_manager_size = 0x48;
376 377
377 // Calculates the part of the size that relates to mix buffers. 378 // Calculates the part of the size that relates to mix buffers.
378 const auto calculate_mix_buffer_sizes = [](const AudioCore::AudioRendererParameter& params) { 379 const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) {
379 // As of 8.0.0 this is the maximum on voice channels. 380 // As of 8.0.0 this is the maximum on voice channels.
380 constexpr u64 max_voice_channels = 6; 381 constexpr u64 max_voice_channels = 6;
381 382
@@ -397,7 +398,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
397 }; 398 };
398 399
399 // Calculates the portion of the size related to the mix data (and the sorting thereof). 400 // Calculates the portion of the size related to the mix data (and the sorting thereof).
400 const auto calculate_mix_info_size = [](const AudioCore::AudioRendererParameter& params) { 401 const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) {
401 // The size of the mixing info data structure. 402 // The size of the mixing info data structure.
402 constexpr u64 mix_info_size = 0x940; 403 constexpr u64 mix_info_size = 0x940;
403 404
@@ -447,7 +448,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
447 }; 448 };
448 449
449 // Calculates the part of the size related to voice channel info. 450 // Calculates the part of the size related to voice channel info.
450 const auto calculate_voice_info_size = [](const AudioCore::AudioRendererParameter& params) { 451 const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) {
451 constexpr u64 voice_info_size = 0x220; 452 constexpr u64 voice_info_size = 0x220;
452 constexpr u64 voice_resource_size = 0xD0; 453 constexpr u64 voice_resource_size = 0xD0;
453 454
@@ -461,7 +462,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
461 }; 462 };
462 463
463 // Calculates the part of the size related to memory pools. 464 // Calculates the part of the size related to memory pools.
464 const auto calculate_memory_pools_size = [](const AudioCore::AudioRendererParameter& params) { 465 const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) {
465 const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count); 466 const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
466 const u64 memory_pool_info_size = 0x20; 467 const u64 memory_pool_info_size = 0x20;
467 return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size); 468 return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
@@ -469,7 +470,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
469 470
470 // Calculates the part of the size related to the splitter context. 471 // Calculates the part of the size related to the splitter context.
471 const auto calculate_splitter_context_size = 472 const auto calculate_splitter_context_size =
472 [](const AudioCore::AudioRendererParameter& params) -> u64 { 473 [](const AudioCommon::AudioRendererParameter& params) -> u64 {
473 if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { 474 if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
474 return 0; 475 return 0;
475 } 476 }
@@ -488,27 +489,29 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
488 }; 489 };
489 490
490 // Calculates the part of the size related to the upsampler info. 491 // Calculates the part of the size related to the upsampler info.
491 const auto calculate_upsampler_info_size = [](const AudioCore::AudioRendererParameter& params) { 492 const auto calculate_upsampler_info_size =
492 constexpr u64 upsampler_info_size = 0x280; 493 [](const AudioCommon::AudioRendererParameter& params) {
493 // Yes, using the buffer size over info alignment size is intentional here. 494 constexpr u64 upsampler_info_size = 0x280;
494 return Common::AlignUp(upsampler_info_size * (u64{params.submix_count} + params.sink_count), 495 // Yes, using the buffer size over info alignment size is intentional here.
495 buffer_alignment_size); 496 return Common::AlignUp(upsampler_info_size *
496 }; 497 (u64{params.submix_count} + params.sink_count),
498 buffer_alignment_size);
499 };
497 500
498 // Calculates the part of the size related to effect info. 501 // Calculates the part of the size related to effect info.
499 const auto calculate_effect_info_size = [](const AudioCore::AudioRendererParameter& params) { 502 const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) {
500 constexpr u64 effect_info_size = 0x2B0; 503 constexpr u64 effect_info_size = 0x2B0;
501 return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size); 504 return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
502 }; 505 };
503 506
504 // Calculates the part of the size related to audio sink info. 507 // Calculates the part of the size related to audio sink info.
505 const auto calculate_sink_info_size = [](const AudioCore::AudioRendererParameter& params) { 508 const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) {
506 const u64 sink_info_size = 0x170; 509 const u64 sink_info_size = 0x170;
507 return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size); 510 return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
508 }; 511 };
509 512
510 // Calculates the part of the size related to voice state info. 513 // Calculates the part of the size related to voice state info.
511 const auto calculate_voice_state_size = [](const AudioCore::AudioRendererParameter& params) { 514 const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) {
512 const u64 voice_state_size = 0x100; 515 const u64 voice_state_size = 0x100;
513 const u64 additional_size = buffer_alignment_size - 1; 516 const u64 additional_size = buffer_alignment_size - 1;
514 return Common::AlignUp(voice_state_size * params.voice_count + additional_size, 517 return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
@@ -516,7 +519,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
516 }; 519 };
517 520
518 // Calculates the part of the size related to performance statistics. 521 // Calculates the part of the size related to performance statistics.
519 const auto calculate_perf_size = [](const AudioCore::AudioRendererParameter& params) { 522 const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) {
520 // Extra size value appended to the end of the calculation. 523 // Extra size value appended to the end of the calculation.
521 constexpr u64 appended = 128; 524 constexpr u64 appended = 128;
522 525
@@ -543,79 +546,81 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
543 }; 546 };
544 547
545 // Calculates the part of the size that relates to the audio command buffer. 548 // Calculates the part of the size that relates to the audio command buffer.
546 const auto calculate_command_buffer_size = [](const AudioCore::AudioRendererParameter& params) { 549 const auto calculate_command_buffer_size =
547 constexpr u64 alignment = (buffer_alignment_size - 1) * 2; 550 [](const AudioCommon::AudioRendererParameter& params) {
551 constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
548 552
549 if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { 553 if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
550 constexpr u64 command_buffer_size = 0x18000; 554 constexpr u64 command_buffer_size = 0x18000;
551 555
552 return command_buffer_size + alignment; 556 return command_buffer_size + alignment;
553 } 557 }
554 558
555 // When the variadic command buffer is supported, this means 559 // When the variadic command buffer is supported, this means
556 // the command generator for the audio renderer can issue commands 560 // the command generator for the audio renderer can issue commands
557 // that are (as one would expect), variable in size. So what we need to do 561 // that are (as one would expect), variable in size. So what we need to do
558 // is determine the maximum possible size for a few command data structures 562 // is determine the maximum possible size for a few command data structures
559 // then multiply them by the amount of present commands indicated by the given 563 // then multiply them by the amount of present commands indicated by the given
560 // respective audio parameters. 564 // respective audio parameters.
561 565
562 constexpr u64 max_biquad_filters = 2; 566 constexpr u64 max_biquad_filters = 2;
563 constexpr u64 max_mix_buffers = 24; 567 constexpr u64 max_mix_buffers = 24;
564 568
565 constexpr u64 biquad_filter_command_size = 0x2C; 569 constexpr u64 biquad_filter_command_size = 0x2C;
566 570
567 constexpr u64 depop_mix_command_size = 0x24; 571 constexpr u64 depop_mix_command_size = 0x24;
568 constexpr u64 depop_setup_command_size = 0x50; 572 constexpr u64 depop_setup_command_size = 0x50;
569 573
570 constexpr u64 effect_command_max_size = 0x540; 574 constexpr u64 effect_command_max_size = 0x540;
571 575
572 constexpr u64 mix_command_size = 0x1C; 576 constexpr u64 mix_command_size = 0x1C;
573 constexpr u64 mix_ramp_command_size = 0x24; 577 constexpr u64 mix_ramp_command_size = 0x24;
574 constexpr u64 mix_ramp_grouped_command_size = 0x13C; 578 constexpr u64 mix_ramp_grouped_command_size = 0x13C;
575 579
576 constexpr u64 perf_command_size = 0x28; 580 constexpr u64 perf_command_size = 0x28;
577 581
578 constexpr u64 sink_command_size = 0x130; 582 constexpr u64 sink_command_size = 0x130;
579 583
580 constexpr u64 submix_command_max_size = 584 constexpr u64 submix_command_max_size =
581 depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; 585 depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
582 586
583 constexpr u64 volume_command_size = 0x1C; 587 constexpr u64 volume_command_size = 0x1C;
584 constexpr u64 volume_ramp_command_size = 0x20; 588 constexpr u64 volume_ramp_command_size = 0x20;
585 589
586 constexpr u64 voice_biquad_filter_command_size = 590 constexpr u64 voice_biquad_filter_command_size =
587 biquad_filter_command_size * max_biquad_filters; 591 biquad_filter_command_size * max_biquad_filters;
588 constexpr u64 voice_data_command_size = 0x9C; 592 constexpr u64 voice_data_command_size = 0x9C;
589 const u64 voice_command_max_size = 593 const u64 voice_command_max_size =
590 (params.splitter_count * depop_setup_command_size) + 594 (params.splitter_count * depop_setup_command_size) +
591 (voice_data_command_size + voice_biquad_filter_command_size + volume_ramp_command_size + 595 (voice_data_command_size + voice_biquad_filter_command_size +
592 mix_ramp_grouped_command_size); 596 volume_ramp_command_size + mix_ramp_grouped_command_size);
593 597
594 // Now calculate the individual elements that comprise the size and add them together. 598 // Now calculate the individual elements that comprise the size and add them together.
595 const u64 effect_commands_size = params.effect_count * effect_command_max_size; 599 const u64 effect_commands_size = params.effect_count * effect_command_max_size;
596 600
597 const u64 final_mix_commands_size = 601 const u64 final_mix_commands_size =
598 depop_mix_command_size + volume_command_size * max_mix_buffers; 602 depop_mix_command_size + volume_command_size * max_mix_buffers;
599 603
600 const u64 perf_commands_size = 604 const u64 perf_commands_size =
601 perf_command_size * (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); 605 perf_command_size *
606 (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
602 607
603 const u64 sink_commands_size = params.sink_count * sink_command_size; 608 const u64 sink_commands_size = params.sink_count * sink_command_size;
604 609
605 const u64 splitter_commands_size = 610 const u64 splitter_commands_size =
606 params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; 611 params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
607 612
608 const u64 submix_commands_size = params.submix_count * submix_command_max_size; 613 const u64 submix_commands_size = params.submix_count * submix_command_max_size;
609 614
610 const u64 voice_commands_size = params.voice_count * voice_command_max_size; 615 const u64 voice_commands_size = params.voice_count * voice_command_max_size;
611 616
612 return effect_commands_size + final_mix_commands_size + perf_commands_size + 617 return effect_commands_size + final_mix_commands_size + perf_commands_size +
613 sink_commands_size + splitter_commands_size + submix_commands_size + 618 sink_commands_size + splitter_commands_size + submix_commands_size +
614 voice_commands_size + alignment; 619 voice_commands_size + alignment;
615 }; 620 };
616 621
617 IPC::RequestParser rp{ctx}; 622 IPC::RequestParser rp{ctx};
618 const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); 623 const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
619 624
620 u64 size = 0; 625 u64 size = 0;
621 size += calculate_mix_buffer_sizes(params); 626 size += calculate_mix_buffer_sizes(params);
@@ -681,7 +686,7 @@ void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& c
681 686
682void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { 687void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
683 IPC::RequestParser rp{ctx}; 688 IPC::RequestParser rp{ctx};
684 const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); 689 const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
685 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 690 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
686 691
687 rb.Push(RESULT_SUCCESS); 692 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index d19513cbb..f1d81602c 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -92,7 +92,7 @@ private:
92 if (performance) { 92 if (performance) {
93 rb.Push<u64>(*performance); 93 rb.Push<u64>(*performance);
94 } 94 }
95 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); 95 ctx.WriteBuffer(samples);
96 } 96 }
97 97
98 bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input, 98 bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp
index d29e78d7e..ca021a99f 100644
--- a/src/core/hle/service/bcat/backend/boxcat.cpp
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -89,12 +89,12 @@ constexpr u32 TIMEOUT_SECONDS = 30;
89 89
90std::string GetBINFilePath(u64 title_id) { 90std::string GetBINFilePath(u64 title_id) {
91 return fmt::format("{}bcat/{:016X}/launchparam.bin", 91 return fmt::format("{}bcat/{:016X}/launchparam.bin",
92 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); 92 Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id);
93} 93}
94 94
95std::string GetZIPFilePath(u64 title_id) { 95std::string GetZIPFilePath(u64 title_id) {
96 return fmt::format("{}bcat/{:016X}/data.zip", 96 return fmt::format("{}bcat/{:016X}/data.zip",
97 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); 97 Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id);
98} 98}
99 99
100// If the error is something the user should know about (build ID mismatch, bad client version), 100// If the error is something the user should know about (build ID mismatch, bad client version),
@@ -205,8 +205,8 @@ private:
205 {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)}, 205 {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)},
206 }; 206 };
207 207
208 if (FileUtil::Exists(path)) { 208 if (Common::FS::Exists(path)) {
209 FileUtil::IOFile file{path, "rb"}; 209 Common::FS::IOFile file{path, "rb"};
210 if (file.IsOpen()) { 210 if (file.IsOpen()) {
211 std::vector<u8> bytes(file.GetSize()); 211 std::vector<u8> bytes(file.GetSize());
212 file.ReadBytes(bytes.data(), bytes.size()); 212 file.ReadBytes(bytes.data(), bytes.size());
@@ -236,8 +236,8 @@ private:
236 return DownloadResult::InvalidContentType; 236 return DownloadResult::InvalidContentType;
237 } 237 }
238 238
239 FileUtil::CreateFullPath(path); 239 Common::FS::CreateFullPath(path);
240 FileUtil::IOFile file{path, "wb"}; 240 Common::FS::IOFile file{path, "wb"};
241 if (!file.IsOpen()) 241 if (!file.IsOpen())
242 return DownloadResult::GeneralFSError; 242 return DownloadResult::GeneralFSError;
243 if (!file.Resize(response->body.size())) 243 if (!file.Resize(response->body.size()))
@@ -290,7 +290,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
290 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); 290 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
291 291
292 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { 292 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
293 FileUtil::Delete(zip_path); 293 Common::FS::Delete(zip_path);
294 } 294 }
295 295
296 HandleDownloadDisplayResult(applet_manager, res); 296 HandleDownloadDisplayResult(applet_manager, res);
@@ -300,7 +300,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
300 300
301 progress.StartProcessingDataList(); 301 progress.StartProcessingDataList();
302 302
303 FileUtil::IOFile zip{zip_path, "rb"}; 303 Common::FS::IOFile zip{zip_path, "rb"};
304 const auto size = zip.GetSize(); 304 const auto size = zip.GetSize();
305 std::vector<u8> bytes(size); 305 std::vector<u8> bytes(size);
306 if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { 306 if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
@@ -365,8 +365,7 @@ bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress)
365 365
366 std::thread([this, title, &progress] { 366 std::thread([this, title, &progress] {
367 SynchronizeInternal(applet_manager, dir_getter, title, progress); 367 SynchronizeInternal(applet_manager, dir_getter, title, progress);
368 }) 368 }).detach();
369 .detach();
370 369
371 return true; 370 return true;
372} 371}
@@ -377,8 +376,7 @@ bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name,
377 376
378 std::thread([this, title, name, &progress] { 377 std::thread([this, title, name, &progress] {
379 SynchronizeInternal(applet_manager, dir_getter, title, progress, name); 378 SynchronizeInternal(applet_manager, dir_getter, title, progress, name);
380 }) 379 }).detach();
381 .detach();
382 380
383 return true; 381 return true;
384} 382}
@@ -422,7 +420,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
422 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); 420 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
423 421
424 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { 422 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
425 FileUtil::Delete(path); 423 Common::FS::Delete(path);
426 } 424 }
427 425
428 HandleDownloadDisplayResult(applet_manager, res); 426 HandleDownloadDisplayResult(applet_manager, res);
@@ -430,7 +428,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
430 } 428 }
431 } 429 }
432 430
433 FileUtil::IOFile bin{path, "rb"}; 431 Common::FS::IOFile bin{path, "rb"};
434 const auto size = bin.GetSize(); 432 const auto size = bin.GetSize();
435 std::vector<u8> bytes(size); 433 std::vector<u8> bytes(size);
436 if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { 434 if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp
index 603b64d4f..db0e06ca1 100644
--- a/src/core/hle/service/bcat/module.cpp
+++ b/src/core/hle/service/bcat/module.cpp
@@ -112,7 +112,7 @@ private:
112 void GetImpl(Kernel::HLERequestContext& ctx) { 112 void GetImpl(Kernel::HLERequestContext& ctx) {
113 LOG_DEBUG(Service_BCAT, "called"); 113 LOG_DEBUG(Service_BCAT, "called");
114 114
115 ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl)); 115 ctx.WriteBuffer(impl);
116 116
117 IPC::ResponseBuilder rb{ctx, 2}; 117 IPC::ResponseBuilder rb{ctx, 2};
118 rb.Push(RESULT_SUCCESS); 118 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp
index ab17a187e..a0ee116fa 100644
--- a/src/core/hle/service/caps/caps_c.cpp
+++ b/src/core/hle/service/caps/caps_c.cpp
@@ -2,6 +2,8 @@
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 "common/logging/log.h"
6#include "core/hle/ipc_helpers.h"
5#include "core/hle/service/caps/caps_c.h" 7#include "core/hle/service/caps/caps_c.h"
6 8
7namespace Service::Capture { 9namespace Service::Capture {
@@ -47,7 +49,7 @@ CAPS_C::CAPS_C() : ServiceFramework("caps:c") {
47 static const FunctionInfo functions[] = { 49 static const FunctionInfo functions[] = {
48 {1, nullptr, "CaptureRawImage"}, 50 {1, nullptr, "CaptureRawImage"},
49 {2, nullptr, "CaptureRawImageWithTimeout"}, 51 {2, nullptr, "CaptureRawImageWithTimeout"},
50 {33, nullptr, "Unknown33"}, 52 {33, &CAPS_C::SetShimLibraryVersion, "SetShimLibraryVersion"},
51 {1001, nullptr, "RequestTakingScreenShot"}, 53 {1001, nullptr, "RequestTakingScreenShot"},
52 {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, 54 {1002, nullptr, "RequestTakingScreenShotWithTimeout"},
53 {1011, nullptr, "NotifyTakingScreenShotRefused"}, 55 {1011, nullptr, "NotifyTakingScreenShotRefused"},
@@ -72,4 +74,16 @@ CAPS_C::CAPS_C() : ServiceFramework("caps:c") {
72 74
73CAPS_C::~CAPS_C() = default; 75CAPS_C::~CAPS_C() = default;
74 76
77void CAPS_C::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) {
78 IPC::RequestParser rp{ctx};
79 const auto library_version{rp.Pop<u64>()};
80 const auto applet_resource_user_id{rp.Pop<u64>()};
81
82 LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
83 library_version, applet_resource_user_id);
84
85 IPC::ResponseBuilder rb{ctx, 2};
86 rb.Push(RESULT_SUCCESS);
87}
88
75} // namespace Service::Capture 89} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h
index a9d028689..b110301d4 100644
--- a/src/core/hle/service/caps/caps_c.h
+++ b/src/core/hle/service/caps/caps_c.h
@@ -16,6 +16,9 @@ class CAPS_C final : public ServiceFramework<CAPS_C> {
16public: 16public:
17 explicit CAPS_C(); 17 explicit CAPS_C();
18 ~CAPS_C() override; 18 ~CAPS_C() override;
19
20private:
21 void SetShimLibraryVersion(Kernel::HLERequestContext& ctx);
19}; 22};
20 23
21} // namespace Service::Capture 24} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp
index fffb2ecf9..e386470f7 100644
--- a/src/core/hle/service/caps/caps_su.cpp
+++ b/src/core/hle/service/caps/caps_su.cpp
@@ -25,7 +25,12 @@ CAPS_SU::CAPS_SU() : ServiceFramework("caps:su") {
25CAPS_SU::~CAPS_SU() = default; 25CAPS_SU::~CAPS_SU() = default;
26 26
27void CAPS_SU::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) { 27void CAPS_SU::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) {
28 LOG_WARNING(Service_Capture, "(STUBBED) called"); 28 IPC::RequestParser rp{ctx};
29 const auto library_version{rp.Pop<u64>()};
30 const auto applet_resource_user_id{rp.Pop<u64>()};
31
32 LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
33 library_version, applet_resource_user_id);
29 34
30 IPC::ResponseBuilder rb{ctx, 2}; 35 IPC::ResponseBuilder rb{ctx, 2};
31 rb.Push(RESULT_SUCCESS); 36 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp
index f36d8de2d..8e2b83629 100644
--- a/src/core/hle/service/caps/caps_u.cpp
+++ b/src/core/hle/service/caps/caps_u.cpp
@@ -31,8 +31,7 @@ public:
31CAPS_U::CAPS_U() : ServiceFramework("caps:u") { 31CAPS_U::CAPS_U() : ServiceFramework("caps:u") {
32 // clang-format off 32 // clang-format off
33 static const FunctionInfo functions[] = { 33 static const FunctionInfo functions[] = {
34 {31, nullptr, "GetShimLibraryVersion"}, 34 {32, &CAPS_U::SetShimLibraryVersion, "SetShimLibraryVersion"},
35 {32, nullptr, "SetShimLibraryVersion"},
36 {102, &CAPS_U::GetAlbumContentsFileListForApplication, "GetAlbumContentsFileListForApplication"}, 35 {102, &CAPS_U::GetAlbumContentsFileListForApplication, "GetAlbumContentsFileListForApplication"},
37 {103, nullptr, "DeleteAlbumContentsFileForApplication"}, 36 {103, nullptr, "DeleteAlbumContentsFileForApplication"},
38 {104, nullptr, "GetAlbumContentsFileSizeForApplication"}, 37 {104, nullptr, "GetAlbumContentsFileSizeForApplication"},
@@ -53,6 +52,18 @@ CAPS_U::CAPS_U() : ServiceFramework("caps:u") {
53 52
54CAPS_U::~CAPS_U() = default; 53CAPS_U::~CAPS_U() = default;
55 54
55void CAPS_U::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) {
56 IPC::RequestParser rp{ctx};
57 const auto library_version{rp.Pop<u64>()};
58 const auto applet_resource_user_id{rp.Pop<u64>()};
59
60 LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
61 library_version, applet_resource_user_id);
62
63 IPC::ResponseBuilder rb{ctx, 2};
64 rb.Push(RESULT_SUCCESS);
65}
66
56void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx) { 67void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx) {
57 // Takes a type-0x6 output buffer containing an array of ApplicationAlbumFileEntry, a PID, an 68 // Takes a type-0x6 output buffer containing an array of ApplicationAlbumFileEntry, a PID, an
58 // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total 69 // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total
diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h
index 689364de4..e04e56bbc 100644
--- a/src/core/hle/service/caps/caps_u.h
+++ b/src/core/hle/service/caps/caps_u.h
@@ -18,6 +18,7 @@ public:
18 ~CAPS_U() override; 18 ~CAPS_U() override;
19 19
20private: 20private:
21 void SetShimLibraryVersion(Kernel::HLERequestContext& ctx);
21 void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx); 22 void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx);
22}; 23};
23 24
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index a41c73c48..c2737a365 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -160,7 +160,7 @@ private:
160 return; 160 return;
161 } 161 }
162 162
163 ctx.WriteBuffer(key.data(), key.size()); 163 ctx.WriteBuffer(key);
164 164
165 IPC::ResponseBuilder rb{ctx, 2}; 165 IPC::ResponseBuilder rb{ctx, 2};
166 rb.Push(RESULT_SUCCESS); 166 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index cadc03805..54a5fb84b 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -36,7 +36,7 @@ constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
36 36
37static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, 37static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
38 std::string_view dir_name_) { 38 std::string_view dir_name_) {
39 std::string dir_name(FileUtil::SanitizePath(dir_name_)); 39 std::string dir_name(Common::FS::SanitizePath(dir_name_));
40 if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\") 40 if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
41 return base; 41 return base;
42 42
@@ -53,9 +53,13 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
53} 53}
54 54
55ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const { 55ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
56 std::string path(FileUtil::SanitizePath(path_)); 56 std::string path(Common::FS::SanitizePath(path_));
57 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 57 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
58 auto file = dir->CreateFile(FileUtil::GetFilename(path)); 58 // dir can be nullptr if path contains subdirectories, create those prior to creating the file.
59 if (dir == nullptr) {
60 dir = backing->CreateSubdirectory(Common::FS::GetParentPath(path));
61 }
62 auto file = dir->CreateFile(Common::FS::GetFilename(path));
59 if (file == nullptr) { 63 if (file == nullptr) {
60 // TODO(DarkLordZach): Find a better error code for this 64 // TODO(DarkLordZach): Find a better error code for this
61 return RESULT_UNKNOWN; 65 return RESULT_UNKNOWN;
@@ -68,17 +72,17 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
68} 72}
69 73
70ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { 74ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
71 std::string path(FileUtil::SanitizePath(path_)); 75 std::string path(Common::FS::SanitizePath(path_));
72 if (path.empty()) { 76 if (path.empty()) {
73 // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but... 77 // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
74 return RESULT_SUCCESS; 78 return RESULT_SUCCESS;
75 } 79 }
76 80
77 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 81 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
78 if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) { 82 if (dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
79 return FileSys::ERROR_PATH_NOT_FOUND; 83 return FileSys::ERROR_PATH_NOT_FOUND;
80 } 84 }
81 if (!dir->DeleteFile(FileUtil::GetFilename(path))) { 85 if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
82 // TODO(DarkLordZach): Find a better error code for this 86 // TODO(DarkLordZach): Find a better error code for this
83 return RESULT_UNKNOWN; 87 return RESULT_UNKNOWN;
84 } 88 }
@@ -87,11 +91,11 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
87} 91}
88 92
89ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const { 93ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
90 std::string path(FileUtil::SanitizePath(path_)); 94 std::string path(Common::FS::SanitizePath(path_));
91 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 95 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
92 if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty()) 96 if (dir == nullptr && Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty())
93 dir = backing; 97 dir = backing;
94 auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path)); 98 auto new_dir = dir->CreateSubdirectory(Common::FS::GetFilename(path));
95 if (new_dir == nullptr) { 99 if (new_dir == nullptr) {
96 // TODO(DarkLordZach): Find a better error code for this 100 // TODO(DarkLordZach): Find a better error code for this
97 return RESULT_UNKNOWN; 101 return RESULT_UNKNOWN;
@@ -100,9 +104,9 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_)
100} 104}
101 105
102ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const { 106ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
103 std::string path(FileUtil::SanitizePath(path_)); 107 std::string path(Common::FS::SanitizePath(path_));
104 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 108 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
105 if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) { 109 if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
106 // TODO(DarkLordZach): Find a better error code for this 110 // TODO(DarkLordZach): Find a better error code for this
107 return RESULT_UNKNOWN; 111 return RESULT_UNKNOWN;
108 } 112 }
@@ -110,9 +114,9 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_)
110} 114}
111 115
112ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const { 116ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
113 std::string path(FileUtil::SanitizePath(path_)); 117 std::string path(Common::FS::SanitizePath(path_));
114 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 118 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
115 if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) { 119 if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
116 // TODO(DarkLordZach): Find a better error code for this 120 // TODO(DarkLordZach): Find a better error code for this
117 return RESULT_UNKNOWN; 121 return RESULT_UNKNOWN;
118 } 122 }
@@ -120,10 +124,10 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
120} 124}
121 125
122ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const { 126ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
123 const std::string sanitized_path(FileUtil::SanitizePath(path)); 127 const std::string sanitized_path(Common::FS::SanitizePath(path));
124 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(sanitized_path)); 128 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));
125 129
126 if (!dir->CleanSubdirectoryRecursive(FileUtil::GetFilename(sanitized_path))) { 130 if (!dir->CleanSubdirectoryRecursive(Common::FS::GetFilename(sanitized_path))) {
127 // TODO(DarkLordZach): Find a better error code for this 131 // TODO(DarkLordZach): Find a better error code for this
128 return RESULT_UNKNOWN; 132 return RESULT_UNKNOWN;
129 } 133 }
@@ -133,14 +137,14 @@ ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::stri
133 137
134ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, 138ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
135 const std::string& dest_path_) const { 139 const std::string& dest_path_) const {
136 std::string src_path(FileUtil::SanitizePath(src_path_)); 140 std::string src_path(Common::FS::SanitizePath(src_path_));
137 std::string dest_path(FileUtil::SanitizePath(dest_path_)); 141 std::string dest_path(Common::FS::SanitizePath(dest_path_));
138 auto src = backing->GetFileRelative(src_path); 142 auto src = backing->GetFileRelative(src_path);
139 if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { 143 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
140 // Use more-optimized vfs implementation rename. 144 // Use more-optimized vfs implementation rename.
141 if (src == nullptr) 145 if (src == nullptr)
142 return FileSys::ERROR_PATH_NOT_FOUND; 146 return FileSys::ERROR_PATH_NOT_FOUND;
143 if (!src->Rename(FileUtil::GetFilename(dest_path))) { 147 if (!src->Rename(Common::FS::GetFilename(dest_path))) {
144 // TODO(DarkLordZach): Find a better error code for this 148 // TODO(DarkLordZach): Find a better error code for this
145 return RESULT_UNKNOWN; 149 return RESULT_UNKNOWN;
146 } 150 }
@@ -158,7 +162,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
158 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), 162 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
159 "Could not write all of the bytes but everything else has succeded."); 163 "Could not write all of the bytes but everything else has succeded.");
160 164
161 if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) { 165 if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
162 // TODO(DarkLordZach): Find a better error code for this 166 // TODO(DarkLordZach): Find a better error code for this
163 return RESULT_UNKNOWN; 167 return RESULT_UNKNOWN;
164 } 168 }
@@ -168,14 +172,14 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
168 172
169ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, 173ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
170 const std::string& dest_path_) const { 174 const std::string& dest_path_) const {
171 std::string src_path(FileUtil::SanitizePath(src_path_)); 175 std::string src_path(Common::FS::SanitizePath(src_path_));
172 std::string dest_path(FileUtil::SanitizePath(dest_path_)); 176 std::string dest_path(Common::FS::SanitizePath(dest_path_));
173 auto src = GetDirectoryRelativeWrapped(backing, src_path); 177 auto src = GetDirectoryRelativeWrapped(backing, src_path);
174 if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { 178 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
175 // Use more-optimized vfs implementation rename. 179 // Use more-optimized vfs implementation rename.
176 if (src == nullptr) 180 if (src == nullptr)
177 return FileSys::ERROR_PATH_NOT_FOUND; 181 return FileSys::ERROR_PATH_NOT_FOUND;
178 if (!src->Rename(FileUtil::GetFilename(dest_path))) { 182 if (!src->Rename(Common::FS::GetFilename(dest_path))) {
179 // TODO(DarkLordZach): Find a better error code for this 183 // TODO(DarkLordZach): Find a better error code for this
180 return RESULT_UNKNOWN; 184 return RESULT_UNKNOWN;
181 } 185 }
@@ -194,7 +198,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
194 198
195ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_, 199ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
196 FileSys::Mode mode) const { 200 FileSys::Mode mode) const {
197 const std::string path(FileUtil::SanitizePath(path_)); 201 const std::string path(Common::FS::SanitizePath(path_));
198 std::string_view npath = path; 202 std::string_view npath = path;
199 while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) { 203 while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
200 npath.remove_prefix(1); 204 npath.remove_prefix(1);
@@ -214,7 +218,7 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::
214} 218}
215 219
216ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) { 220ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) {
217 std::string path(FileUtil::SanitizePath(path_)); 221 std::string path(Common::FS::SanitizePath(path_));
218 auto dir = GetDirectoryRelativeWrapped(backing, path); 222 auto dir = GetDirectoryRelativeWrapped(backing, path);
219 if (dir == nullptr) { 223 if (dir == nullptr) {
220 // TODO(DarkLordZach): Find a better error code for this 224 // TODO(DarkLordZach): Find a better error code for this
@@ -225,11 +229,11 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s
225 229
226ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( 230ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
227 const std::string& path_) const { 231 const std::string& path_) const {
228 std::string path(FileUtil::SanitizePath(path_)); 232 std::string path(Common::FS::SanitizePath(path_));
229 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 233 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
230 if (dir == nullptr) 234 if (dir == nullptr)
231 return FileSys::ERROR_PATH_NOT_FOUND; 235 return FileSys::ERROR_PATH_NOT_FOUND;
232 auto filename = FileUtil::GetFilename(path); 236 auto filename = Common::FS::GetFilename(path);
233 // TODO(Subv): Some games use the '/' path, find out what this means. 237 // TODO(Subv): Some games use the '/' path, find out what this means.
234 if (filename.empty()) 238 if (filename.empty())
235 return MakeResult(FileSys::EntryType::Directory); 239 return MakeResult(FileSys::EntryType::Directory);
@@ -307,7 +311,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
307} 311}
308 312
309ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData( 313ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
310 FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const { 314 FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const {
311 LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", 315 LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}",
312 static_cast<u8>(space), save_struct.DebugInfo()); 316 static_cast<u8>(space), save_struct.DebugInfo());
313 317
@@ -319,15 +323,15 @@ ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
319} 323}
320 324
321ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData( 325ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData(
322 FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const { 326 FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& attribute) const {
323 LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", 327 LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
324 static_cast<u8>(space), descriptor.DebugInfo()); 328 static_cast<u8>(space), attribute.DebugInfo());
325 329
326 if (save_data_factory == nullptr) { 330 if (save_data_factory == nullptr) {
327 return FileSys::ERROR_ENTITY_NOT_FOUND; 331 return FileSys::ERROR_ENTITY_NOT_FOUND;
328 } 332 }
329 333
330 return save_data_factory->Open(space, descriptor); 334 return save_data_factory->Open(space, attribute);
331} 335}
332 336
333ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace( 337ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace(
@@ -375,7 +379,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage(
375 return FileSys::ERROR_ENTITY_NOT_FOUND; 379 return FileSys::ERROR_ENTITY_NOT_FOUND;
376 } 380 }
377 381
378 auto part = bis_factory->OpenPartitionStorage(id); 382 auto part = bis_factory->OpenPartitionStorage(id, system.GetFilesystem());
379 if (part == nullptr) { 383 if (part == nullptr) {
380 return FileSys::ERROR_INVALID_ARGUMENT; 384 return FileSys::ERROR_INVALID_ARGUMENT;
381 } 385 }
@@ -691,13 +695,13 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
691 sdmc_factory = nullptr; 695 sdmc_factory = nullptr;
692 } 696 }
693 697
694 auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), 698 auto nand_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir),
695 FileSys::Mode::ReadWrite); 699 FileSys::Mode::ReadWrite);
696 auto sd_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), 700 auto sd_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir),
697 FileSys::Mode::ReadWrite); 701 FileSys::Mode::ReadWrite);
698 auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), 702 auto load_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
699 FileSys::Mode::ReadWrite); 703 FileSys::Mode::ReadWrite);
700 auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), 704 auto dump_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir),
701 FileSys::Mode::ReadWrite); 705 FileSys::Mode::ReadWrite);
702 706
703 if (bis_factory == nullptr) { 707 if (bis_factory == nullptr) {
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 1b0a6a949..6dbbf0b2b 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -31,7 +31,7 @@ enum class SaveDataSpaceId : u8;
31enum class SaveDataType : u8; 31enum class SaveDataType : u8;
32enum class StorageId : u8; 32enum class StorageId : u8;
33 33
34struct SaveDataDescriptor; 34struct SaveDataAttribute;
35struct SaveDataSize; 35struct SaveDataSize;
36} // namespace FileSys 36} // namespace FileSys
37 37
@@ -69,9 +69,9 @@ public:
69 ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, 69 ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
70 FileSys::ContentRecordType type) const; 70 FileSys::ContentRecordType type) const;
71 ResultVal<FileSys::VirtualDir> CreateSaveData( 71 ResultVal<FileSys::VirtualDir> CreateSaveData(
72 FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const; 72 FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const;
73 ResultVal<FileSys::VirtualDir> OpenSaveData( 73 ResultVal<FileSys::VirtualDir> OpenSaveData(
74 FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const; 74 FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const;
75 ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const; 75 ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const;
76 ResultVal<FileSys::VirtualDir> OpenSDMC() const; 76 ResultVal<FileSys::VirtualDir> OpenSDMC() const;
77 ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const; 77 ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const;
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 20c331b77..649128be4 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -696,8 +696,8 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
696 {67, nullptr, "FindSaveDataWithFilter"}, 696 {67, nullptr, "FindSaveDataWithFilter"},
697 {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"}, 697 {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"},
698 {69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"}, 698 {69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"},
699 {70, nullptr, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"}, 699 {70, &FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"},
700 {71, nullptr, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"}, 700 {71, &FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"},
701 {80, nullptr, "OpenSaveDataMetaFile"}, 701 {80, nullptr, "OpenSaveDataMetaFile"},
702 {81, nullptr, "OpenSaveDataTransferManager"}, 702 {81, nullptr, "OpenSaveDataTransferManager"},
703 {82, nullptr, "OpenSaveDataTransferManagerVersion2"}, 703 {82, nullptr, "OpenSaveDataTransferManagerVersion2"},
@@ -812,7 +812,7 @@ void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
812void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { 812void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
813 IPC::RequestParser rp{ctx}; 813 IPC::RequestParser rp{ctx};
814 814
815 auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>(); 815 auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>();
816 [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); 816 [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
817 u128 uid = rp.PopRaw<u128>(); 817 u128 uid = rp.PopRaw<u128>();
818 818
@@ -826,31 +826,40 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
826} 826}
827 827
828void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { 828void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
829 LOG_INFO(Service_FS, "called."); 829 IPC::RequestParser rp{ctx};
830 830
831 struct Parameters { 831 struct Parameters {
832 FileSys::SaveDataSpaceId save_data_space_id; 832 FileSys::SaveDataSpaceId space_id;
833 FileSys::SaveDataDescriptor descriptor; 833 FileSys::SaveDataAttribute attribute;
834 }; 834 };
835 835
836 IPC::RequestParser rp{ctx};
837 const auto parameters = rp.PopRaw<Parameters>(); 836 const auto parameters = rp.PopRaw<Parameters>();
838 837
839 auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor); 838 LOG_INFO(Service_FS, "called.");
839
840 auto dir = fsc.OpenSaveData(parameters.space_id, parameters.attribute);
840 if (dir.Failed()) { 841 if (dir.Failed()) {
841 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 842 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
842 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); 843 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
843 return; 844 return;
844 } 845 }
845 846
846 FileSys::StorageId id; 847 FileSys::StorageId id{};
847 if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) { 848 switch (parameters.space_id) {
849 case FileSys::SaveDataSpaceId::NandUser:
848 id = FileSys::StorageId::NandUser; 850 id = FileSys::StorageId::NandUser;
849 } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem || 851 break;
850 parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) { 852 case FileSys::SaveDataSpaceId::SdCardSystem:
853 case FileSys::SaveDataSpaceId::SdCardUser:
851 id = FileSys::StorageId::SdCard; 854 id = FileSys::StorageId::SdCard;
852 } else { 855 break;
856 case FileSys::SaveDataSpaceId::NandSystem:
853 id = FileSys::StorageId::NandSystem; 857 id = FileSys::StorageId::NandSystem;
858 break;
859 case FileSys::SaveDataSpaceId::TemporaryStorage:
860 case FileSys::SaveDataSpaceId::ProperSystem:
861 case FileSys::SaveDataSpaceId::SafeMode:
862 UNREACHABLE();
854 } 863 }
855 864
856 auto filesystem = 865 auto filesystem =
@@ -876,22 +885,38 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext&
876 rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc)); 885 rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc));
877} 886}
878 887
879void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { 888void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(Kernel::HLERequestContext& ctx) {
880 IPC::RequestParser rp{ctx}; 889 LOG_WARNING(Service_FS, "(STUBBED) called.");
881 log_mode = rp.PopEnum<LogMode>();
882
883 LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode));
884 890
885 IPC::ResponseBuilder rb{ctx, 2}; 891 IPC::ResponseBuilder rb{ctx, 2};
886 rb.Push(RESULT_SUCCESS); 892 rb.Push(RESULT_SUCCESS);
887} 893}
888 894
889void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { 895void FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(
890 LOG_DEBUG(Service_FS, "called"); 896 Kernel::HLERequestContext& ctx) {
897 IPC::RequestParser rp{ctx};
898
899 struct Parameters {
900 FileSys::SaveDataSpaceId space_id;
901 FileSys::SaveDataAttribute attribute;
902 };
903
904 const auto parameters = rp.PopRaw<Parameters>();
905 // Stub this to None for now, backend needs an impl to read/write the SaveDataExtraData
906 constexpr auto flags = static_cast<u32>(FileSys::SaveDataFlags::None);
907
908 LOG_WARNING(Service_FS,
909 "(STUBBED) called, flags={}, space_id={}, attribute.title_id={:016X}\n"
910 "attribute.user_id={:016X}{:016X}, attribute.save_id={:016X}\n"
911 "attribute.type={}, attribute.rank={}, attribute.index={}",
912 flags, static_cast<u32>(parameters.space_id), parameters.attribute.title_id,
913 parameters.attribute.user_id[1], parameters.attribute.user_id[0],
914 parameters.attribute.save_id, static_cast<u32>(parameters.attribute.type),
915 static_cast<u32>(parameters.attribute.rank), parameters.attribute.index);
891 916
892 IPC::ResponseBuilder rb{ctx, 3}; 917 IPC::ResponseBuilder rb{ctx, 3};
893 rb.Push(RESULT_SUCCESS); 918 rb.Push(RESULT_SUCCESS);
894 rb.PushEnum(log_mode); 919 rb.Push(flags);
895} 920}
896 921
897void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { 922void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
@@ -966,6 +991,24 @@ void FSP_SRV::OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ct
966 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); 991 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
967} 992}
968 993
994void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
995 IPC::RequestParser rp{ctx};
996 log_mode = rp.PopEnum<LogMode>();
997
998 LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode));
999
1000 IPC::ResponseBuilder rb{ctx, 2};
1001 rb.Push(RESULT_SUCCESS);
1002}
1003
1004void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
1005 LOG_DEBUG(Service_FS, "called");
1006
1007 IPC::ResponseBuilder rb{ctx, 3};
1008 rb.Push(RESULT_SUCCESS);
1009 rb.PushEnum(log_mode);
1010}
1011
969void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) { 1012void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) {
970 const auto raw = ctx.ReadBuffer(); 1013 const auto raw = ctx.ReadBuffer();
971 auto log = Common::StringFromFixedZeroTerminatedBuffer( 1014 auto log = Common::StringFromFixedZeroTerminatedBuffer(
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index dfb3e395b..4964e874e 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -43,11 +43,13 @@ private:
43 void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx); 43 void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx);
44 void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); 44 void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
45 void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx); 45 void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx);
46 void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); 46 void WriteSaveDataFileSystemExtraDataBySaveDataAttribute(Kernel::HLERequestContext& ctx);
47 void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); 47 void ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(Kernel::HLERequestContext& ctx);
48 void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); 48 void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
49 void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); 49 void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
50 void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); 50 void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
51 void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
52 void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
51 void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); 53 void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
52 void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); 54 void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx);
53 void OpenMultiCommitManager(Kernel::HLERequestContext& ctx); 55 void OpenMultiCommitManager(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 8bc69c372..f47a9e61c 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -31,6 +31,10 @@ public:
31 virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 31 virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
32 std::size_t size) = 0; 32 std::size_t size) = 0;
33 33
34 // When the controller is requesting a motion update for the shared memory
35 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
36 std::size_t size) {}
37
34 // Called when input devices should be loaded 38 // Called when input devices should be loaded
35 virtual void OnLoadInputDevices() = 0; 39 virtual void OnLoadInputDevices() = 0;
36 40
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index cb35919e9..ad251ed4a 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -39,33 +39,36 @@ void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing,
39 39
40 cur_entry.sampling_number = last_entry.sampling_number + 1; 40 cur_entry.sampling_number = last_entry.sampling_number + 1;
41 cur_entry.sampling_number2 = cur_entry.sampling_number; 41 cur_entry.sampling_number2 = cur_entry.sampling_number;
42 cur_entry.attribute.connected.Assign(1);
43 auto& pad = cur_entry.pad_state;
44 42
45 using namespace Settings::NativeButton; 43 if (Settings::values.debug_pad_enabled) {
46 pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); 44 cur_entry.attribute.connected.Assign(1);
47 pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); 45 auto& pad = cur_entry.pad_state;
48 pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
49 pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
50 pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
51 pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
52 pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
53 pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
54 pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
55 pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
56 pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
57 pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
58 pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
59 pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
60 46
61 const auto [stick_l_x_f, stick_l_y_f] = 47 using namespace Settings::NativeButton;
62 analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); 48 pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
63 const auto [stick_r_x_f, stick_r_y_f] = 49 pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
64 analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); 50 pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
65 cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); 51 pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
66 cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); 52 pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
67 cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); 53 pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
68 cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); 54 pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
55 pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
56 pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
57 pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
58 pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
59 pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
60 pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
61 pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
62
63 const auto [stick_l_x_f, stick_l_y_f] =
64 analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
65 const auto [stick_r_x_f, stick_r_y_f] =
66 analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
67 cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
68 cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
69 cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
70 cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
71 }
69 72
70 std::memcpy(data, &shared_memory, sizeof(SharedMemory)); 73 std::memcpy(data, &shared_memory, sizeof(SharedMemory));
71} 74}
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index feae89525..0b896d5ad 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -40,15 +40,16 @@ void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing,
40 40
41 cur_entry.key.fill(0); 41 cur_entry.key.fill(0);
42 cur_entry.modifier = 0; 42 cur_entry.modifier = 0;
43 43 if (Settings::values.keyboard_enabled) {
44 for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { 44 for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
45 cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)); 45 cur_entry.key[i / KEYS_PER_BYTE] |=
46 } 46 (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE));
47 47 }
48 for (std::size_t i = 0; i < keyboard_mods.size(); ++i) { 48
49 cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i); 49 for (std::size_t i = 0; i < keyboard_mods.size(); ++i) {
50 cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i);
51 }
50 } 52 }
51
52 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 53 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
53} 54}
54 55
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index ef67ad690..2de4ed348 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -24,6 +24,7 @@ constexpr s32 HID_JOYSTICK_MAX = 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;
27constexpr std::size_t HANDHELD_INDEX = 8;
27constexpr std::array<u32, 10> npad_id_list{ 28constexpr std::array<u32, 10> npad_id_list{
28 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, 29 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
29}; 30};
@@ -33,19 +34,41 @@ enum class JoystickId : std::size_t {
33 Joystick_Right, 34 Joystick_Right,
34}; 35};
35 36
36static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) { 37Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
38 Settings::ControllerType type) {
37 switch (type) { 39 switch (type) {
38 case Settings::ControllerType::ProController: 40 case Settings::ControllerType::ProController:
39 return Controller_NPad::NPadControllerType::ProController; 41 return NPadControllerType::ProController;
40 case Settings::ControllerType::DualJoycon: 42 case Settings::ControllerType::DualJoyconDetached:
41 return Controller_NPad::NPadControllerType::JoyDual; 43 return NPadControllerType::JoyDual;
42 case Settings::ControllerType::LeftJoycon: 44 case Settings::ControllerType::LeftJoycon:
43 return Controller_NPad::NPadControllerType::JoyLeft; 45 return NPadControllerType::JoyLeft;
44 case Settings::ControllerType::RightJoycon: 46 case Settings::ControllerType::RightJoycon:
45 return Controller_NPad::NPadControllerType::JoyRight; 47 return NPadControllerType::JoyRight;
48 case Settings::ControllerType::Handheld:
49 return NPadControllerType::Handheld;
46 default: 50 default:
47 UNREACHABLE(); 51 UNREACHABLE();
48 return Controller_NPad::NPadControllerType::JoyDual; 52 return NPadControllerType::ProController;
53 }
54}
55
56Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
57 Controller_NPad::NPadControllerType type) {
58 switch (type) {
59 case NPadControllerType::ProController:
60 return Settings::ControllerType::ProController;
61 case NPadControllerType::JoyDual:
62 return Settings::ControllerType::DualJoyconDetached;
63 case NPadControllerType::JoyLeft:
64 return Settings::ControllerType::LeftJoycon;
65 case NPadControllerType::JoyRight:
66 return Settings::ControllerType::RightJoycon;
67 case NPadControllerType::Handheld:
68 return Settings::ControllerType::Handheld;
69 default:
70 UNREACHABLE();
71 return Settings::ControllerType::ProController;
49 } 72 }
50} 73}
51 74
@@ -60,9 +83,9 @@ std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
60 case 6: 83 case 6:
61 case 7: 84 case 7:
62 return npad_id; 85 return npad_id;
63 case 8: 86 case HANDHELD_INDEX:
64 case NPAD_HANDHELD: 87 case NPAD_HANDHELD:
65 return 8; 88 return HANDHELD_INDEX;
66 case 9: 89 case 9:
67 case NPAD_UNKNOWN: 90 case NPAD_UNKNOWN:
68 return 9; 91 return 9;
@@ -83,38 +106,48 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
83 case 6: 106 case 6:
84 case 7: 107 case 7:
85 return static_cast<u32>(index); 108 return static_cast<u32>(index);
86 case 8: 109 case HANDHELD_INDEX:
87 return NPAD_HANDHELD; 110 return NPAD_HANDHELD;
88 case 9: 111 case 9:
89 return NPAD_UNKNOWN; 112 return NPAD_UNKNOWN;
90 default: 113 default:
91 UNIMPLEMENTED_MSG("Unknown npad index {}", index); 114 UNIMPLEMENTED_MSG("Unknown npad index {}", index);
92 return 0; 115 return 0;
93 }; 116 }
94} 117}
95 118
96Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} 119Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
97Controller_NPad::~Controller_NPad() = default; 120Controller_NPad::~Controller_NPad() = default;
98 121
99void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { 122void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
100 const auto controller_type = connected_controllers[controller_idx].type; 123 const auto controller_type = connected_controllers[controller_idx].type;
101 auto& controller = shared_memory_entries[controller_idx]; 124 auto& controller = shared_memory_entries[controller_idx];
102 if (controller_type == NPadControllerType::None) { 125 if (controller_type == NPadControllerType::None) {
126 styleset_changed_events[controller_idx].writable->Signal();
103 return; 127 return;
104 } 128 }
105 controller.joy_styles.raw = 0; // Zero out 129 controller.joy_styles.raw = 0; // Zero out
106 controller.device_type.raw = 0; 130 controller.device_type.raw = 0;
131 controller.properties.raw = 0;
107 switch (controller_type) { 132 switch (controller_type) {
108 case NPadControllerType::None: 133 case NPadControllerType::None:
109 UNREACHABLE(); 134 UNREACHABLE();
110 break; 135 break;
136 case NPadControllerType::ProController:
137 controller.joy_styles.pro_controller.Assign(1);
138 controller.device_type.pro_controller.Assign(1);
139 controller.properties.is_vertical.Assign(1);
140 controller.properties.use_plus.Assign(1);
141 controller.properties.use_minus.Assign(1);
142 controller.pad_assignment = NPadAssignments::Single;
143 break;
111 case NPadControllerType::Handheld: 144 case NPadControllerType::Handheld:
112 controller.joy_styles.handheld.Assign(1); 145 controller.joy_styles.handheld.Assign(1);
113 controller.device_type.handheld.Assign(1); 146 controller.device_type.handheld.Assign(1);
114 controller.pad_assignment = NPadAssignments::Dual;
115 controller.properties.is_vertical.Assign(1); 147 controller.properties.is_vertical.Assign(1);
116 controller.properties.use_plus.Assign(1); 148 controller.properties.use_plus.Assign(1);
117 controller.properties.use_minus.Assign(1); 149 controller.properties.use_minus.Assign(1);
150 controller.pad_assignment = NPadAssignments::Dual;
118 break; 151 break;
119 case NPadControllerType::JoyDual: 152 case NPadControllerType::JoyDual:
120 controller.joy_styles.joycon_dual.Assign(1); 153 controller.joy_styles.joycon_dual.Assign(1);
@@ -144,14 +177,6 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
144 controller.device_type.pokeball.Assign(1); 177 controller.device_type.pokeball.Assign(1);
145 controller.pad_assignment = NPadAssignments::Single; 178 controller.pad_assignment = NPadAssignments::Single;
146 break; 179 break;
147 case NPadControllerType::ProController:
148 controller.joy_styles.pro_controller.Assign(1);
149 controller.device_type.pro_controller.Assign(1);
150 controller.properties.is_vertical.Assign(1);
151 controller.properties.use_plus.Assign(1);
152 controller.properties.use_minus.Assign(1);
153 controller.pad_assignment = NPadAssignments::Single;
154 break;
155 } 180 }
156 181
157 controller.single_color_error = ColorReadError::ReadOk; 182 controller.single_color_error = ColorReadError::ReadOk;
@@ -168,7 +193,8 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
168 controller.battery_level[0] = BATTERY_FULL; 193 controller.battery_level[0] = BATTERY_FULL;
169 controller.battery_level[1] = BATTERY_FULL; 194 controller.battery_level[1] = BATTERY_FULL;
170 controller.battery_level[2] = BATTERY_FULL; 195 controller.battery_level[2] = BATTERY_FULL;
171 styleset_changed_events[controller_idx].writable->Signal(); 196
197 SignalStyleSetChangedEvent(IndexToNPad(controller_idx));
172} 198}
173 199
174void Controller_NPad::OnInit() { 200void Controller_NPad::OnInit() {
@@ -192,36 +218,25 @@ void Controller_NPad::OnInit() {
192 style.pokeball.Assign(1); 218 style.pokeball.Assign(1);
193 } 219 }
194 220
195 std::transform( 221 std::transform(Settings::values.players.begin(), Settings::values.players.end(),
196 Settings::values.players.begin(), Settings::values.players.end(), 222 connected_controllers.begin(), [](const Settings::PlayerInput& player) {
197 connected_controllers.begin(), [](const Settings::PlayerInput& player) { 223 return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
198 return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected}; 224 player.connected};
199 }); 225 });
200
201 std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
202 [](const ControllerHolder& holder) { return holder.is_connected; });
203 226
204 // Account for handheld 227 // Account for handheld
205 if (connected_controllers[8].is_connected) 228 if (connected_controllers[HANDHELD_INDEX].is_connected) {
206 connected_controllers[8].type = NPadControllerType::Handheld; 229 connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
230 }
207 231
208 supported_npad_id_types.resize(npad_id_list.size()); 232 supported_npad_id_types.resize(npad_id_list.size());
209 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 233 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
210 npad_id_list.size() * sizeof(u32)); 234 npad_id_list.size() * sizeof(u32));
211 235
212 // Add a default dual joycon controller if none are present.
213 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
214 [](const ControllerHolder& controller) { return controller.is_connected; })) {
215 supported_npad_id_types.resize(npad_id_list.size());
216 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
217 npad_id_list.size() * sizeof(u32));
218 AddNewController(NPadControllerType::JoyDual);
219 }
220
221 for (std::size_t i = 0; i < connected_controllers.size(); ++i) { 236 for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
222 const auto& controller = connected_controllers[i]; 237 const auto& controller = connected_controllers[i];
223 if (controller.is_connected) { 238 if (controller.is_connected) {
224 AddNewControllerAt(controller.type, IndexToNPad(i)); 239 AddNewControllerAt(controller.type, i);
225 } 240 }
226 } 241 }
227} 242}
@@ -235,6 +250,9 @@ void Controller_NPad::OnLoadInputDevices() {
235 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 250 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
236 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, 251 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
237 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>); 252 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
253 std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
254 players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
255 motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
238 } 256 }
239} 257}
240 258
@@ -242,7 +260,7 @@ void Controller_NPad::OnRelease() {}
242 260
243void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { 261void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
244 const auto controller_idx = NPadIdToIndex(npad_id); 262 const auto controller_idx = NPadIdToIndex(npad_id);
245 [[maybe_unused]] const auto controller_type = connected_controllers[controller_idx].type; 263 const auto controller_type = connected_controllers[controller_idx].type;
246 if (!connected_controllers[controller_idx].is_connected) { 264 if (!connected_controllers[controller_idx].is_connected) {
247 return; 265 return;
248 } 266 }
@@ -251,66 +269,77 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
251 auto& rstick_entry = npad_pad_states[controller_idx].r_stick; 269 auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
252 const auto& button_state = buttons[controller_idx]; 270 const auto& button_state = buttons[controller_idx];
253 const auto& analog_state = sticks[controller_idx]; 271 const auto& analog_state = sticks[controller_idx];
272 const auto& motion_state = motions[controller_idx];
254 const auto [stick_l_x_f, stick_l_y_f] = 273 const auto [stick_l_x_f, stick_l_y_f] =
255 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); 274 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
256 const auto [stick_r_x_f, stick_r_y_f] = 275 const auto [stick_r_x_f, stick_r_y_f] =
257 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); 276 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
258 277
259 using namespace Settings::NativeButton; 278 using namespace Settings::NativeButton;
260 pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); 279 if (controller_type != NPadControllerType::JoyLeft) {
261 pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus()); 280 pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
262 pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus()); 281 pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
263 pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus()); 282 pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
264 pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus()); 283 pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
265 pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus()); 284 pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
266 pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus()); 285 pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
267 pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus()); 286 pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
268 pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus()); 287 pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
269 pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus()); 288
270 pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus()); 289 pad_state.r_stick_right.Assign(
271 pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus()); 290 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
272 291 ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
273 pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus()); 292 pad_state.r_stick_left.Assign(
274 pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus()); 293 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
275 pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus()); 294 ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
276 pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus()); 295 pad_state.r_stick_up.Assign(
277 296 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
278 pad_state.l_stick_right.Assign( 297 ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
279 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( 298 pad_state.r_stick_down.Assign(
280 Input::AnalogDirection::RIGHT)); 299 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
281 pad_state.l_stick_left.Assign( 300 ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
282 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( 301 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
283 Input::AnalogDirection::LEFT)); 302 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
284 pad_state.l_stick_up.Assign( 303 }
285 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( 304
286 Input::AnalogDirection::UP)); 305 if (controller_type != NPadControllerType::JoyRight) {
287 pad_state.l_stick_down.Assign( 306 pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
288 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( 307 pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
289 Input::AnalogDirection::DOWN)); 308 pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
290 309 pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
291 pad_state.r_stick_right.Assign( 310 pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
292 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] 311 pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
293 ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT)); 312 pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
294 pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] 313 pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
295 ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT)); 314
296 pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] 315 pad_state.l_stick_right.Assign(
297 ->GetAnalogDirectionStatus(Input::AnalogDirection::UP)); 316 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
298 pad_state.r_stick_down.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] 317 ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
299 ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); 318 pad_state.l_stick_left.Assign(
300 319 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
301 pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); 320 ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
302 pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); 321 pad_state.l_stick_up.Assign(
303 322 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
304 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); 323 ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
305 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); 324 pad_state.l_stick_down.Assign(
306 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); 325 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
307 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); 326 ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
327 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
328 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
329 }
330
331 if (controller_type == NPadControllerType::JoyLeft ||
332 controller_type == NPadControllerType::JoyRight) {
333 pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
334 pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
335 }
308} 336}
309 337
310void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 338void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
311 std::size_t data_len) { 339 std::size_t data_len) {
312 if (!IsControllerActivated()) 340 if (!IsControllerActivated()) {
313 return; 341 return;
342 }
314 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { 343 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
315 auto& npad = shared_memory_entries[i]; 344 auto& npad = shared_memory_entries[i];
316 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states, 345 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
@@ -344,6 +373,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
344 continue; 373 continue;
345 } 374 }
346 const u32 npad_index = static_cast<u32>(i); 375 const u32 npad_index = static_cast<u32>(i);
376
347 RequestPadStateUpdate(npad_index); 377 RequestPadStateUpdate(npad_index);
348 auto& pad_state = npad_pad_states[npad_index]; 378 auto& pad_state = npad_pad_states[npad_index];
349 379
@@ -360,13 +390,37 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
360 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; 390 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
361 391
362 libnx_entry.connection_status.raw = 0; 392 libnx_entry.connection_status.raw = 0;
393 libnx_entry.connection_status.IsConnected.Assign(1);
394 auto& full_sixaxis_entry =
395 npad.sixaxis_full.sixaxis[npad.sixaxis_full.common.last_entry_index];
396 auto& handheld_sixaxis_entry =
397 npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index];
398 auto& dual_left_sixaxis_entry =
399 npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index];
400 auto& dual_right_sixaxis_entry =
401 npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index];
402 auto& left_sixaxis_entry =
403 npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index];
404 auto& right_sixaxis_entry =
405 npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index];
363 406
364 switch (controller_type) { 407 switch (controller_type) {
365 case NPadControllerType::None: 408 case NPadControllerType::None:
366 UNREACHABLE(); 409 UNREACHABLE();
367 break; 410 break;
411 case NPadControllerType::ProController:
412 main_controller.connection_status.raw = 0;
413 main_controller.connection_status.IsConnected.Assign(1);
414 main_controller.connection_status.IsWired.Assign(1);
415 main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
416 main_controller.pad.l_stick = pad_state.l_stick;
417 main_controller.pad.r_stick = pad_state.r_stick;
418
419 libnx_entry.connection_status.IsWired.Assign(1);
420 break;
368 case NPadControllerType::Handheld: 421 case NPadControllerType::Handheld:
369 handheld_entry.connection_status.raw = 0; 422 handheld_entry.connection_status.raw = 0;
423 handheld_entry.connection_status.IsConnected.Assign(1);
370 handheld_entry.connection_status.IsWired.Assign(1); 424 handheld_entry.connection_status.IsWired.Assign(1);
371 handheld_entry.connection_status.IsLeftJoyConnected.Assign(1); 425 handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
372 handheld_entry.connection_status.IsRightJoyConnected.Assign(1); 426 handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
@@ -375,57 +429,52 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
375 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw; 429 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
376 handheld_entry.pad.l_stick = pad_state.l_stick; 430 handheld_entry.pad.l_stick = pad_state.l_stick;
377 handheld_entry.pad.r_stick = pad_state.r_stick; 431 handheld_entry.pad.r_stick = pad_state.r_stick;
432
433 libnx_entry.connection_status.IsWired.Assign(1);
434 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
435 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
436 libnx_entry.connection_status.IsLeftJoyWired.Assign(1);
437 libnx_entry.connection_status.IsRightJoyWired.Assign(1);
378 break; 438 break;
379 case NPadControllerType::JoyDual: 439 case NPadControllerType::JoyDual:
380 dual_entry.connection_status.raw = 0; 440 dual_entry.connection_status.raw = 0;
381 441 dual_entry.connection_status.IsConnected.Assign(1);
382 dual_entry.connection_status.IsLeftJoyConnected.Assign(1); 442 dual_entry.connection_status.IsLeftJoyConnected.Assign(1);
383 dual_entry.connection_status.IsRightJoyConnected.Assign(1); 443 dual_entry.connection_status.IsRightJoyConnected.Assign(1);
384 dual_entry.connection_status.IsConnected.Assign(1);
385
386 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
387 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
388 libnx_entry.connection_status.IsConnected.Assign(1);
389
390 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; 444 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
391 dual_entry.pad.l_stick = pad_state.l_stick; 445 dual_entry.pad.l_stick = pad_state.l_stick;
392 dual_entry.pad.r_stick = pad_state.r_stick; 446 dual_entry.pad.r_stick = pad_state.r_stick;
447
448 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
449 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
393 break; 450 break;
394 case NPadControllerType::JoyLeft: 451 case NPadControllerType::JoyLeft:
395 left_entry.connection_status.raw = 0; 452 left_entry.connection_status.raw = 0;
396
397 left_entry.connection_status.IsConnected.Assign(1); 453 left_entry.connection_status.IsConnected.Assign(1);
454 left_entry.connection_status.IsLeftJoyConnected.Assign(1);
398 left_entry.pad.pad_states.raw = pad_state.pad_states.raw; 455 left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
399 left_entry.pad.l_stick = pad_state.l_stick; 456 left_entry.pad.l_stick = pad_state.l_stick;
400 left_entry.pad.r_stick = pad_state.r_stick; 457 left_entry.pad.r_stick = pad_state.r_stick;
458
459 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
401 break; 460 break;
402 case NPadControllerType::JoyRight: 461 case NPadControllerType::JoyRight:
403 right_entry.connection_status.raw = 0; 462 right_entry.connection_status.raw = 0;
404
405 right_entry.connection_status.IsConnected.Assign(1); 463 right_entry.connection_status.IsConnected.Assign(1);
464 right_entry.connection_status.IsRightJoyConnected.Assign(1);
406 right_entry.pad.pad_states.raw = pad_state.pad_states.raw; 465 right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
407 right_entry.pad.l_stick = pad_state.l_stick; 466 right_entry.pad.l_stick = pad_state.l_stick;
408 right_entry.pad.r_stick = pad_state.r_stick; 467 right_entry.pad.r_stick = pad_state.r_stick;
468
469 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
409 break; 470 break;
410 case NPadControllerType::Pokeball: 471 case NPadControllerType::Pokeball:
411 pokeball_entry.connection_status.raw = 0; 472 pokeball_entry.connection_status.raw = 0;
412
413 pokeball_entry.connection_status.IsConnected.Assign(1); 473 pokeball_entry.connection_status.IsConnected.Assign(1);
414 pokeball_entry.connection_status.IsWired.Assign(1);
415
416 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw; 474 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
417 pokeball_entry.pad.l_stick = pad_state.l_stick; 475 pokeball_entry.pad.l_stick = pad_state.l_stick;
418 pokeball_entry.pad.r_stick = pad_state.r_stick; 476 pokeball_entry.pad.r_stick = pad_state.r_stick;
419 break; 477 break;
420 case NPadControllerType::ProController:
421 main_controller.connection_status.raw = 0;
422
423 main_controller.connection_status.IsConnected.Assign(1);
424 main_controller.connection_status.IsWired.Assign(1);
425 main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
426 main_controller.pad.l_stick = pad_state.l_stick;
427 main_controller.pad.r_stick = pad_state.r_stick;
428 break;
429 } 478 }
430 479
431 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate 480 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
@@ -440,6 +489,143 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
440 shared_memory_entries.size() * sizeof(NPadEntry)); 489 shared_memory_entries.size() * sizeof(NPadEntry));
441} 490}
442 491
492void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
493 std::size_t data_len) {
494 if (!IsControllerActivated()) {
495 return;
496 }
497 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
498 auto& npad = shared_memory_entries[i];
499
500 const auto& controller_type = connected_controllers[i].type;
501
502 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
503 continue;
504 }
505
506 const std::array<SixAxisGeneric*, 6> controller_sixaxes{
507 &npad.sixaxis_full, &npad.sixaxis_handheld, &npad.sixaxis_dual_left,
508 &npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right,
509 };
510
511 for (auto* sixaxis_sensor : controller_sixaxes) {
512 sixaxis_sensor->common.entry_count = 16;
513 sixaxis_sensor->common.total_entry_count = 17;
514
515 const auto& last_entry =
516 sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
517
518 sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks();
519 sixaxis_sensor->common.last_entry_index =
520 (sixaxis_sensor->common.last_entry_index + 1) % 17;
521
522 auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
523
524 cur_entry.timestamp = last_entry.timestamp + 1;
525 cur_entry.timestamp2 = cur_entry.timestamp;
526 }
527
528 // Try to read sixaxis sensor states
529 std::array<MotionDevice, 2> motion_devices;
530
531 if (sixaxis_sensors_enabled && Settings::values.motion_enabled) {
532 sixaxis_at_rest = true;
533 for (std::size_t e = 0; e < motion_devices.size(); ++e) {
534 const auto& device = motions[i][e];
535 if (device) {
536 std::tie(motion_devices[e].accel, motion_devices[e].gyro,
537 motion_devices[e].rotation, motion_devices[e].orientation) =
538 device->GetStatus();
539 sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f;
540 }
541 }
542 }
543
544 auto& main_controller =
545 npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
546 auto& handheld_entry =
547 npad.handheld_states.npad[npad.handheld_states.common.last_entry_index];
548 auto& dual_entry = npad.dual_states.npad[npad.dual_states.common.last_entry_index];
549 auto& left_entry = npad.left_joy_states.npad[npad.left_joy_states.common.last_entry_index];
550 auto& right_entry =
551 npad.right_joy_states.npad[npad.right_joy_states.common.last_entry_index];
552 auto& pokeball_entry =
553 npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
554 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
555
556 auto& full_sixaxis_entry =
557 npad.sixaxis_full.sixaxis[npad.sixaxis_full.common.last_entry_index];
558 auto& handheld_sixaxis_entry =
559 npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index];
560 auto& dual_left_sixaxis_entry =
561 npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index];
562 auto& dual_right_sixaxis_entry =
563 npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index];
564 auto& left_sixaxis_entry =
565 npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index];
566 auto& right_sixaxis_entry =
567 npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index];
568
569 switch (controller_type) {
570 case NPadControllerType::None:
571 UNREACHABLE();
572 break;
573 case NPadControllerType::ProController:
574 if (sixaxis_sensors_enabled && motions[i][0]) {
575 full_sixaxis_entry.accel = motion_devices[0].accel;
576 full_sixaxis_entry.gyro = motion_devices[0].gyro;
577 full_sixaxis_entry.rotation = motion_devices[0].rotation;
578 full_sixaxis_entry.orientation = motion_devices[0].orientation;
579 }
580 break;
581 case NPadControllerType::Handheld:
582 if (sixaxis_sensors_enabled && motions[i][0]) {
583 handheld_sixaxis_entry.accel = motion_devices[0].accel;
584 handheld_sixaxis_entry.gyro = motion_devices[0].gyro;
585 handheld_sixaxis_entry.rotation = motion_devices[0].rotation;
586 handheld_sixaxis_entry.orientation = motion_devices[0].orientation;
587 }
588 break;
589 case NPadControllerType::JoyDual:
590 if (sixaxis_sensors_enabled && motions[i][0]) {
591 // Set motion for the left joycon
592 dual_left_sixaxis_entry.accel = motion_devices[0].accel;
593 dual_left_sixaxis_entry.gyro = motion_devices[0].gyro;
594 dual_left_sixaxis_entry.rotation = motion_devices[0].rotation;
595 dual_left_sixaxis_entry.orientation = motion_devices[0].orientation;
596 }
597 if (sixaxis_sensors_enabled && motions[i][1]) {
598 // Set motion for the right joycon
599 dual_right_sixaxis_entry.accel = motion_devices[1].accel;
600 dual_right_sixaxis_entry.gyro = motion_devices[1].gyro;
601 dual_right_sixaxis_entry.rotation = motion_devices[1].rotation;
602 dual_right_sixaxis_entry.orientation = motion_devices[1].orientation;
603 }
604 break;
605 case NPadControllerType::JoyLeft:
606 if (sixaxis_sensors_enabled && motions[i][0]) {
607 left_sixaxis_entry.accel = motion_devices[0].accel;
608 left_sixaxis_entry.gyro = motion_devices[0].gyro;
609 left_sixaxis_entry.rotation = motion_devices[0].rotation;
610 left_sixaxis_entry.orientation = motion_devices[0].orientation;
611 }
612 break;
613 case NPadControllerType::JoyRight:
614 if (sixaxis_sensors_enabled && motions[i][1]) {
615 right_sixaxis_entry.accel = motion_devices[1].accel;
616 right_sixaxis_entry.gyro = motion_devices[1].gyro;
617 right_sixaxis_entry.rotation = motion_devices[1].rotation;
618 right_sixaxis_entry.orientation = motion_devices[1].orientation;
619 }
620 break;
621 case NPadControllerType::Pokeball:
622 break;
623 }
624 }
625 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
626 shared_memory_entries.size() * sizeof(NPadEntry));
627}
628
443void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { 629void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
444 style.raw = style_set.raw; 630 style.raw = style_set.raw;
445} 631}
@@ -453,26 +639,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
453 supported_npad_id_types.clear(); 639 supported_npad_id_types.clear();
454 supported_npad_id_types.resize(length / sizeof(u32)); 640 supported_npad_id_types.resize(length / sizeof(u32));
455 std::memcpy(supported_npad_id_types.data(), data, length); 641 std::memcpy(supported_npad_id_types.data(), data, length);
456 for (std::size_t i = 0; i < connected_controllers.size(); i++) {
457 auto& controller = connected_controllers[i];
458 if (!controller.is_connected) {
459 continue;
460 }
461 const auto requested_controller =
462 i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
463 : NPadControllerType::Handheld;
464 if (!IsControllerSupported(requested_controller)) {
465 const auto is_handheld = requested_controller == NPadControllerType::Handheld;
466 if (is_handheld) {
467 controller.type = NPadControllerType::None;
468 controller.is_connected = false;
469 AddNewController(requested_controller);
470 } else {
471 controller.type = requested_controller;
472 InitNewlyAddedControler(i);
473 }
474 }
475 }
476} 642}
477 643
478void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 644void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
@@ -492,6 +658,14 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
492 return hold_type; 658 return hold_type;
493} 659}
494 660
661void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
662 handheld_activation_mode = activation_mode;
663}
664
665Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const {
666 return handheld_activation_mode;
667}
668
495void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { 669void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
496 const std::size_t npad_index = NPadIdToIndex(npad_id); 670 const std::size_t npad_index = NPadIdToIndex(npad_id);
497 ASSERT(npad_index < shared_memory_entries.size()); 671 ASSERT(npad_index < shared_memory_entries.size());
@@ -500,70 +674,86 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode)
500 } 674 }
501} 675}
502 676
503void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, 677void Controller_NPad::VibrateController(const std::vector<u32>& controllers,
504 const std::vector<Vibration>& vibrations) { 678 const std::vector<Vibration>& vibrations) {
505 LOG_DEBUG(Service_HID, "(STUBBED) called"); 679 LOG_TRACE(Service_HID, "called");
506 680
507 if (!can_controllers_vibrate) { 681 if (!Settings::values.vibration_enabled || !can_controllers_vibrate) {
508 return; 682 return;
509 } 683 }
510 for (std::size_t i = 0; i < controller_ids.size(); i++) { 684 bool success = true;
511 std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i)); 685 for (std::size_t i = 0; i < controllers.size(); ++i) {
512 if (connected_controllers[controller_pos].is_connected) { 686 if (!connected_controllers[i].is_connected) {
513 // TODO(ogniK): Vibrate the physical controller 687 continue;
688 }
689 using namespace Settings::NativeButton;
690 const auto& button_state = buttons[i];
691 if (button_state[A - BUTTON_HID_BEGIN]) {
692 if (button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay(
693 vibrations[0].amp_high, vibrations[0].amp_low, vibrations[0].freq_high,
694 vibrations[0].freq_low)) {
695 success = false;
696 }
514 } 697 }
515 } 698 }
516 last_processed_vibration = vibrations.back(); 699 if (success) {
700 last_processed_vibration = vibrations.back();
701 }
702}
703
704Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
705 return last_processed_vibration;
517} 706}
518 707
519std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { 708std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
520 // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
521 // be signalled at least once, and signaled after a new controller is connected?
522 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; 709 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
523 return styleset_event.readable; 710 return styleset_event.readable;
524} 711}
525 712
526Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { 713void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const {
527 return last_processed_vibration; 714 styleset_changed_events[NPadIdToIndex(npad_id)].writable->Signal();
528} 715}
529 716
530void Controller_NPad::AddNewController(NPadControllerType controller) { 717void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
531 controller = DecideBestController(controller); 718 UpdateControllerAt(controller, npad_index, true);
532 if (controller == NPadControllerType::Handheld) { 719}
533 connected_controllers[8] = {controller, true}; 720
534 InitNewlyAddedControler(8); 721void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
535 return; 722 bool connected) {
536 } 723 if (!connected) {
537 const auto pos = 724 DisconnectNPadAtIndex(npad_index);
538 std::find_if(connected_controllers.begin(), connected_controllers.end() - 2,
539 [](const ControllerHolder& holder) { return !holder.is_connected; });
540 if (pos == connected_controllers.end() - 2) {
541 LOG_ERROR(Service_HID, "Cannot connect any more controllers!");
542 return; 725 return;
543 } 726 }
544 const auto controller_id = std::distance(connected_controllers.begin(), pos);
545 connected_controllers[controller_id] = {controller, true};
546 InitNewlyAddedControler(controller_id);
547}
548 727
549void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
550 controller = DecideBestController(controller);
551 if (controller == NPadControllerType::Handheld) { 728 if (controller == NPadControllerType::Handheld) {
552 connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true}; 729 Settings::values.players[HANDHELD_INDEX].controller_type =
553 InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD)); 730 MapNPadToSettingsType(controller);
731 Settings::values.players[HANDHELD_INDEX].connected = true;
732 connected_controllers[HANDHELD_INDEX] = {controller, true};
733 InitNewlyAddedController(HANDHELD_INDEX);
554 return; 734 return;
555 } 735 }
556 736
557 connected_controllers[NPadIdToIndex(npad_id)] = {controller, true}; 737 Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller);
558 InitNewlyAddedControler(NPadIdToIndex(npad_id)); 738 Settings::values.players[npad_index].connected = true;
739 connected_controllers[npad_index] = {controller, true};
740 InitNewlyAddedController(npad_index);
559} 741}
560 742
561void Controller_NPad::ConnectNPad(u32 npad_id) { 743void Controller_NPad::DisconnectNPad(u32 npad_id) {
562 connected_controllers[NPadIdToIndex(npad_id)].is_connected = true; 744 DisconnectNPadAtIndex(NPadIdToIndex(npad_id));
563} 745}
564 746
565void Controller_NPad::DisconnectNPad(u32 npad_id) { 747void Controller_NPad::DisconnectNPadAtIndex(std::size_t npad_index) {
566 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; 748 Settings::values.players[npad_index].connected = false;
749 connected_controllers[npad_index].is_connected = false;
750
751 auto& controller = shared_memory_entries[npad_index];
752 controller.joy_styles.raw = 0; // Zero out
753 controller.device_type.raw = 0;
754 controller.properties.raw = 0;
755
756 SignalStyleSetChangedEvent(IndexToNPad(npad_index));
567} 757}
568 758
569void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { 759void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
@@ -574,6 +764,30 @@ Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMo
574 return gyroscope_zero_drift_mode; 764 return gyroscope_zero_drift_mode;
575} 765}
576 766
767bool Controller_NPad::IsSixAxisSensorAtRest() const {
768 return sixaxis_at_rest;
769}
770
771void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) {
772 sixaxis_sensors_enabled = six_axis_status;
773}
774
775void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
776 const auto npad_index_1 = NPadIdToIndex(npad_id_1);
777 const auto npad_index_2 = NPadIdToIndex(npad_id_2);
778
779 // If the controllers at both npad indices form a pair of left and right joycons, merge them.
780 // Otherwise, do nothing.
781 if ((connected_controllers[npad_index_1].type == NPadControllerType::JoyLeft &&
782 connected_controllers[npad_index_2].type == NPadControllerType::JoyRight) ||
783 (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft &&
784 connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) {
785 // Disconnect the joycon at the second id and connect the dual joycon at the first index.
786 DisconnectNPad(npad_id_2);
787 AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1);
788 }
789}
790
577void Controller_NPad::StartLRAssignmentMode() { 791void Controller_NPad::StartLRAssignmentMode() {
578 // Nothing internally is used for lr assignment mode. Since we have the ability to set the 792 // Nothing internally is used for lr assignment mode. Since we have the ability to set the
579 // controller types from boot, it doesn't really matter about showing a selection screen 793 // controller types from boot, it doesn't really matter about showing a selection screen
@@ -599,8 +813,8 @@ bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) {
599 813
600 std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); 814 std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type);
601 815
602 InitNewlyAddedControler(npad_index_1); 816 AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1);
603 InitNewlyAddedControler(npad_index_2); 817 AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2);
604 818
605 return true; 819 return true;
606} 820}
@@ -614,11 +828,11 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
614 case 0: 828 case 0:
615 return LedPattern{1, 0, 0, 0}; 829 return LedPattern{1, 0, 0, 0};
616 case 1: 830 case 1:
617 return LedPattern{0, 1, 0, 0}; 831 return LedPattern{1, 1, 0, 0};
618 case 2: 832 case 2:
619 return LedPattern{0, 0, 1, 0}; 833 return LedPattern{1, 1, 1, 0};
620 case 3: 834 case 3:
621 return LedPattern{0, 0, 0, 1}; 835 return LedPattern{1, 1, 1, 1};
622 case 4: 836 case 4:
623 return LedPattern{1, 0, 0, 1}; 837 return LedPattern{1, 0, 0, 1};
624 case 5: 838 case 5:
@@ -628,9 +842,17 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
628 case 7: 842 case 7:
629 return LedPattern{0, 1, 1, 0}; 843 return LedPattern{0, 1, 1, 0};
630 default: 844 default:
631 UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
632 return LedPattern{0, 0, 0, 0}; 845 return LedPattern{0, 0, 0, 0};
633 }; 846 }
847}
848
849bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const {
850 return unintended_home_button_input_protection[NPadIdToIndex(npad_id)];
851}
852
853void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
854 u32 npad_id) {
855 unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled;
634} 856}
635 857
636void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { 858void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
@@ -651,13 +873,13 @@ void Controller_NPad::ClearAllConnectedControllers() {
651} 873}
652 874
653void Controller_NPad::DisconnectAllConnectedControllers() { 875void Controller_NPad::DisconnectAllConnectedControllers() {
654 for (ControllerHolder& controller : connected_controllers) { 876 for (auto& controller : connected_controllers) {
655 controller.is_connected = false; 877 controller.is_connected = false;
656 } 878 }
657} 879}
658 880
659void Controller_NPad::ConnectAllDisconnectedControllers() { 881void Controller_NPad::ConnectAllDisconnectedControllers() {
660 for (ControllerHolder& controller : connected_controllers) { 882 for (auto& controller : connected_controllers) {
661 if (controller.type != NPadControllerType::None && !controller.is_connected) { 883 if (controller.type != NPadControllerType::None && !controller.is_connected) {
662 controller.is_connected = true; 884 controller.is_connected = true;
663 } 885 }
@@ -665,7 +887,7 @@ void Controller_NPad::ConnectAllDisconnectedControllers() {
665} 887}
666 888
667void Controller_NPad::ClearAllControllers() { 889void Controller_NPad::ClearAllControllers() {
668 for (ControllerHolder& controller : connected_controllers) { 890 for (auto& controller : connected_controllers) {
669 controller.type = NPadControllerType::None; 891 controller.type = NPadControllerType::None;
670 controller.is_connected = false; 892 controller.is_connected = false;
671 } 893 }
@@ -713,92 +935,4 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
713 return false; 935 return false;
714} 936}
715 937
716Controller_NPad::NPadControllerType Controller_NPad::DecideBestController(
717 NPadControllerType priority) const {
718 if (IsControllerSupported(priority)) {
719 return priority;
720 }
721 const auto is_docked = Settings::values.use_docked_mode;
722 if (is_docked && priority == NPadControllerType::Handheld) {
723 priority = NPadControllerType::JoyDual;
724 if (IsControllerSupported(priority)) {
725 return priority;
726 }
727 }
728 std::vector<NPadControllerType> priority_list;
729 switch (priority) {
730 case NPadControllerType::ProController:
731 priority_list.push_back(NPadControllerType::JoyDual);
732 if (!is_docked) {
733 priority_list.push_back(NPadControllerType::Handheld);
734 }
735 priority_list.push_back(NPadControllerType::JoyLeft);
736 priority_list.push_back(NPadControllerType::JoyRight);
737 priority_list.push_back(NPadControllerType::Pokeball);
738 break;
739 case NPadControllerType::Handheld:
740 priority_list.push_back(NPadControllerType::JoyDual);
741 priority_list.push_back(NPadControllerType::ProController);
742 priority_list.push_back(NPadControllerType::JoyLeft);
743 priority_list.push_back(NPadControllerType::JoyRight);
744 priority_list.push_back(NPadControllerType::Pokeball);
745 break;
746 case NPadControllerType::JoyDual:
747 if (!is_docked) {
748 priority_list.push_back(NPadControllerType::Handheld);
749 }
750 priority_list.push_back(NPadControllerType::ProController);
751 priority_list.push_back(NPadControllerType::JoyLeft);
752 priority_list.push_back(NPadControllerType::JoyRight);
753 priority_list.push_back(NPadControllerType::Pokeball);
754 break;
755 case NPadControllerType::JoyLeft:
756 priority_list.push_back(NPadControllerType::JoyRight);
757 priority_list.push_back(NPadControllerType::JoyDual);
758 if (!is_docked) {
759 priority_list.push_back(NPadControllerType::Handheld);
760 }
761 priority_list.push_back(NPadControllerType::ProController);
762 priority_list.push_back(NPadControllerType::Pokeball);
763 break;
764 case NPadControllerType::JoyRight:
765 priority_list.push_back(NPadControllerType::JoyLeft);
766 priority_list.push_back(NPadControllerType::JoyDual);
767 if (!is_docked) {
768 priority_list.push_back(NPadControllerType::Handheld);
769 }
770 priority_list.push_back(NPadControllerType::ProController);
771 priority_list.push_back(NPadControllerType::Pokeball);
772 break;
773 case NPadControllerType::Pokeball:
774 priority_list.push_back(NPadControllerType::JoyLeft);
775 priority_list.push_back(NPadControllerType::JoyRight);
776 priority_list.push_back(NPadControllerType::JoyDual);
777 if (!is_docked) {
778 priority_list.push_back(NPadControllerType::Handheld);
779 }
780 priority_list.push_back(NPadControllerType::ProController);
781 break;
782 default:
783 priority_list.push_back(NPadControllerType::JoyDual);
784 if (!is_docked) {
785 priority_list.push_back(NPadControllerType::Handheld);
786 }
787 priority_list.push_back(NPadControllerType::ProController);
788 priority_list.push_back(NPadControllerType::JoyLeft);
789 priority_list.push_back(NPadControllerType::JoyRight);
790 priority_list.push_back(NPadControllerType::JoyDual);
791 break;
792 }
793
794 const auto iter = std::find_if(priority_list.begin(), priority_list.end(),
795 [this](auto type) { return IsControllerSupported(type); });
796 if (iter == priority_list.end()) {
797 UNIMPLEMENTED_MSG("Could not find supported controller!");
798 return priority;
799 }
800
801 return *iter;
802}
803
804} // namespace Service::HID 938} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 5d4c58a43..fd5c5a6eb 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -32,6 +32,10 @@ public:
32 // When the controller is requesting an update for the shared memory 32 // When the controller is requesting an update for the shared memory
33 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 33 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
34 34
35 // When the controller is requesting a motion update for the shared memory
36 void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
37 std::size_t size) override;
38
35 // Called when input devices should be loaded 39 // Called when input devices should be loaded
36 void OnLoadInputDevices() override; 40 void OnLoadInputDevices() override;
37 41
@@ -74,6 +78,12 @@ public:
74 Single = 1, 78 Single = 1,
75 }; 79 };
76 80
81 enum class NpadHandheldActivationMode : u64 {
82 Dual = 0,
83 Single = 1,
84 None = 2,
85 };
86
77 enum class NPadControllerType { 87 enum class NPadControllerType {
78 None, 88 None,
79 ProController, 89 ProController,
@@ -110,22 +120,34 @@ public:
110 void SetHoldType(NpadHoldType joy_hold_type); 120 void SetHoldType(NpadHoldType joy_hold_type);
111 NpadHoldType GetHoldType() const; 121 NpadHoldType GetHoldType() const;
112 122
123 void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
124 NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
125
113 void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode); 126 void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode);
114 127
115 void VibrateController(const std::vector<u32>& controller_ids, 128 void VibrateController(const std::vector<u32>& controllers,
116 const std::vector<Vibration>& vibrations); 129 const std::vector<Vibration>& vibrations);
117 130
118 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
119 Vibration GetLastVibration() const; 131 Vibration GetLastVibration() const;
120 132
121 void AddNewController(NPadControllerType controller); 133 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
122 void AddNewControllerAt(NPadControllerType controller, u32 npad_id); 134 void SignalStyleSetChangedEvent(u32 npad_id) const;
135
136 // Adds a new controller at an index.
137 void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index);
138 // Adds a new controller at an index with connection status.
139 void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
123 140
124 void ConnectNPad(u32 npad_id);
125 void DisconnectNPad(u32 npad_id); 141 void DisconnectNPad(u32 npad_id);
142 void DisconnectNPadAtIndex(std::size_t index);
143
126 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); 144 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
127 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; 145 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
146 bool IsSixAxisSensorAtRest() const;
147 void SetSixAxisEnabled(bool six_axis_status);
128 LedPattern GetLedPattern(u32 npad_id); 148 LedPattern GetLedPattern(u32 npad_id);
149 bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const;
150 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id);
129 void SetVibrationEnabled(bool can_vibrate); 151 void SetVibrationEnabled(bool can_vibrate);
130 bool IsVibrationEnabled() const; 152 bool IsVibrationEnabled() const;
131 void ClearAllConnectedControllers(); 153 void ClearAllConnectedControllers();
@@ -133,6 +155,7 @@ public:
133 void ConnectAllDisconnectedControllers(); 155 void ConnectAllDisconnectedControllers();
134 void ClearAllControllers(); 156 void ClearAllControllers();
135 157
158 void MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2);
136 void StartLRAssignmentMode(); 159 void StartLRAssignmentMode();
137 void StopLRAssignmentMode(); 160 void StopLRAssignmentMode();
138 bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2); 161 bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2);
@@ -141,6 +164,8 @@ public:
141 // Specifically for cheat engine and other features. 164 // Specifically for cheat engine and other features.
142 u32 GetAndResetPressState(); 165 u32 GetAndResetPressState();
143 166
167 static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type);
168 static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type);
144 static std::size_t NPadIdToIndex(u32 npad_id); 169 static std::size_t NPadIdToIndex(u32 npad_id);
145 static u32 IndexToNPad(std::size_t index); 170 static u32 IndexToNPad(std::size_t index);
146 171
@@ -244,6 +269,24 @@ private:
244 }; 269 };
245 static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size"); 270 static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
246 271
272 struct SixAxisStates {
273 s64_le timestamp{};
274 INSERT_PADDING_WORDS(2);
275 s64_le timestamp2{};
276 Common::Vec3f accel{};
277 Common::Vec3f gyro{};
278 Common::Vec3f rotation{};
279 std::array<Common::Vec3f, 3> orientation{};
280 s64_le always_one{1};
281 };
282 static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size");
283
284 struct SixAxisGeneric {
285 CommonHeader common{};
286 std::array<SixAxisStates, 17> sixaxis{};
287 };
288 static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
289
247 enum class ColorReadError : u32_le { 290 enum class ColorReadError : u32_le {
248 ReadOk = 0, 291 ReadOk = 0,
249 ColorDoesntExist = 1, 292 ColorDoesntExist = 1,
@@ -273,6 +316,13 @@ private:
273 }; 316 };
274 }; 317 };
275 318
319 struct MotionDevice {
320 Common::Vec3f accel;
321 Common::Vec3f gyro;
322 Common::Vec3f rotation;
323 std::array<Common::Vec3f, 3> orientation;
324 };
325
276 struct NPadEntry { 326 struct NPadEntry {
277 NPadType joy_styles; 327 NPadType joy_styles;
278 NPadAssignments pad_assignment; 328 NPadAssignments pad_assignment;
@@ -292,9 +342,12 @@ private:
292 NPadGeneric pokeball_states; 342 NPadGeneric pokeball_states;
293 NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be 343 NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be
294 // relying on this for the time being 344 // relying on this for the time being
295 INSERT_PADDING_BYTES( 345 SixAxisGeneric sixaxis_full;
296 0x708 * 346 SixAxisGeneric sixaxis_handheld;
297 6); // TODO(ogniK): SixAxis states, require more information before implementation 347 SixAxisGeneric sixaxis_dual_left;
348 SixAxisGeneric sixaxis_dual_right;
349 SixAxisGeneric sixaxis_left;
350 SixAxisGeneric sixaxis_right;
298 NPadDevice device_type; 351 NPadDevice device_type;
299 NPadProperties properties; 352 NPadProperties properties;
300 INSERT_PADDING_WORDS(1); 353 INSERT_PADDING_WORDS(1);
@@ -309,31 +362,38 @@ private:
309 bool is_connected; 362 bool is_connected;
310 }; 363 };
311 364
312 void InitNewlyAddedControler(std::size_t controller_idx); 365 void InitNewlyAddedController(std::size_t controller_idx);
313 bool IsControllerSupported(NPadControllerType controller) const; 366 bool IsControllerSupported(NPadControllerType controller) const;
314 NPadControllerType DecideBestController(NPadControllerType priority) const;
315 void RequestPadStateUpdate(u32 npad_id); 367 void RequestPadStateUpdate(u32 npad_id);
316 368
317 u32 press_state{}; 369 u32 press_state{};
318 370
319 NPadType style{}; 371 NPadType style{};
320 std::array<NPadEntry, 10> shared_memory_entries{}; 372 std::array<NPadEntry, 10> shared_memory_entries{};
321 std::array< 373 using ButtonArray = std::array<
322 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>, 374 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
323 10> 375 10>;
324 buttons; 376 using StickArray = std::array<
325 std::array<
326 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, 377 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
327 10> 378 10>;
328 sticks; 379 using MotionArray = std::array<
380 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTION_HID>,
381 10>;
382 ButtonArray buttons;
383 StickArray sticks;
384 MotionArray motions;
329 std::vector<u32> supported_npad_id_types{}; 385 std::vector<u32> supported_npad_id_types{};
330 NpadHoldType hold_type{NpadHoldType::Vertical}; 386 NpadHoldType hold_type{NpadHoldType::Vertical};
387 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
331 // Each controller should have their own styleset changed event 388 // Each controller should have their own styleset changed event
332 std::array<Kernel::EventPair, 10> styleset_changed_events; 389 std::array<Kernel::EventPair, 10> styleset_changed_events;
333 Vibration last_processed_vibration{}; 390 Vibration last_processed_vibration{};
334 std::array<ControllerHolder, 10> connected_controllers{}; 391 std::array<ControllerHolder, 10> connected_controllers{};
392 std::array<bool, 10> unintended_home_button_input_protection{};
335 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; 393 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
336 bool can_controllers_vibrate{true}; 394 bool can_controllers_vibrate{true};
395 bool sixaxis_sensors_enabled{true};
396 bool sixaxis_at_rest{true};
337 std::array<ControllerPad, 10> npad_pad_states{}; 397 std::array<ControllerPad, 10> npad_pad_states{};
338 bool is_in_lr_assignment_mode{false}; 398 bool is_in_lr_assignment_mode{false};
339 Core::System& system; 399 Core::System& system;
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index e326f8f5c..0df395e85 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -40,9 +40,14 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
40 cur_entry.sampling_number = last_entry.sampling_number + 1; 40 cur_entry.sampling_number = last_entry.sampling_number + 1;
41 cur_entry.sampling_number2 = cur_entry.sampling_number; 41 cur_entry.sampling_number2 = cur_entry.sampling_number;
42 42
43 const auto [x, y, pressed] = touch_device->GetStatus(); 43 bool pressed = false;
44 float x, y;
45 std::tie(x, y, pressed) = touch_device->GetStatus();
44 auto& touch_entry = cur_entry.states[0]; 46 auto& touch_entry = cur_entry.states[0];
45 touch_entry.attribute.raw = 0; 47 touch_entry.attribute.raw = 0;
48 if (!pressed && touch_btn_device) {
49 std::tie(x, y, pressed) = touch_btn_device->GetStatus();
50 }
46 if (pressed && Settings::values.touchscreen.enabled) { 51 if (pressed && Settings::values.touchscreen.enabled) {
47 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); 52 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
48 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); 53 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
@@ -63,5 +68,10 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
63 68
64void Controller_Touchscreen::OnLoadInputDevices() { 69void Controller_Touchscreen::OnLoadInputDevices() {
65 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device); 70 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device);
71 if (Settings::values.use_touch_from_button) {
72 touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
73 } else {
74 touch_btn_device.reset();
75 }
66} 76}
67} // namespace Service::HID 77} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index a1d97269e..4d9042adc 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -68,6 +68,7 @@ private:
68 "TouchScreenSharedMemory is an invalid size"); 68 "TouchScreenSharedMemory is an invalid size");
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 std::unique_ptr<Input::TouchDevice> touch_btn_device;
71 s64_le last_touch{}; 72 s64_le last_touch{};
72}; 73};
73} // namespace Service::HID 74} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index e9020e0dc..71dbaba7f 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -38,10 +38,10 @@
38namespace Service::HID { 38namespace Service::HID {
39 39
40// Updating period for each HID device. 40// Updating period for each HID device.
41// TODO(ogniK): Find actual polling rate of hid 41// HID is polled every 15ms, this value was derived from
42constexpr s64 pad_update_ticks = static_cast<s64>(1000000000 / 66); 42// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
43[[maybe_unused]] constexpr s64 accelerometer_update_ticks = static_cast<s64>(1000000000 / 100); 43constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz)
44[[maybe_unused]] constexpr s64 gyroscope_update_ticks = static_cast<s64>(1000000000 / 100); 44constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz)
45constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 45constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
46 46
47IAppletResource::IAppletResource(Core::System& system) 47IAppletResource::IAppletResource(Core::System& system)
@@ -75,14 +75,19 @@ IAppletResource::IAppletResource(Core::System& system)
75 GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); 75 GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
76 76
77 // Register update callbacks 77 // Register update callbacks
78 pad_update_event = 78 pad_update_event = Core::Timing::CreateEvent(
79 Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 ns_late) { 79 "HID::UpdatePadCallback",
80 UpdateControllers(userdata, ns_late); 80 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
81 UpdateControllers(user_data, ns_late);
82 });
83 motion_update_event = Core::Timing::CreateEvent(
84 "HID::MotionPadCallback",
85 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
86 UpdateMotion(user_data, ns_late);
81 }); 87 });
82 88
83 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) 89 system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
84 90 system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event);
85 system.CoreTiming().ScheduleEvent(pad_update_ticks, pad_update_event);
86 91
87 ReloadInputDevices(); 92 ReloadInputDevices();
88} 93}
@@ -107,7 +112,8 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
107 rb.PushCopyObjects(shared_mem); 112 rb.PushCopyObjects(shared_mem);
108} 113}
109 114
110void IAppletResource::UpdateControllers(u64 userdata, s64 ns_late) { 115void IAppletResource::UpdateControllers(std::uintptr_t user_data,
116 std::chrono::nanoseconds ns_late) {
111 auto& core_timing = system.CoreTiming(); 117 auto& core_timing = system.CoreTiming();
112 118
113 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); 119 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
@@ -118,7 +124,17 @@ void IAppletResource::UpdateControllers(u64 userdata, s64 ns_late) {
118 controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); 124 controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
119 } 125 }
120 126
121 core_timing.ScheduleEvent(pad_update_ticks - ns_late, pad_update_event); 127 core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
128}
129
130void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
131 auto& core_timing = system.CoreTiming();
132
133 for (const auto& controller : controllers) {
134 controller->OnMotionUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
135 }
136
137 core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event);
122} 138}
123 139
124class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { 140class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
@@ -163,8 +179,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
163 {56, nullptr, "ActivateJoyXpad"}, 179 {56, nullptr, "ActivateJoyXpad"},
164 {58, nullptr, "GetJoyXpadLifoHandle"}, 180 {58, nullptr, "GetJoyXpadLifoHandle"},
165 {59, nullptr, "GetJoyXpadIds"}, 181 {59, nullptr, "GetJoyXpadIds"},
166 {60, nullptr, "ActivateSixAxisSensor"}, 182 {60, &Hid::ActivateSixAxisSensor, "ActivateSixAxisSensor"},
167 {61, nullptr, "DeactivateSixAxisSensor"}, 183 {61, &Hid::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"},
168 {62, nullptr, "GetSixAxisSensorLifoHandle"}, 184 {62, nullptr, "GetSixAxisSensorLifoHandle"},
169 {63, nullptr, "ActivateJoySixAxisSensor"}, 185 {63, nullptr, "ActivateJoySixAxisSensor"},
170 {64, nullptr, "DeactivateJoySixAxisSensor"}, 186 {64, nullptr, "DeactivateJoySixAxisSensor"},
@@ -172,7 +188,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
172 {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, 188 {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
173 {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, 189 {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
174 {68, nullptr, "IsSixAxisSensorFusionEnabled"}, 190 {68, nullptr, "IsSixAxisSensorFusionEnabled"},
175 {69, nullptr, "EnableSixAxisSensorFusion"}, 191 {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
176 {70, nullptr, "SetSixAxisSensorFusionParameters"}, 192 {70, nullptr, "SetSixAxisSensorFusionParameters"},
177 {71, nullptr, "GetSixAxisSensorFusionParameters"}, 193 {71, nullptr, "GetSixAxisSensorFusionParameters"},
178 {72, nullptr, "ResetSixAxisSensorFusionParameters"}, 194 {72, nullptr, "ResetSixAxisSensorFusionParameters"},
@@ -208,8 +224,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
208 {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, 224 {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
209 {129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"}, 225 {129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"},
210 {130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"}, 226 {130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"},
211 {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"}, 227 {131, &Hid::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"},
212 {132, nullptr, "EnableUnintendedHomeButtonInputProtection"}, 228 {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
213 {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, 229 {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
214 {134, nullptr, "SetNpadAnalogStickUseCenterClamp"}, 230 {134, nullptr, "SetNpadAnalogStickUseCenterClamp"},
215 {135, nullptr, "SetNpadCaptureButtonAssignment"}, 231 {135, nullptr, "SetNpadCaptureButtonAssignment"},
@@ -328,6 +344,31 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
328 rb.Push(0); 344 rb.Push(0);
329} 345}
330 346
347void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
348 IPC::RequestParser rp{ctx};
349 const auto handle{rp.Pop<u32>()};
350 const auto applet_resource_user_id{rp.Pop<u64>()};
351 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
352 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
353 applet_resource_user_id);
354
355 IPC::ResponseBuilder rb{ctx, 2};
356 rb.Push(RESULT_SUCCESS);
357}
358
359void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
360 IPC::RequestParser rp{ctx};
361 const auto handle{rp.Pop<u32>()};
362 const auto applet_resource_user_id{rp.Pop<u64>()};
363 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
364
365 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
366 applet_resource_user_id);
367
368 IPC::ResponseBuilder rb{ctx, 2};
369 rb.Push(RESULT_SUCCESS);
370}
371
331void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { 372void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) {
332 IPC::RequestParser rp{ctx}; 373 IPC::RequestParser rp{ctx};
333 const auto applet_resource_user_id{rp.Pop<u64>()}; 374 const auto applet_resource_user_id{rp.Pop<u64>()};
@@ -432,6 +473,19 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
432 rb.Push(RESULT_SUCCESS); 473 rb.Push(RESULT_SUCCESS);
433} 474}
434 475
476void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
477 IPC::RequestParser rp{ctx};
478 const auto enable{rp.Pop<bool>()};
479 const auto handle{rp.Pop<u32>()};
480 const auto applet_resource_user_id{rp.Pop<u64>()};
481
482 LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
483 applet_resource_user_id);
484
485 IPC::ResponseBuilder rb{ctx, 2};
486 rb.Push(RESULT_SUCCESS);
487}
488
435void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 489void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
436 IPC::RequestParser rp{ctx}; 490 IPC::RequestParser rp{ctx};
437 const auto handle{rp.Pop<u32>()}; 491 const auto handle{rp.Pop<u32>()};
@@ -483,13 +537,13 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
483 const auto handle{rp.Pop<u32>()}; 537 const auto handle{rp.Pop<u32>()};
484 const auto applet_resource_user_id{rp.Pop<u64>()}; 538 const auto applet_resource_user_id{rp.Pop<u64>()};
485 539
486 LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, 540 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
487 applet_resource_user_id); 541 applet_resource_user_id);
488 542
489 IPC::ResponseBuilder rb{ctx, 3}; 543 IPC::ResponseBuilder rb{ctx, 3};
490 rb.Push(RESULT_SUCCESS); 544 rb.Push(RESULT_SUCCESS);
491 // TODO (Hexagon12): Properly implement reading gyroscope values from controllers. 545 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
492 rb.Push(true); 546 .IsSixAxisSensorAtRest());
493} 547}
494 548
495void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 549void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
@@ -670,13 +724,15 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
670 724
671void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { 725void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
672 IPC::RequestParser rp{ctx}; 726 IPC::RequestParser rp{ctx};
673 const auto unknown_1{rp.Pop<u32>()}; 727 const auto npad_id_1{rp.Pop<u32>()};
674 const auto unknown_2{rp.Pop<u32>()}; 728 const auto npad_id_2{rp.Pop<u32>()};
675 const auto applet_resource_user_id{rp.Pop<u64>()}; 729 const auto applet_resource_user_id{rp.Pop<u64>()};
676 730
677 LOG_WARNING(Service_HID, 731 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
678 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", 732 npad_id_1, npad_id_2, applet_resource_user_id);
679 unknown_1, unknown_2, applet_resource_user_id); 733
734 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
735 controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
680 736
681 IPC::ResponseBuilder rb{ctx, 2}; 737 IPC::ResponseBuilder rb{ctx, 2};
682 rb.Push(RESULT_SUCCESS); 738 rb.Push(RESULT_SUCCESS);
@@ -711,8 +767,11 @@ void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
711 const auto applet_resource_user_id{rp.Pop<u64>()}; 767 const auto applet_resource_user_id{rp.Pop<u64>()};
712 const auto mode{rp.Pop<u64>()}; 768 const auto mode{rp.Pop<u64>()};
713 769
714 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}", 770 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, mode={}", applet_resource_user_id,
715 applet_resource_user_id, mode); 771 mode);
772
773 applet_resource->GetController<Controller_NPad>(HidController::NPad)
774 .SetNpadHandheldActivationMode(Controller_NPad::NpadHandheldActivationMode{mode});
716 775
717 IPC::ResponseBuilder rb{ctx, 2}; 776 IPC::ResponseBuilder rb{ctx, 2};
718 rb.Push(RESULT_SUCCESS); 777 rb.Push(RESULT_SUCCESS);
@@ -722,11 +781,13 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
722 IPC::RequestParser rp{ctx}; 781 IPC::RequestParser rp{ctx};
723 const auto applet_resource_user_id{rp.Pop<u64>()}; 782 const auto applet_resource_user_id{rp.Pop<u64>()};
724 783
725 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", 784 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
726 applet_resource_user_id);
727 785
728 IPC::ResponseBuilder rb{ctx, 2}; 786 IPC::ResponseBuilder rb{ctx, 4};
729 rb.Push(RESULT_SUCCESS); 787 rb.Push(RESULT_SUCCESS);
788 rb.Push<u64>(
789 static_cast<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
790 .GetNpadHandheldActivationMode()));
730} 791}
731 792
732void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { 793void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
@@ -748,6 +809,40 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
748 } 809 }
749} 810}
750 811
812void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
813 IPC::RequestParser rp{ctx};
814 const auto npad_id{rp.Pop<u32>()};
815 const auto applet_resource_user_id{rp.Pop<u64>()};
816
817 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id,
818 applet_resource_user_id);
819
820 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
821
822 IPC::ResponseBuilder rb{ctx, 3};
823 rb.Push(RESULT_SUCCESS);
824 rb.Push<bool>(controller.IsUnintendedHomeButtonInputProtectionEnabled(npad_id));
825}
826
827void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) {
828 IPC::RequestParser rp{ctx};
829 const auto unintended_home_button_input_protection{rp.Pop<bool>()};
830 const auto npad_id{rp.Pop<u32>()};
831 const auto applet_resource_user_id{rp.Pop<u64>()};
832
833 LOG_WARNING(Service_HID,
834 "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={},"
835 "applet_resource_user_id={}",
836 npad_id, unintended_home_button_input_protection, applet_resource_user_id);
837
838 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
839 controller.SetUnintendedHomeButtonInputProtectionEnabled(
840 unintended_home_button_input_protection, npad_id);
841
842 IPC::ResponseBuilder rb{ctx, 2};
843 rb.Push(RESULT_SUCCESS);
844}
845
751void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { 846void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
752 IPC::RequestParser rp{ctx}; 847 IPC::RequestParser rp{ctx};
753 const auto applet_resource_user_id{rp.Pop<u64>()}; 848 const auto applet_resource_user_id{rp.Pop<u64>()};
@@ -769,18 +864,18 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
769 864
770void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { 865void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
771 IPC::RequestParser rp{ctx}; 866 IPC::RequestParser rp{ctx};
772 const auto controller_id{rp.Pop<u32>()}; 867 const auto controller{rp.Pop<u32>()};
773 const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()}; 868 const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()};
774 const auto applet_resource_user_id{rp.Pop<u64>()}; 869 const auto applet_resource_user_id{rp.Pop<u64>()};
775 870
776 LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id, 871 LOG_DEBUG(Service_HID, "called, controller={}, applet_resource_user_id={}", controller,
777 applet_resource_user_id); 872 applet_resource_user_id);
778 873
779 IPC::ResponseBuilder rb{ctx, 2}; 874 IPC::ResponseBuilder rb{ctx, 2};
780 rb.Push(RESULT_SUCCESS); 875 rb.Push(RESULT_SUCCESS);
781 876
782 applet_resource->GetController<Controller_NPad>(HidController::NPad) 877 applet_resource->GetController<Controller_NPad>(HidController::NPad)
783 .VibrateController({controller_id}, {vibration_values}); 878 .VibrateController({controller}, {vibration_values});
784} 879}
785 880
786void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { 881void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
@@ -798,8 +893,6 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
798 893
799 std::memcpy(controller_list.data(), controllers.data(), controllers.size()); 894 std::memcpy(controller_list.data(), controllers.data(), controllers.size());
800 std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size()); 895 std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size());
801 std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(),
802 [](u32 controller_id) { return controller_id - 3; });
803 896
804 applet_resource->GetController<Controller_NPad>(HidController::NPad) 897 applet_resource->GetController<Controller_NPad>(HidController::NPad)
805 .VibrateController(controller_list, vibration_list); 898 .VibrateController(controller_list, vibration_list);
@@ -842,8 +935,7 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
842void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { 935void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
843 IPC::RequestParser rp{ctx}; 936 IPC::RequestParser rp{ctx};
844 const auto can_vibrate{rp.Pop<bool>()}; 937 const auto can_vibrate{rp.Pop<bool>()};
845 applet_resource->GetController<Controller_NPad>(HidController::NPad) 938 Settings::values.vibration_enabled = can_vibrate;
846 .SetVibrationEnabled(can_vibrate);
847 939
848 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); 940 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
849 941
@@ -856,8 +948,7 @@ void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
856 948
857 IPC::ResponseBuilder rb{ctx, 3}; 949 IPC::ResponseBuilder rb{ctx, 3};
858 rb.Push(RESULT_SUCCESS); 950 rb.Push(RESULT_SUCCESS);
859 rb.Push( 951 rb.Push(Settings::values.vibration_enabled);
860 applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled());
861} 952}
862 953
863void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 954void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 6fb048360..fd0372b18 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -4,10 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/hid/controllers/controller_base.h" 7#include <chrono>
8#include "core/hle/service/service.h"
9 8
10#include "controllers/controller_base.h" 9#include "core/hle/service/hid/controllers/controller_base.h"
11#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
12 11
13namespace Core::Timing { 12namespace Core::Timing {
@@ -65,11 +64,13 @@ private:
65 } 64 }
66 65
67 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); 66 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
68 void UpdateControllers(u64 userdata, s64 cycles_late); 67 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
68 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
69 69
70 std::shared_ptr<Kernel::SharedMemory> shared_mem; 70 std::shared_ptr<Kernel::SharedMemory> shared_mem;
71 71
72 std::shared_ptr<Core::Timing::EventType> pad_update_event; 72 std::shared_ptr<Core::Timing::EventType> pad_update_event;
73 std::shared_ptr<Core::Timing::EventType> motion_update_event;
73 Core::System& system; 74 Core::System& system;
74 75
75 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> 76 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
@@ -87,6 +88,8 @@ private:
87 void CreateAppletResource(Kernel::HLERequestContext& ctx); 88 void CreateAppletResource(Kernel::HLERequestContext& ctx);
88 void ActivateXpad(Kernel::HLERequestContext& ctx); 89 void ActivateXpad(Kernel::HLERequestContext& ctx);
89 void GetXpadIDs(Kernel::HLERequestContext& ctx); 90 void GetXpadIDs(Kernel::HLERequestContext& ctx);
91 void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
92 void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
90 void ActivateDebugPad(Kernel::HLERequestContext& ctx); 93 void ActivateDebugPad(Kernel::HLERequestContext& ctx);
91 void ActivateTouchScreen(Kernel::HLERequestContext& ctx); 94 void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
92 void ActivateMouse(Kernel::HLERequestContext& ctx); 95 void ActivateMouse(Kernel::HLERequestContext& ctx);
@@ -96,6 +99,7 @@ private:
96 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); 99 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
97 void StartSixAxisSensor(Kernel::HLERequestContext& ctx); 100 void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
98 void StopSixAxisSensor(Kernel::HLERequestContext& ctx); 101 void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
102 void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
99 void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); 103 void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
100 void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); 104 void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
101 void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); 105 void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
@@ -119,6 +123,8 @@ private:
119 void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx); 123 void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
120 void GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx); 124 void GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
121 void SwapNpadAssignment(Kernel::HLERequestContext& ctx); 125 void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
126 void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx);
127 void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx);
122 void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx); 128 void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
123 void EndPermitVibrationSession(Kernel::HLERequestContext& ctx); 129 void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
124 void SendVibrationValue(Kernel::HLERequestContext& ctx); 130 void SendVibrationValue(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 64a526b9e..d8cd10e31 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -310,7 +310,7 @@ public:
310 310
311 ResultVal<VAddr> MapProcessCodeMemory(Kernel::Process* process, VAddr baseAddress, 311 ResultVal<VAddr> MapProcessCodeMemory(Kernel::Process* process, VAddr baseAddress,
312 u64 size) const { 312 u64 size) const {
313 for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) { 313 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
314 auto& page_table{process->PageTable()}; 314 auto& page_table{process->PageTable()};
315 const VAddr addr{GetRandomMapRegion(page_table, size)}; 315 const VAddr addr{GetRandomMapRegion(page_table, size)};
316 const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)}; 316 const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)};
@@ -331,8 +331,7 @@ public:
331 331
332 ResultVal<VAddr> MapNro(Kernel::Process* process, VAddr nro_addr, std::size_t nro_size, 332 ResultVal<VAddr> MapNro(Kernel::Process* process, VAddr nro_addr, std::size_t nro_size,
333 VAddr bss_addr, std::size_t bss_size, std::size_t size) const { 333 VAddr bss_addr, std::size_t bss_size, std::size_t size) const {
334 334 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
335 for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) {
336 auto& page_table{process->PageTable()}; 335 auto& page_table{process->PageTable()};
337 VAddr addr{}; 336 VAddr addr{};
338 337
diff --git a/src/core/hle/service/mii/manager.cpp b/src/core/hle/service/mii/manager.cpp
index 4a1d1182e..4730070cb 100644
--- a/src/core/hle/service/mii/manager.cpp
+++ b/src/core/hle/service/mii/manager.cpp
@@ -47,66 +47,67 @@ std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& i
47MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) { 47MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
48 MiiStoreBitFields bf; 48 MiiStoreBitFields bf;
49 std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields)); 49 std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields));
50 MiiInfo info{}; 50
51 info.name = ResizeArray<char16_t, 10, 11>(data.data.name); 51 return {
52 info.uuid = data.data.uuid; 52 .uuid = data.data.uuid,
53 info.font_region = static_cast<u8>(bf.font_region.Value()); 53 .name = ResizeArray<char16_t, 10, 11>(data.data.name),
54 info.favorite_color = static_cast<u8>(bf.favorite_color.Value()); 54 .font_region = static_cast<u8>(bf.font_region.Value()),
55 info.gender = static_cast<u8>(bf.gender.Value()); 55 .favorite_color = static_cast<u8>(bf.favorite_color.Value()),
56 info.height = static_cast<u8>(bf.height.Value()); 56 .gender = static_cast<u8>(bf.gender.Value()),
57 info.build = static_cast<u8>(bf.build.Value()); 57 .height = static_cast<u8>(bf.height.Value()),
58 info.type = static_cast<u8>(bf.type.Value()); 58 .build = static_cast<u8>(bf.build.Value()),
59 info.region_move = static_cast<u8>(bf.region_move.Value()); 59 .type = static_cast<u8>(bf.type.Value()),
60 info.faceline_type = static_cast<u8>(bf.faceline_type.Value()); 60 .region_move = static_cast<u8>(bf.region_move.Value()),
61 info.faceline_color = static_cast<u8>(bf.faceline_color.Value()); 61 .faceline_type = static_cast<u8>(bf.faceline_type.Value()),
62 info.faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value()); 62 .faceline_color = static_cast<u8>(bf.faceline_color.Value()),
63 info.faceline_make = static_cast<u8>(bf.faceline_makeup.Value()); 63 .faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value()),
64 info.hair_type = static_cast<u8>(bf.hair_type.Value()); 64 .faceline_make = static_cast<u8>(bf.faceline_makeup.Value()),
65 info.hair_color = static_cast<u8>(bf.hair_color.Value()); 65 .hair_type = static_cast<u8>(bf.hair_type.Value()),
66 info.hair_flip = static_cast<u8>(bf.hair_flip.Value()); 66 .hair_color = static_cast<u8>(bf.hair_color.Value()),
67 info.eye_type = static_cast<u8>(bf.eye_type.Value()); 67 .hair_flip = static_cast<u8>(bf.hair_flip.Value()),
68 info.eye_color = static_cast<u8>(bf.eye_color.Value()); 68 .eye_type = static_cast<u8>(bf.eye_type.Value()),
69 info.eye_scale = static_cast<u8>(bf.eye_scale.Value()); 69 .eye_color = static_cast<u8>(bf.eye_color.Value()),
70 info.eye_aspect = static_cast<u8>(bf.eye_aspect.Value()); 70 .eye_scale = static_cast<u8>(bf.eye_scale.Value()),
71 info.eye_rotate = static_cast<u8>(bf.eye_rotate.Value()); 71 .eye_aspect = static_cast<u8>(bf.eye_aspect.Value()),
72 info.eye_x = static_cast<u8>(bf.eye_x.Value()); 72 .eye_rotate = static_cast<u8>(bf.eye_rotate.Value()),
73 info.eye_y = static_cast<u8>(bf.eye_y.Value()); 73 .eye_x = static_cast<u8>(bf.eye_x.Value()),
74 info.eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value()); 74 .eye_y = static_cast<u8>(bf.eye_y.Value()),
75 info.eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value()); 75 .eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value()),
76 info.eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value()); 76 .eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value()),
77 info.eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value()); 77 .eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value()),
78 info.eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value()); 78 .eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value()),
79 info.eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value()); 79 .eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value()),
80 info.eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3); 80 .eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value()),
81 info.nose_type = static_cast<u8>(bf.nose_type.Value()); 81 .eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3),
82 info.nose_scale = static_cast<u8>(bf.nose_scale.Value()); 82 .nose_type = static_cast<u8>(bf.nose_type.Value()),
83 info.nose_y = static_cast<u8>(bf.nose_y.Value()); 83 .nose_scale = static_cast<u8>(bf.nose_scale.Value()),
84 info.mouth_type = static_cast<u8>(bf.mouth_type.Value()); 84 .nose_y = static_cast<u8>(bf.nose_y.Value()),
85 info.mouth_color = static_cast<u8>(bf.mouth_color.Value()); 85 .mouth_type = static_cast<u8>(bf.mouth_type.Value()),
86 info.mouth_scale = static_cast<u8>(bf.mouth_scale.Value()); 86 .mouth_color = static_cast<u8>(bf.mouth_color.Value()),
87 info.mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value()); 87 .mouth_scale = static_cast<u8>(bf.mouth_scale.Value()),
88 info.mouth_y = static_cast<u8>(bf.mouth_y.Value()); 88 .mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value()),
89 info.beard_color = static_cast<u8>(bf.beard_color.Value()); 89 .mouth_y = static_cast<u8>(bf.mouth_y.Value()),
90 info.beard_type = static_cast<u8>(bf.beard_type.Value()); 90 .beard_color = static_cast<u8>(bf.beard_color.Value()),
91 info.mustache_type = static_cast<u8>(bf.mustache_type.Value()); 91 .beard_type = static_cast<u8>(bf.beard_type.Value()),
92 info.mustache_scale = static_cast<u8>(bf.mustache_scale.Value()); 92 .mustache_type = static_cast<u8>(bf.mustache_type.Value()),
93 info.mustache_y = static_cast<u8>(bf.mustache_y.Value()); 93 .mustache_scale = static_cast<u8>(bf.mustache_scale.Value()),
94 info.glasses_type = static_cast<u8>(bf.glasses_type.Value()); 94 .mustache_y = static_cast<u8>(bf.mustache_y.Value()),
95 info.glasses_color = static_cast<u8>(bf.glasses_color.Value()); 95 .glasses_type = static_cast<u8>(bf.glasses_type.Value()),
96 info.glasses_scale = static_cast<u8>(bf.glasses_scale.Value()); 96 .glasses_color = static_cast<u8>(bf.glasses_color.Value()),
97 info.glasses_y = static_cast<u8>(bf.glasses_y.Value()); 97 .glasses_scale = static_cast<u8>(bf.glasses_scale.Value()),
98 info.mole_type = static_cast<u8>(bf.mole_type.Value()); 98 .glasses_y = static_cast<u8>(bf.glasses_y.Value()),
99 info.mole_scale = static_cast<u8>(bf.mole_scale.Value()); 99 .mole_type = static_cast<u8>(bf.mole_type.Value()),
100 info.mole_x = static_cast<u8>(bf.mole_x.Value()); 100 .mole_scale = static_cast<u8>(bf.mole_scale.Value()),
101 info.mole_y = static_cast<u8>(bf.mole_y.Value()); 101 .mole_x = static_cast<u8>(bf.mole_x.Value()),
102 return info; 102 .mole_y = static_cast<u8>(bf.mole_y.Value()),
103 };
103} 104}
104 105
105u16 GenerateCrc16(const void* data, std::size_t size) { 106u16 GenerateCrc16(const void* data, std::size_t size) {
106 s32 crc{}; 107 s32 crc{};
107 for (int i = 0; i < size; i++) { 108 for (std::size_t i = 0; i < size; i++) {
108 crc ^= reinterpret_cast<const u8*>(data)[i] << 8; 109 crc ^= static_cast<const u8*>(data)[i] << 8;
109 for (int j = 0; j < 8; j++) { 110 for (std::size_t j = 0; j < 8; j++) {
110 crc <<= 1; 111 crc <<= 1;
111 if ((crc & 0x10000) != 0) { 112 if ((crc & 0x10000) != 0) {
112 crc = (crc ^ 0x1021) & 0xFFFF; 113 crc = (crc ^ 0x1021) & 0xFFFF;
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 4b79eb81d..a0469ffbd 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -2,6 +2,7 @@
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 <array>
5#include <atomic> 6#include <atomic>
6 7
7#include "common/logging/log.h" 8#include "common/logging/log.h"
@@ -72,10 +73,10 @@ private:
72 std::array<u8, 10> uuid; 73 std::array<u8, 10> uuid;
73 u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it 74 u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it
74 // mean something else 75 // mean something else
75 INSERT_PADDING_BYTES(0x15); 76 std::array<u8, 0x15> padding_1;
76 u32_le protocol; 77 u32_le protocol;
77 u32_le tag_type; 78 u32_le tag_type;
78 INSERT_PADDING_BYTES(0x2c); 79 std::array<u8, 0x2c> padding_2;
79 }; 80 };
80 static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); 81 static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size");
81 82
@@ -127,7 +128,7 @@ private:
127 const u32 array_size = rp.Pop<u32>(); 128 const u32 array_size = rp.Pop<u32>();
128 LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); 129 LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);
129 130
130 ctx.WriteBuffer(&device_handle, sizeof(device_handle)); 131 ctx.WriteBuffer(device_handle);
131 132
132 IPC::ResponseBuilder rb{ctx, 3}; 133 IPC::ResponseBuilder rb{ctx, 3};
133 rb.Push(RESULT_SUCCESS); 134 rb.Push(RESULT_SUCCESS);
@@ -213,14 +214,16 @@ private:
213 LOG_DEBUG(Service_NFP, "called"); 214 LOG_DEBUG(Service_NFP, "called");
214 215
215 IPC::ResponseBuilder rb{ctx, 2}; 216 IPC::ResponseBuilder rb{ctx, 2};
216 auto amiibo = nfp_interface.GetAmiiboBuffer(); 217 const auto& amiibo = nfp_interface.GetAmiiboBuffer();
217 TagInfo tag_info{}; 218 const TagInfo tag_info{
218 tag_info.uuid = amiibo.uuid; 219 .uuid = amiibo.uuid,
219 tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size()); 220 .uuid_length = static_cast<u8>(tag_info.uuid.size()),
220 221 .padding_1 = {},
221 tag_info.protocol = 1; // TODO(ogniK): Figure out actual values 222 .protocol = 1, // TODO(ogniK): Figure out actual values
222 tag_info.tag_type = 2; 223 .tag_type = 2,
223 ctx.WriteBuffer(&tag_info, sizeof(TagInfo)); 224 .padding_2 = {},
225 };
226 ctx.WriteBuffer(tag_info);
224 rb.Push(RESULT_SUCCESS); 227 rb.Push(RESULT_SUCCESS);
225 } 228 }
226 229
@@ -236,8 +239,8 @@ private:
236 LOG_DEBUG(Service_NFP, "called"); 239 LOG_DEBUG(Service_NFP, "called");
237 240
238 IPC::ResponseBuilder rb{ctx, 2}; 241 IPC::ResponseBuilder rb{ctx, 2};
239 auto amiibo = nfp_interface.GetAmiiboBuffer(); 242 const auto& amiibo = nfp_interface.GetAmiiboBuffer();
240 ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info)); 243 ctx.WriteBuffer(amiibo.model_info);
241 rb.Push(RESULT_SUCCESS); 244 rb.Push(RESULT_SUCCESS);
242 } 245 }
243 246
@@ -283,7 +286,7 @@ private:
283 286
284 CommonInfo common_info{}; 287 CommonInfo common_info{};
285 common_info.application_area_size = 0; 288 common_info.application_area_size = 0;
286 ctx.WriteBuffer(&common_info, sizeof(CommonInfo)); 289 ctx.WriteBuffer(common_info);
287 290
288 IPC::ResponseBuilder rb{ctx, 2}; 291 IPC::ResponseBuilder rb{ctx, 2};
289 rb.Push(RESULT_SUCCESS); 292 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 01ddcdbd6..2e9d95195 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -9,6 +9,7 @@
9#include "core/hle/kernel/writable_event.h" 9#include "core/hle/kernel/writable_event.h"
10#include "core/hle/service/nifm/nifm.h" 10#include "core/hle/service/nifm/nifm.h"
11#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
12#include "core/network/network.h"
12#include "core/settings.h" 13#include "core/settings.h"
13 14
14namespace Service::NIFM { 15namespace Service::NIFM {
@@ -174,6 +175,16 @@ private:
174 IPC::ResponseBuilder rb{ctx, 2}; 175 IPC::ResponseBuilder rb{ctx, 2};
175 rb.Push(RESULT_SUCCESS); 176 rb.Push(RESULT_SUCCESS);
176 } 177 }
178 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
179 LOG_WARNING(Service_NIFM, "(STUBBED) called");
180
181 const auto [ipv4, error] = Network::GetHostIPv4Address();
182 UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS);
183
184 IPC::ResponseBuilder rb{ctx, 3};
185 rb.Push(RESULT_SUCCESS);
186 rb.PushRaw(ipv4);
187 }
177 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { 188 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
178 LOG_DEBUG(Service_NIFM, "called"); 189 LOG_DEBUG(Service_NIFM, "called");
179 190
@@ -235,7 +246,7 @@ IGeneralService::IGeneralService(Core::System& system)
235 {9, nullptr, "SetNetworkProfile"}, 246 {9, nullptr, "SetNetworkProfile"},
236 {10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"}, 247 {10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"},
237 {11, nullptr, "GetScanDataOld"}, 248 {11, nullptr, "GetScanDataOld"},
238 {12, nullptr, "GetCurrentIpAddress"}, 249 {12, &IGeneralService::GetCurrentIpAddress, "GetCurrentIpAddress"},
239 {13, nullptr, "GetCurrentAccessPointOld"}, 250 {13, nullptr, "GetCurrentAccessPointOld"},
240 {14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"}, 251 {14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"},
241 {15, nullptr, "GetCurrentIpConfigInfo"}, 252 {15, nullptr, "GetCurrentIpConfigInfo"},
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 886450be2..58ee1f712 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -5,6 +5,7 @@
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/file_sys/control_metadata.h" 6#include "core/file_sys/control_metadata.h"
7#include "core/file_sys/patch_manager.h" 7#include "core/file_sys/patch_manager.h"
8#include "core/file_sys/vfs.h"
8#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/hle_ipc.h" 10#include "core/hle/kernel/hle_ipc.h"
10#include "core/hle/service/ns/errors.h" 11#include "core/hle/service/ns/errors.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 1b52511a5..0240d6643 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -21,8 +21,9 @@ namespace Service::Nvidia::Devices {
21/// implement the ioctl interface. 21/// implement the ioctl interface.
22class nvdevice { 22class nvdevice {
23public: 23public:
24 explicit nvdevice(Core::System& system) : system{system} {}; 24 explicit nvdevice(Core::System& system) : system{system} {}
25 virtual ~nvdevice() = default; 25 virtual ~nvdevice() = default;
26
26 union Ioctl { 27 union Ioctl {
27 u32_le raw; 28 u32_le raw;
28 BitField<0, 8, u32> cmd; 29 BitField<0, 8, u32> cmd;
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 195421cc0..39bd2a45b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -16,11 +16,12 @@
16#include "video_core/renderer_base.h" 16#include "video_core/renderer_base.h"
17 17
18namespace Service::Nvidia::Devices { 18namespace Service::Nvidia::Devices {
19
19namespace NvErrCodes { 20namespace NvErrCodes {
20enum { 21constexpr u32 Success{};
21 InvalidNmapHandle = -22, 22constexpr u32 OutOfMemory{static_cast<u32>(-12)};
22}; 23constexpr u32 InvalidInput{static_cast<u32>(-22)};
23} 24} // namespace NvErrCodes
24 25
25nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) 26nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
26 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 27 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
@@ -49,8 +50,9 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std:
49 break; 50 break;
50 } 51 }
51 52
52 if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) 53 if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) {
53 return Remap(input, output); 54 return Remap(input, output);
55 }
54 56
55 UNIMPLEMENTED_MSG("Unimplemented ioctl command"); 57 UNIMPLEMENTED_MSG("Unimplemented ioctl command");
56 return 0; 58 return 0;
@@ -59,6 +61,7 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std:
59u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) { 61u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) {
60 IoctlInitalizeEx params{}; 62 IoctlInitalizeEx params{};
61 std::memcpy(&params, input.data(), input.size()); 63 std::memcpy(&params, input.data(), input.size());
64
62 LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); 65 LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size);
63 66
64 return 0; 67 return 0;
@@ -67,53 +70,61 @@ u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& ou
67u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { 70u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
68 IoctlAllocSpace params{}; 71 IoctlAllocSpace params{};
69 std::memcpy(&params, input.data(), input.size()); 72 std::memcpy(&params, input.data(), input.size());
73
70 LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, 74 LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
71 params.page_size, params.flags); 75 params.page_size, params.flags);
72 76
73 auto& gpu = system.GPU(); 77 const auto size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
74 const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; 78 if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) {
75 if (params.flags & 1) { 79 params.offset = *system.GPU().MemoryManager().AllocateFixed(params.offset, size);
76 params.offset = gpu.MemoryManager().AllocateSpace(params.offset, size, 1);
77 } else { 80 } else {
78 params.offset = gpu.MemoryManager().AllocateSpace(size, params.align); 81 params.offset = system.GPU().MemoryManager().Allocate(size, params.align);
82 }
83
84 auto result{NvErrCodes::Success};
85 if (!params.offset) {
86 LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size);
87 result = NvErrCodes::OutOfMemory;
79 } 88 }
80 89
81 std::memcpy(output.data(), &params, output.size()); 90 std::memcpy(output.data(), &params, output.size());
82 return 0; 91 return result;
83} 92}
84 93
85u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { 94u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
86 std::size_t num_entries = input.size() / sizeof(IoctlRemapEntry); 95 const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
87 96
88 LOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries); 97 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
89 98
99 auto result{NvErrCodes::Success};
90 std::vector<IoctlRemapEntry> entries(num_entries); 100 std::vector<IoctlRemapEntry> entries(num_entries);
91 std::memcpy(entries.data(), input.data(), input.size()); 101 std::memcpy(entries.data(), input.data(), input.size());
92 102
93 auto& gpu = system.GPU();
94 for (const auto& entry : entries) { 103 for (const auto& entry : entries) {
95 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", 104 LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
96 entry.offset, entry.nvmap_handle, entry.pages); 105 entry.offset, entry.nvmap_handle, entry.pages);
97 GPUVAddr offset = static_cast<GPUVAddr>(entry.offset) << 0x10; 106
98 auto object = nvmap_dev->GetObject(entry.nvmap_handle); 107 const auto object{nvmap_dev->GetObject(entry.nvmap_handle)};
99 if (!object) { 108 if (!object) {
100 LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle); 109 LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle);
101 std::memcpy(output.data(), entries.data(), output.size()); 110 result = NvErrCodes::InvalidInput;
102 return static_cast<u32>(NvErrCodes::InvalidNmapHandle); 111 break;
103 } 112 }
104 113
105 ASSERT(object->status == nvmap::Object::Status::Allocated); 114 const auto offset{static_cast<GPUVAddr>(entry.offset) << 0x10};
115 const auto size{static_cast<u64>(entry.pages) << 0x10};
116 const auto map_offset{static_cast<u64>(entry.map_offset) << 0x10};
117 const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)};
106 118
107 const u64 size = static_cast<u64>(entry.pages) << 0x10; 119 if (!addr) {
108 ASSERT(size <= object->size); 120 LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!");
109 const u64 map_offset = static_cast<u64>(entry.map_offset) << 0x10; 121 result = NvErrCodes::InvalidInput;
110 122 break;
111 const GPUVAddr returned = 123 }
112 gpu.MemoryManager().MapBufferEx(object->addr + map_offset, offset, size);
113 ASSERT(returned == offset);
114 } 124 }
125
115 std::memcpy(output.data(), entries.data(), output.size()); 126 std::memcpy(output.data(), entries.data(), output.size());
116 return 0; 127 return result;
117} 128}
118 129
119u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { 130u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -126,44 +137,76 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
126 params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, 137 params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size,
127 params.offset); 138 params.offset);
128 139
129 if (!params.nvmap_handle) { 140 const auto object{nvmap_dev->GetObject(params.nvmap_handle)};
130 return 0; 141 if (!object) {
142 LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
143 std::memcpy(output.data(), &params, output.size());
144 return NvErrCodes::InvalidInput;
131 } 145 }
132 146
133 auto object = nvmap_dev->GetObject(params.nvmap_handle);
134 ASSERT(object);
135
136 // We can only map objects that have already been assigned a CPU address.
137 ASSERT(object->status == nvmap::Object::Status::Allocated);
138
139 ASSERT(params.buffer_offset == 0);
140
141 // The real nvservices doesn't make a distinction between handles and ids, and 147 // The real nvservices doesn't make a distinction between handles and ids, and
142 // object can only have one handle and it will be the same as its id. Assert that this is the 148 // object can only have one handle and it will be the same as its id. Assert that this is the
143 // case to prevent unexpected behavior. 149 // case to prevent unexpected behavior.
144 ASSERT(object->id == params.nvmap_handle); 150 ASSERT(object->id == params.nvmap_handle);
145
146 auto& gpu = system.GPU(); 151 auto& gpu = system.GPU();
147 152
148 if (params.flags & 1) { 153 u64 page_size{params.page_size};
149 params.offset = gpu.MemoryManager().MapBufferEx(object->addr, params.offset, object->size); 154 if (!page_size) {
150 } else { 155 page_size = object->align;
151 params.offset = gpu.MemoryManager().MapBufferEx(object->addr, object->size); 156 }
157
158 if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) {
159 if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) {
160 const auto cpu_addr{static_cast<VAddr>(buffer_map->CpuAddr() + params.buffer_offset)};
161 const auto gpu_addr{static_cast<GPUVAddr>(params.offset + params.buffer_offset)};
162
163 if (!gpu.MemoryManager().Map(cpu_addr, gpu_addr, params.mapping_size)) {
164 LOG_CRITICAL(Service_NVDRV,
165 "remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, "
166 "mapping_size = {}, offset={}",
167 params.flags, params.nvmap_handle, params.buffer_offset,
168 params.mapping_size, params.offset);
169
170 std::memcpy(output.data(), &params, output.size());
171 return NvErrCodes::InvalidInput;
172 }
173
174 std::memcpy(output.data(), &params, output.size());
175 return NvErrCodes::Success;
176 } else {
177 LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset);
178
179 std::memcpy(output.data(), &params, output.size());
180 return NvErrCodes::InvalidInput;
181 }
152 } 182 }
153 183
154 // Create a new mapping entry for this operation. 184 // We can only map objects that have already been assigned a CPU address.
155 ASSERT_MSG(buffer_mappings.find(params.offset) == buffer_mappings.end(), 185 ASSERT(object->status == nvmap::Object::Status::Allocated);
156 "Offset is already mapped"); 186
187 const auto physical_address{object->addr + params.buffer_offset};
188 u64 size{params.mapping_size};
189 if (!size) {
190 size = object->size;
191 }
157 192
158 BufferMapping mapping{}; 193 const bool is_alloc{(params.flags & AddressSpaceFlags::FixedOffset) == AddressSpaceFlags::None};
159 mapping.nvmap_handle = params.nvmap_handle; 194 if (is_alloc) {
160 mapping.offset = params.offset; 195 params.offset = gpu.MemoryManager().MapAllocate(physical_address, size, page_size);
161 mapping.size = object->size; 196 } else {
197 params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size);
198 }
162 199
163 buffer_mappings[params.offset] = mapping; 200 auto result{NvErrCodes::Success};
201 if (!params.offset) {
202 LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size);
203 result = NvErrCodes::InvalidInput;
204 } else {
205 AddBufferMap(params.offset, size, physical_address, is_alloc);
206 }
164 207
165 std::memcpy(output.data(), &params, output.size()); 208 std::memcpy(output.data(), &params, output.size());
166 return 0; 209 return result;
167} 210}
168 211
169u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 212u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -172,24 +215,20 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
172 215
173 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); 216 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
174 217
175 const auto itr = buffer_mappings.find(params.offset); 218 if (const auto size{RemoveBufferMap(params.offset)}; size) {
176 if (itr == buffer_mappings.end()) { 219 system.GPU().MemoryManager().Unmap(params.offset, *size);
177 LOG_WARNING(Service_NVDRV, "Tried to unmap an invalid offset 0x{:X}", params.offset); 220 } else {
178 // Hardware tests shows that unmapping an already unmapped buffer always returns successful 221 LOG_ERROR(Service_NVDRV, "invalid offset=0x{:X}", params.offset);
179 // and doesn't fail.
180 return 0;
181 } 222 }
182 223
183 params.offset = system.GPU().MemoryManager().UnmapBuffer(params.offset, itr->second.size);
184 buffer_mappings.erase(itr->second.offset);
185
186 std::memcpy(output.data(), &params, output.size()); 224 std::memcpy(output.data(), &params, output.size());
187 return 0; 225 return NvErrCodes::Success;
188} 226}
189 227
190u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { 228u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
191 IoctlBindChannel params{}; 229 IoctlBindChannel params{};
192 std::memcpy(&params, input.data(), input.size()); 230 std::memcpy(&params, input.data(), input.size());
231
193 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); 232 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
194 233
195 channel = params.fd; 234 channel = params.fd;
@@ -199,6 +238,7 @@ u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& ou
199u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { 238u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
200 IoctlGetVaRegions params{}; 239 IoctlGetVaRegions params{};
201 std::memcpy(&params, input.data(), input.size()); 240 std::memcpy(&params, input.data(), input.size());
241
202 LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, 242 LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
203 params.buf_size); 243 params.buf_size);
204 244
@@ -210,9 +250,43 @@ u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& o
210 params.regions[1].offset = 0x04000000; 250 params.regions[1].offset = 0x04000000;
211 params.regions[1].page_size = 0x10000; 251 params.regions[1].page_size = 0x10000;
212 params.regions[1].pages = 0x1bffff; 252 params.regions[1].pages = 0x1bffff;
253
213 // TODO(ogniK): This probably can stay stubbed but should add support way way later 254 // TODO(ogniK): This probably can stay stubbed but should add support way way later
255
214 std::memcpy(output.data(), &params, output.size()); 256 std::memcpy(output.data(), &params, output.size());
215 return 0; 257 return 0;
216} 258}
217 259
260std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const {
261 const auto end{buffer_mappings.upper_bound(gpu_addr)};
262 for (auto iter{buffer_mappings.begin()}; iter != end; ++iter) {
263 if (gpu_addr >= iter->second.StartAddr() && gpu_addr < iter->second.EndAddr()) {
264 return iter->second;
265 }
266 }
267
268 return std::nullopt;
269}
270
271void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
272 bool is_allocated) {
273 buffer_mappings[gpu_addr] = {gpu_addr, size, cpu_addr, is_allocated};
274}
275
276std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) {
277 if (const auto iter{buffer_mappings.find(gpu_addr)}; iter != buffer_mappings.end()) {
278 std::size_t size{};
279
280 if (iter->second.IsAllocated()) {
281 size = iter->second.Size();
282 }
283
284 buffer_mappings.erase(iter);
285
286 return size;
287 }
288
289 return std::nullopt;
290}
291
218} // namespace Service::Nvidia::Devices 292} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index f79fcc065..9a0cdff0c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -4,9 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <map>
7#include <memory> 8#include <memory>
8#include <unordered_map> 9#include <optional>
9#include <vector> 10#include <vector>
11
12#include "common/common_funcs.h"
10#include "common/common_types.h" 13#include "common/common_types.h"
11#include "common/swap.h" 14#include "common/swap.h"
12#include "core/hle/service/nvdrv/devices/nvdevice.h" 15#include "core/hle/service/nvdrv/devices/nvdevice.h"
@@ -15,6 +18,13 @@ namespace Service::Nvidia::Devices {
15 18
16class nvmap; 19class nvmap;
17 20
21enum class AddressSpaceFlags : u32 {
22 None = 0x0,
23 FixedOffset = 0x1,
24 Remap = 0x100,
25};
26DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags);
27
18class nvhost_as_gpu final : public nvdevice { 28class nvhost_as_gpu final : public nvdevice {
19public: 29public:
20 explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 30 explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
@@ -25,6 +35,45 @@ public:
25 IoctlVersion version) override; 35 IoctlVersion version) override;
26 36
27private: 37private:
38 class BufferMap final {
39 public:
40 constexpr BufferMap() = default;
41
42 constexpr BufferMap(GPUVAddr start_addr, std::size_t size)
43 : start_addr{start_addr}, end_addr{start_addr + size} {}
44
45 constexpr BufferMap(GPUVAddr start_addr, std::size_t size, VAddr cpu_addr,
46 bool is_allocated)
47 : start_addr{start_addr}, end_addr{start_addr + size}, cpu_addr{cpu_addr},
48 is_allocated{is_allocated} {}
49
50 constexpr VAddr StartAddr() const {
51 return start_addr;
52 }
53
54 constexpr VAddr EndAddr() const {
55 return end_addr;
56 }
57
58 constexpr std::size_t Size() const {
59 return end_addr - start_addr;
60 }
61
62 constexpr VAddr CpuAddr() const {
63 return cpu_addr;
64 }
65
66 constexpr bool IsAllocated() const {
67 return is_allocated;
68 }
69
70 private:
71 GPUVAddr start_addr{};
72 GPUVAddr end_addr{};
73 VAddr cpu_addr{};
74 bool is_allocated{};
75 };
76
28 enum class IoctlCommand : u32_le { 77 enum class IoctlCommand : u32_le {
29 IocInitalizeExCommand = 0x40284109, 78 IocInitalizeExCommand = 0x40284109,
30 IocAllocateSpaceCommand = 0xC0184102, 79 IocAllocateSpaceCommand = 0xC0184102,
@@ -49,7 +98,7 @@ private:
49 struct IoctlAllocSpace { 98 struct IoctlAllocSpace {
50 u32_le pages; 99 u32_le pages;
51 u32_le page_size; 100 u32_le page_size;
52 u32_le flags; 101 AddressSpaceFlags flags;
53 INSERT_PADDING_WORDS(1); 102 INSERT_PADDING_WORDS(1);
54 union { 103 union {
55 u64_le offset; 104 u64_le offset;
@@ -69,18 +118,18 @@ private:
69 static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); 118 static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
70 119
71 struct IoctlMapBufferEx { 120 struct IoctlMapBufferEx {
72 u32_le flags; // bit0: fixed_offset, bit2: cacheable 121 AddressSpaceFlags flags; // bit0: fixed_offset, bit2: cacheable
73 u32_le kind; // -1 is default 122 u32_le kind; // -1 is default
74 u32_le nvmap_handle; 123 u32_le nvmap_handle;
75 u32_le page_size; // 0 means don't care 124 u32_le page_size; // 0 means don't care
76 u64_le buffer_offset; 125 s64_le buffer_offset;
77 u64_le mapping_size; 126 u64_le mapping_size;
78 u64_le offset; 127 s64_le offset;
79 }; 128 };
80 static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size"); 129 static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size");
81 130
82 struct IoctlUnmapBuffer { 131 struct IoctlUnmapBuffer {
83 u64_le offset; 132 s64_le offset;
84 }; 133 };
85 static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size"); 134 static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size");
86 135
@@ -106,15 +155,6 @@ private:
106 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, 155 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2,
107 "IoctlGetVaRegions is incorrect size"); 156 "IoctlGetVaRegions is incorrect size");
108 157
109 struct BufferMapping {
110 u64 offset;
111 u64 size;
112 u32 nvmap_handle;
113 };
114
115 /// Map containing the nvmap object mappings in GPU memory.
116 std::unordered_map<u64, BufferMapping> buffer_mappings;
117
118 u32 channel{}; 158 u32 channel{};
119 159
120 u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output); 160 u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
@@ -125,7 +165,14 @@ private:
125 u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output); 165 u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
126 u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); 166 u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
127 167
168 std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
169 void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
170 std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
171
128 std::shared_ptr<nvmap> nvmap_dev; 172 std::shared_ptr<nvmap> nvmap_dev;
173
174 // This is expected to be ordered, therefore we must use a map, not unordered_map
175 std::map<GPUVAddr, BufferMap> buffer_mappings;
129}; 176};
130 177
131} // namespace Service::Nvidia::Devices 178} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index bdae8b887..fcb612864 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -22,6 +22,18 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::
22 switch (static_cast<IoctlCommand>(command.raw)) { 22 switch (static_cast<IoctlCommand>(command.raw)) {
23 case IoctlCommand::IocSetNVMAPfdCommand: 23 case IoctlCommand::IocSetNVMAPfdCommand:
24 return SetNVMAPfd(input, output); 24 return SetNVMAPfd(input, output);
25 case IoctlCommand::IocSubmit:
26 return Submit(input, output);
27 case IoctlCommand::IocGetSyncpoint:
28 return GetSyncpoint(input, output);
29 case IoctlCommand::IocGetWaitbase:
30 return GetWaitbase(input, output);
31 case IoctlCommand::IocMapBuffer:
32 return MapBuffer(input, output);
33 case IoctlCommand::IocMapBufferEx:
34 return MapBufferEx(input, output);
35 case IoctlCommand::IocUnmapBufferEx:
36 return UnmapBufferEx(input, output);
25 } 37 }
26 38
27 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 39 UNIMPLEMENTED_MSG("Unimplemented ioctl");
@@ -30,11 +42,67 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::
30 42
31u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { 43u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
32 IoctlSetNvmapFD params{}; 44 IoctlSetNvmapFD params{};
33 std::memcpy(&params, input.data(), input.size()); 45 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
34 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 46 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
35 47
36 nvmap_fd = params.nvmap_fd; 48 nvmap_fd = params.nvmap_fd;
37 return 0; 49 return 0;
38} 50}
39 51
52u32 nvhost_nvdec::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
53 IoctlSubmit params{};
54 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
55 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
56 std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
57 return 0;
58}
59
60u32 nvhost_nvdec::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
61 IoctlGetSyncpoint params{};
62 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
63 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
64 params.value = 0; // Seems to be hard coded at 0
65 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
66 return 0;
67}
68
69u32 nvhost_nvdec::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
70 IoctlGetWaitbase params{};
71 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
72 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
73 params.value = 0; // Seems to be hard coded at 0
74 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
75 return 0;
76}
77
78u32 nvhost_nvdec::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
79 IoctlMapBuffer params{};
80 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
81 LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
82 params.address_1);
83 params.address_1 = 0;
84 params.address_2 = 0;
85 std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
86 return 0;
87}
88
89u32 nvhost_nvdec::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
90 IoctlMapBufferEx params{};
91 std::memcpy(&params, input.data(), sizeof(IoctlMapBufferEx));
92 LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
93 params.address_1);
94 params.address_1 = 0;
95 params.address_2 = 0;
96 std::memcpy(output.data(), &params, sizeof(IoctlMapBufferEx));
97 return 0;
98}
99
100u32 nvhost_nvdec::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
101 IoctlUnmapBufferEx params{};
102 std::memcpy(&params, input.data(), sizeof(IoctlUnmapBufferEx));
103 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
104 std::memcpy(output.data(), &params, sizeof(IoctlUnmapBufferEx));
105 return 0;
106}
107
40} // namespace Service::Nvidia::Devices 108} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index cbdac8069..4332db118 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -23,16 +23,66 @@ public:
23private: 23private:
24 enum class IoctlCommand : u32_le { 24 enum class IoctlCommand : u32_le {
25 IocSetNVMAPfdCommand = 0x40044801, 25 IocSetNVMAPfdCommand = 0x40044801,
26 IocSubmit = 0xC0400001,
27 IocGetSyncpoint = 0xC0080002,
28 IocGetWaitbase = 0xC0080003,
29 IocMapBuffer = 0xC01C0009,
30 IocMapBufferEx = 0xC0A40009,
31 IocUnmapBufferEx = 0xC0A4000A,
26 }; 32 };
27 33
28 struct IoctlSetNvmapFD { 34 struct IoctlSetNvmapFD {
29 u32_le nvmap_fd; 35 u32_le nvmap_fd;
30 }; 36 };
31 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); 37 static_assert(sizeof(IoctlSetNvmapFD) == 0x4, "IoctlSetNvmapFD is incorrect size");
38
39 struct IoctlSubmit {
40 INSERT_PADDING_BYTES(0x40); // TODO(DarkLordZach): RE this structure
41 };
42 static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit has incorrect size");
43
44 struct IoctlGetSyncpoint {
45 u32 unknown; // seems to be ignored? Nintendo added this
46 u32 value;
47 };
48 static_assert(sizeof(IoctlGetSyncpoint) == 0x08, "IoctlGetSyncpoint has incorrect size");
49
50 struct IoctlGetWaitbase {
51 u32 unknown; // seems to be ignored? Nintendo added this
52 u32 value;
53 };
54 static_assert(sizeof(IoctlGetWaitbase) == 0x08, "IoctlGetWaitbase has incorrect size");
55
56 struct IoctlMapBuffer {
57 u32 unknown;
58 u32 address_1;
59 u32 address_2;
60 INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure
61 };
62 static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size");
63
64 struct IoctlMapBufferEx {
65 u32 unknown;
66 u32 address_1;
67 u32 address_2;
68 INSERT_PADDING_BYTES(0x98); // TODO(DarkLordZach): RE this structure
69 };
70 static_assert(sizeof(IoctlMapBufferEx) == 0xA4, "IoctlMapBufferEx has incorrect size");
71
72 struct IoctlUnmapBufferEx {
73 INSERT_PADDING_BYTES(0xA4); // TODO(DarkLordZach): RE this structure
74 };
75 static_assert(sizeof(IoctlUnmapBufferEx) == 0xA4, "IoctlUnmapBufferEx has incorrect size");
32 76
33 u32_le nvmap_fd{}; 77 u32_le nvmap_fd{};
34 78
35 u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); 79 u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
80 u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
81 u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
82 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
83 u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
84 u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
85 u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
36}; 86};
37 87
38} // namespace Service::Nvidia::Devices 88} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index c695b8863..9da19ad56 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -22,6 +22,18 @@ u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
22 switch (static_cast<IoctlCommand>(command.raw)) { 22 switch (static_cast<IoctlCommand>(command.raw)) {
23 case IoctlCommand::IocSetNVMAPfdCommand: 23 case IoctlCommand::IocSetNVMAPfdCommand:
24 return SetNVMAPfd(input, output); 24 return SetNVMAPfd(input, output);
25 case IoctlCommand::IocSubmit:
26 return Submit(input, output);
27 case IoctlCommand::IocGetSyncpoint:
28 return GetSyncpoint(input, output);
29 case IoctlCommand::IocGetWaitbase:
30 return GetWaitbase(input, output);
31 case IoctlCommand::IocMapBuffer:
32 return MapBuffer(input, output);
33 case IoctlCommand::IocMapBufferEx:
34 return MapBuffer(input, output);
35 case IoctlCommand::IocUnmapBufferEx:
36 return UnmapBufferEx(input, output);
25 } 37 }
26 38
27 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 39 UNIMPLEMENTED_MSG("Unimplemented ioctl");
@@ -30,11 +42,71 @@ u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
30 42
31u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { 43u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
32 IoctlSetNvmapFD params{}; 44 IoctlSetNvmapFD params{};
33 std::memcpy(&params, input.data(), input.size()); 45 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
34 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 46 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
35 47
36 nvmap_fd = params.nvmap_fd; 48 nvmap_fd = params.nvmap_fd;
37 return 0; 49 return 0;
38} 50}
39 51
52u32 nvhost_vic::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
53 IoctlSubmit params{};
54 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
55 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
56
57 // Workaround for Luigi's Mansion 3, as nvhost_vic is not implemented for asynch GPU
58 params.command_buffer = {};
59
60 std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
61 return 0;
62}
63
64u32 nvhost_vic::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
65 IoctlGetSyncpoint params{};
66 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
67 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
68 params.value = 0; // Seems to be hard coded at 0
69 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
70 return 0;
71}
72
73u32 nvhost_vic::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
74 IoctlGetWaitbase params{};
75 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
76 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
77 params.value = 0; // Seems to be hard coded at 0
78 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
79 return 0;
80}
81
82u32 nvhost_vic::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
83 IoctlMapBuffer params{};
84 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
85 LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
86 params.address_1);
87 params.address_1 = 0;
88 params.address_2 = 0;
89 std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
90 return 0;
91}
92
93u32 nvhost_vic::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
94 IoctlMapBufferEx params{};
95 std::memcpy(&params, input.data(), sizeof(IoctlMapBufferEx));
96 LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
97 params.address_1);
98 params.address_1 = 0;
99 params.address_2 = 0;
100 std::memcpy(output.data(), &params, sizeof(IoctlMapBufferEx));
101 return 0;
102}
103
104u32 nvhost_vic::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
105 IoctlUnmapBufferEx params{};
106 std::memcpy(&params, input.data(), sizeof(IoctlUnmapBufferEx));
107 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
108 std::memcpy(output.data(), &params, sizeof(IoctlUnmapBufferEx));
109 return 0;
110}
111
40} // namespace Service::Nvidia::Devices 112} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index bec32bea1..a7bb7bbd5 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <vector> 8#include <vector>
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/swap.h" 10#include "common/swap.h"
@@ -23,6 +24,12 @@ public:
23private: 24private:
24 enum class IoctlCommand : u32_le { 25 enum class IoctlCommand : u32_le {
25 IocSetNVMAPfdCommand = 0x40044801, 26 IocSetNVMAPfdCommand = 0x40044801,
27 IocSubmit = 0xC0400001,
28 IocGetSyncpoint = 0xC0080002,
29 IocGetWaitbase = 0xC0080003,
30 IocMapBuffer = 0xC01C0009,
31 IocMapBufferEx = 0xC03C0009,
32 IocUnmapBufferEx = 0xC03C000A,
26 }; 33 };
27 34
28 struct IoctlSetNvmapFD { 35 struct IoctlSetNvmapFD {
@@ -30,9 +37,65 @@ private:
30 }; 37 };
31 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); 38 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
32 39
40 struct IoctlSubmitCommandBuffer {
41 u32 id;
42 u32 offset;
43 u32 count;
44 };
45 static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC,
46 "IoctlSubmitCommandBuffer is incorrect size");
47
48 struct IoctlSubmit {
49 u32 command_buffer_count;
50 u32 relocations_count;
51 u32 syncpt_count;
52 u32 wait_count;
53 std::array<IoctlSubmitCommandBuffer, 4> command_buffer;
54 };
55 static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit is incorrect size");
56
57 struct IoctlGetSyncpoint {
58 u32 unknown; // seems to be ignored? Nintendo added this
59 u32 value;
60 };
61 static_assert(sizeof(IoctlGetSyncpoint) == 0x8, "IoctlGetSyncpoint is incorrect size");
62
63 struct IoctlGetWaitbase {
64 u32 unknown; // seems to be ignored? Nintendo added this
65 u32 value;
66 };
67 static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size");
68
69 struct IoctlMapBuffer {
70 u32 unknown;
71 u32 address_1;
72 u32 address_2;
73 INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure
74 };
75 static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size");
76
77 struct IoctlMapBufferEx {
78 u32 unknown;
79 u32 address_1;
80 u32 address_2;
81 INSERT_PADDING_BYTES(0x30); // TODO(DarkLordZach): RE this structure
82 };
83 static_assert(sizeof(IoctlMapBufferEx) == 0x3C, "IoctlMapBufferEx is incorrect size");
84
85 struct IoctlUnmapBufferEx {
86 INSERT_PADDING_BYTES(0x3C); // TODO(DarkLordZach): RE this structure
87 };
88 static_assert(sizeof(IoctlUnmapBufferEx) == 0x3C, "IoctlUnmapBufferEx is incorrect size");
89
33 u32_le nvmap_fd{}; 90 u32_le nvmap_fd{};
34 91
35 u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); 92 u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
93 u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
94 u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
95 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
96 u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
97 u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
98 u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
36}; 99};
37 100
38} // namespace Service::Nvidia::Devices 101} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 8c742316c..9436e16ad 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -18,7 +18,12 @@ enum {
18}; 18};
19} 19}
20 20
21nvmap::nvmap(Core::System& system) : nvdevice(system) {} 21nvmap::nvmap(Core::System& system) : nvdevice(system) {
22 // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to
23 // represent this.
24 CreateObject(0);
25}
26
22nvmap::~nvmap() = default; 27nvmap::~nvmap() = default;
23 28
24VAddr nvmap::GetObjectAddress(u32 handle) const { 29VAddr nvmap::GetObjectAddress(u32 handle) const {
@@ -50,6 +55,21 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<
50 return 0; 55 return 0;
51} 56}
52 57
58u32 nvmap::CreateObject(u32 size) {
59 // Create a new nvmap object and obtain a handle to it.
60 auto object = std::make_shared<Object>();
61 object->id = next_id++;
62 object->size = size;
63 object->status = Object::Status::Created;
64 object->refcount = 1;
65
66 const u32 handle = next_handle++;
67
68 handles.insert_or_assign(handle, std::move(object));
69
70 return handle;
71}
72
53u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { 73u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
54 IocCreateParams params; 74 IocCreateParams params;
55 std::memcpy(&params, input.data(), sizeof(params)); 75 std::memcpy(&params, input.data(), sizeof(params));
@@ -59,17 +79,8 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
59 LOG_ERROR(Service_NVDRV, "Size is 0"); 79 LOG_ERROR(Service_NVDRV, "Size is 0");
60 return static_cast<u32>(NvErrCodes::InvalidValue); 80 return static_cast<u32>(NvErrCodes::InvalidValue);
61 } 81 }
62 // Create a new nvmap object and obtain a handle to it.
63 auto object = std::make_shared<Object>();
64 object->id = next_id++;
65 object->size = params.size;
66 object->status = Object::Status::Created;
67 object->refcount = 1;
68
69 u32 handle = next_handle++;
70 handles[handle] = std::move(object);
71 82
72 params.handle = handle; 83 params.handle = CreateObject(params.size);
73 84
74 std::memcpy(output.data(), &params, sizeof(params)); 85 std::memcpy(output.data(), &params, sizeof(params));
75 return 0; 86 return 0;
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 73c2e8809..84624be00 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -49,10 +49,10 @@ public:
49 49
50private: 50private:
51 /// Id to use for the next handle that is created. 51 /// Id to use for the next handle that is created.
52 u32 next_handle = 1; 52 u32 next_handle = 0;
53 53
54 /// Id to use for the next object that is created. 54 /// Id to use for the next object that is created.
55 u32 next_id = 1; 55 u32 next_id = 0;
56 56
57 /// Mapping of currently allocated handles to the objects they represent. 57 /// Mapping of currently allocated handles to the objects they represent.
58 std::unordered_map<u32, std::shared_ptr<Object>> handles; 58 std::unordered_map<u32, std::shared_ptr<Object>> handles;
@@ -119,6 +119,8 @@ private:
119 }; 119 };
120 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); 120 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
121 121
122 u32 CreateObject(u32 size);
123
122 u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output); 124 u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
123 u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); 125 u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
124 u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output); 126 u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index deaf0808b..88fbfa9b0 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -60,24 +60,24 @@ void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) {
60 60
61 if (ctrl.must_delay) { 61 if (ctrl.must_delay) {
62 ctrl.fresh_call = false; 62 ctrl.fresh_call = false;
63 ctx.SleepClientThread("NVServices::DelayedResponse", ctrl.timeout, 63 ctx.SleepClientThread(
64 [=](std::shared_ptr<Kernel::Thread> thread, 64 "NVServices::DelayedResponse", ctrl.timeout,
65 Kernel::HLERequestContext& ctx, 65 [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
66 Kernel::ThreadWakeupReason reason) { 66 Kernel::ThreadWakeupReason reason) {
67 IoctlCtrl ctrl2{ctrl}; 67 IoctlCtrl ctrl2{ctrl};
68 std::vector<u8> tmp_output = output; 68 std::vector<u8> tmp_output = output;
69 std::vector<u8> tmp_output2 = output2; 69 std::vector<u8> tmp_output2 = output2;
70 u32 result = nvdrv->Ioctl(fd, command, input, input2, tmp_output, 70 const u32 ioctl_result = nvdrv->Ioctl(fd, command, input, input2, tmp_output,
71 tmp_output2, ctrl2, version); 71 tmp_output2, ctrl2, version);
72 ctx.WriteBuffer(tmp_output, 0); 72 ctx_.WriteBuffer(tmp_output, 0);
73 if (version == IoctlVersion::Version3) { 73 if (version == IoctlVersion::Version3) {
74 ctx.WriteBuffer(tmp_output2, 1); 74 ctx_.WriteBuffer(tmp_output2, 1);
75 } 75 }
76 IPC::ResponseBuilder rb{ctx, 3}; 76 IPC::ResponseBuilder rb{ctx_, 3};
77 rb.Push(RESULT_SUCCESS); 77 rb.Push(RESULT_SUCCESS);
78 rb.Push(result); 78 rb.Push(ioctl_result);
79 }, 79 },
80 nvdrv->GetEventWriteable(ctrl.event_id)); 80 nvdrv->GetEventWriteable(ctrl.event_id));
81 } else { 81 } else {
82 ctx.WriteBuffer(output); 82 ctx.WriteBuffer(output);
83 if (version == IoctlVersion::Version3) { 83 if (version == IoctlVersion::Version3) {
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index d7a1bef91..7706a5590 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -54,7 +54,7 @@ struct EventInterface {
54 } 54 }
55 mask = mask >> 1; 55 mask = mask >> 1;
56 } 56 }
57 return {}; 57 return std::nullopt;
58 } 58 }
59 void SetEventStatus(const u32 event_id, EventState new_status) { 59 void SetEventStatus(const u32 event_id, EventState new_status) {
60 EventState old_status = status[event_id]; 60 EventState old_status = status[event_id];
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index caca80dde..637b310d7 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -24,13 +24,13 @@ BufferQueue::~BufferQueue() = default;
24void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { 24void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
25 LOG_WARNING(Service, "Adding graphics buffer {}", slot); 25 LOG_WARNING(Service, "Adding graphics buffer {}", slot);
26 26
27 Buffer buffer{};
28 buffer.slot = slot;
29 buffer.igbp_buffer = igbp_buffer;
30 buffer.status = Buffer::Status::Free;
31 free_buffers.push_back(slot); 27 free_buffers.push_back(slot);
28 queue.push_back({
29 .slot = slot,
30 .status = Buffer::Status::Free,
31 .igbp_buffer = igbp_buffer,
32 });
32 33
33 queue.emplace_back(buffer);
34 buffer_wait_event.writable->Signal(); 34 buffer_wait_event.writable->Signal();
35} 35}
36 36
@@ -38,7 +38,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue
38 u32 height) { 38 u32 height) {
39 39
40 if (free_buffers.empty()) { 40 if (free_buffers.empty()) {
41 return {}; 41 return std::nullopt;
42 } 42 }
43 43
44 auto f_itr = free_buffers.begin(); 44 auto f_itr = free_buffers.begin();
@@ -69,7 +69,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue
69 } 69 }
70 70
71 if (itr == queue.end()) { 71 if (itr == queue.end()) {
72 return {}; 72 return std::nullopt;
73 } 73 }
74 74
75 itr->status = Buffer::Status::Dequeued; 75 itr->status = Buffer::Status::Dequeued;
@@ -103,14 +103,15 @@ std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::Ac
103 auto itr = queue.end(); 103 auto itr = queue.end();
104 // Iterate to find a queued buffer matching the requested slot. 104 // Iterate to find a queued buffer matching the requested slot.
105 while (itr == queue.end() && !queue_sequence.empty()) { 105 while (itr == queue.end() && !queue_sequence.empty()) {
106 u32 slot = queue_sequence.front(); 106 const u32 slot = queue_sequence.front();
107 itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) { 107 itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) {
108 return buffer.status == Buffer::Status::Queued && buffer.slot == slot; 108 return buffer.status == Buffer::Status::Queued && buffer.slot == slot;
109 }); 109 });
110 queue_sequence.pop_front(); 110 queue_sequence.pop_front();
111 } 111 }
112 if (itr == queue.end()) 112 if (itr == queue.end()) {
113 return {}; 113 return std::nullopt;
114 }
114 itr->status = Buffer::Status::Acquired; 115 itr->status = Buffer::Status::Acquired;
115 return *itr; 116 return *itr;
116} 117}
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 2f44d3779..c64673dba 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -28,8 +28,7 @@
28 28
29namespace Service::NVFlinger { 29namespace Service::NVFlinger {
30 30
31constexpr s64 frame_ticks = static_cast<s64>(1000000000 / 60); 31constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60};
32constexpr s64 frame_ticks_30fps = static_cast<s64>(1000000000 / 30);
33 32
34void NVFlinger::VSyncThread(NVFlinger& nv_flinger) { 33void NVFlinger::VSyncThread(NVFlinger& nv_flinger) {
35 nv_flinger.SplitVSync(); 34 nv_flinger.SplitVSync();
@@ -67,20 +66,24 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) {
67 guard = std::make_shared<std::mutex>(); 66 guard = std::make_shared<std::mutex>();
68 67
69 // Schedule the screen composition events 68 // Schedule the screen composition events
70 composition_event = 69 composition_event = Core::Timing::CreateEvent(
71 Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 ns_late) { 70 "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) {
72 Lock(); 71 const auto guard = Lock();
73 Compose(); 72 Compose();
74 const auto ticks = GetNextTicks(); 73
75 this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - ns_late), 74 const auto ticks = std::chrono::nanoseconds{GetNextTicks()};
76 composition_event); 75 const auto ticks_delta = ticks - ns_late;
76 const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta);
77
78 this->system.CoreTiming().ScheduleEvent(future_ns, composition_event);
77 }); 79 });
80
78 if (system.IsMulticore()) { 81 if (system.IsMulticore()) {
79 is_running = true; 82 is_running = true;
80 wait_event = std::make_unique<Common::Event>(); 83 wait_event = std::make_unique<Common::Event>();
81 vsync_thread = std::make_unique<std::thread>(VSyncThread, std::ref(*this)); 84 vsync_thread = std::make_unique<std::thread>(VSyncThread, std::ref(*this));
82 } else { 85 } else {
83 system.CoreTiming().ScheduleEvent(frame_ticks, composition_event); 86 system.CoreTiming().ScheduleEvent(frame_ns, composition_event);
84 } 87 }
85} 88}
86 89
@@ -111,7 +114,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
111 [&](const VI::Display& display) { return display.GetName() == name; }); 114 [&](const VI::Display& display) { return display.GetName() == name; });
112 115
113 if (itr == displays.end()) { 116 if (itr == displays.end()) {
114 return {}; 117 return std::nullopt;
115 } 118 }
116 119
117 return itr->GetID(); 120 return itr->GetID();
@@ -121,7 +124,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
121 auto* const display = FindDisplay(display_id); 124 auto* const display = FindDisplay(display_id);
122 125
123 if (display == nullptr) { 126 if (display == nullptr) {
124 return {}; 127 return std::nullopt;
125 } 128 }
126 129
127 const u64 layer_id = next_layer_id++; 130 const u64 layer_id = next_layer_id++;
@@ -141,7 +144,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co
141 const auto* const layer = FindLayer(display_id, layer_id); 144 const auto* const layer = FindLayer(display_id, layer_id);
142 145
143 if (layer == nullptr) { 146 if (layer == nullptr) {
144 return {}; 147 return std::nullopt;
145 } 148 }
146 149
147 return layer->GetBufferQueue().GetId(); 150 return layer->GetBufferQueue().GetId();
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index e4959a9af..1ebe949c0 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -54,12 +54,12 @@ public:
54 /// Opens the specified display and returns the ID. 54 /// Opens the specified display and returns the ID.
55 /// 55 ///
56 /// If an invalid display name is provided, then an empty optional is returned. 56 /// If an invalid display name is provided, then an empty optional is returned.
57 std::optional<u64> OpenDisplay(std::string_view name); 57 [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name);
58 58
59 /// Creates a layer on the specified display and returns the layer ID. 59 /// Creates a layer on the specified display and returns the layer ID.
60 /// 60 ///
61 /// If an invalid display ID is specified, then an empty optional is returned. 61 /// If an invalid display ID is specified, then an empty optional is returned.
62 std::optional<u64> CreateLayer(u64 display_id); 62 [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id);
63 63
64 /// Closes a layer on all displays for the given layer ID. 64 /// Closes a layer on all displays for the given layer ID.
65 void CloseLayer(u64 layer_id); 65 void CloseLayer(u64 layer_id);
@@ -67,41 +67,41 @@ public:
67 /// Finds the buffer queue ID of the specified layer in the specified display. 67 /// Finds the buffer queue ID of the specified layer in the specified display.
68 /// 68 ///
69 /// If an invalid display ID or layer ID is provided, then an empty optional is returned. 69 /// If an invalid display ID or layer ID is provided, then an empty optional is returned.
70 std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const; 70 [[nodiscard]] std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const;
71 71
72 /// Gets the vsync event for the specified display. 72 /// Gets the vsync event for the specified display.
73 /// 73 ///
74 /// If an invalid display ID is provided, then nullptr is returned. 74 /// If an invalid display ID is provided, then nullptr is returned.
75 std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; 75 [[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const;
76 76
77 /// Obtains a buffer queue identified by the ID. 77 /// Obtains a buffer queue identified by the ID.
78 BufferQueue& FindBufferQueue(u32 id); 78 [[nodiscard]] BufferQueue& FindBufferQueue(u32 id);
79 79
80 /// Obtains a buffer queue identified by the ID. 80 /// Obtains a buffer queue identified by the ID.
81 const BufferQueue& FindBufferQueue(u32 id) const; 81 [[nodiscard]] const BufferQueue& FindBufferQueue(u32 id) const;
82 82
83 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when 83 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
84 /// finished. 84 /// finished.
85 void Compose(); 85 void Compose();
86 86
87 s64 GetNextTicks() const; 87 [[nodiscard]] s64 GetNextTicks() const;
88 88
89 std::unique_lock<std::mutex> Lock() { 89 [[nodiscard]] std::unique_lock<std::mutex> Lock() const {
90 return std::unique_lock{*guard}; 90 return std::unique_lock{*guard};
91 } 91 }
92 92
93private: 93private:
94 /// Finds the display identified by the specified ID. 94 /// Finds the display identified by the specified ID.
95 VI::Display* FindDisplay(u64 display_id); 95 [[nodiscard]] VI::Display* FindDisplay(u64 display_id);
96 96
97 /// Finds the display identified by the specified ID. 97 /// Finds the display identified by the specified ID.
98 const VI::Display* FindDisplay(u64 display_id) const; 98 [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const;
99 99
100 /// Finds the layer identified by the specified ID in the desired display. 100 /// Finds the layer identified by the specified ID in the desired display.
101 VI::Layer* FindLayer(u64 display_id, u64 layer_id); 101 [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
102 102
103 /// Finds the layer identified by the specified ID in the desired display. 103 /// Finds the layer identified by the specified ID in the desired display.
104 const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; 104 [[nodiscard]] const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const;
105 105
106 static void VSyncThread(NVFlinger& nv_flinger); 106 static void VSyncThread(NVFlinger& nv_flinger);
107 107
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index fa5347af9..ba9159ee0 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -89,8 +89,6 @@ namespace Service {
89 return function_string; 89 return function_string;
90} 90}
91 91
92////////////////////////////////////////////////////////////////////////////////////////////////////
93
94ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions, 92ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions,
95 InvokerFn* handler_invoker) 93 InvokerFn* handler_invoker)
96 : service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {} 94 : service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {}
@@ -105,10 +103,9 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager)
105 port_installed = true; 103 port_installed = true;
106} 104}
107 105
108void ServiceFrameworkBase::InstallAsNamedPort() { 106void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
109 ASSERT(!port_installed); 107 ASSERT(!port_installed);
110 108
111 auto& kernel = Core::System::GetInstance().Kernel();
112 auto [server_port, client_port] = 109 auto [server_port, client_port] =
113 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); 110 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
114 server_port->SetHleHandler(shared_from_this()); 111 server_port->SetHleHandler(shared_from_this());
@@ -116,10 +113,9 @@ void ServiceFrameworkBase::InstallAsNamedPort() {
116 port_installed = true; 113 port_installed = true;
117} 114}
118 115
119std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { 116std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort(Kernel::KernelCore& kernel) {
120 ASSERT(!port_installed); 117 ASSERT(!port_installed);
121 118
122 auto& kernel = Core::System::GetInstance().Kernel();
123 auto [server_port, client_port] = 119 auto [server_port, client_port] =
124 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); 120 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
125 auto port = MakeResult(std::move(server_port)).Unwrap(); 121 auto port = MakeResult(std::move(server_port)).Unwrap();
@@ -191,9 +187,6 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
191 return RESULT_SUCCESS; 187 return RESULT_SUCCESS;
192} 188}
193 189
194////////////////////////////////////////////////////////////////////////////////////////////////////
195// Module interface
196
197/// Initialize ServiceManager 190/// Initialize ServiceManager
198void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { 191void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
199 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 192 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
@@ -246,7 +239,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
246 PSC::InstallInterfaces(*sm); 239 PSC::InstallInterfaces(*sm);
247 PSM::InstallInterfaces(*sm); 240 PSM::InstallInterfaces(*sm);
248 Set::InstallInterfaces(*sm); 241 Set::InstallInterfaces(*sm);
249 Sockets::InstallInterfaces(*sm); 242 Sockets::InstallInterfaces(*sm, system);
250 SPL::InstallInterfaces(*sm); 243 SPL::InstallInterfaces(*sm);
251 SSL::InstallInterfaces(*sm); 244 SSL::InstallInterfaces(*sm);
252 Time::InstallInterfaces(system); 245 Time::InstallInterfaces(system);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 022d885b6..a01ef3353 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -63,9 +63,9 @@ public:
63 /// Creates a port pair and registers this service with the given ServiceManager. 63 /// Creates a port pair and registers this service with the given ServiceManager.
64 void InstallAsService(SM::ServiceManager& service_manager); 64 void InstallAsService(SM::ServiceManager& service_manager);
65 /// Creates a port pair and registers it on the kernel's global port registry. 65 /// Creates a port pair and registers it on the kernel's global port registry.
66 void InstallAsNamedPort(); 66 void InstallAsNamedPort(Kernel::KernelCore& kernel);
67 /// Creates and returns an unregistered port for the service. 67 /// Creates and returns an unregistered port for the service.
68 std::shared_ptr<Kernel::ClientPort> CreatePort(); 68 std::shared_ptr<Kernel::ClientPort> CreatePort(Kernel::KernelCore& kernel);
69 69
70 void InvokeRequest(Kernel::HLERequestContext& ctx); 70 void InvokeRequest(Kernel::HLERequestContext& ctx);
71 71
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 34fe2fd82..e64777668 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -106,7 +106,7 @@ void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
106 106
107 IPC::ResponseBuilder rb{ctx, 2}; 107 IPC::ResponseBuilder rb{ctx, 2};
108 rb.Push(RESULT_SUCCESS); 108 rb.Push(RESULT_SUCCESS);
109 ctx.WriteBuffer(&layout, sizeof(KeyboardLayout)); 109 ctx.WriteBuffer(layout);
110} 110}
111} // Anonymous namespace 111} // Anonymous namespace
112 112
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index d872de16c..9c1da361b 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -19,7 +19,7 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::SM, 4);
19constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6); 19constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6);
20constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); 20constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
21 21
22ServiceManager::ServiceManager() = default; 22ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {}
23ServiceManager::~ServiceManager() = default; 23ServiceManager::~ServiceManager() = default;
24 24
25void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { 25void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
@@ -27,11 +27,11 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
27} 27}
28 28
29static ResultCode ValidateServiceName(const std::string& name) { 29static ResultCode ValidateServiceName(const std::string& name) {
30 if (name.size() <= 0 || name.size() > 8) { 30 if (name.empty() || name.size() > 8) {
31 LOG_ERROR(Service_SM, "Invalid service name! service={}", name); 31 LOG_ERROR(Service_SM, "Invalid service name! service={}", name);
32 return ERR_INVALID_NAME; 32 return ERR_INVALID_NAME;
33 } 33 }
34 if (name.find('\0') != std::string::npos) { 34 if (name.rfind('\0') != std::string::npos) {
35 LOG_ERROR(Service_SM, "A non null terminated service was passed"); 35 LOG_ERROR(Service_SM, "A non null terminated service was passed");
36 return ERR_INVALID_NAME; 36 return ERR_INVALID_NAME;
37 } 37 }
@@ -43,13 +43,13 @@ void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self,
43 ASSERT(self->sm_interface.expired()); 43 ASSERT(self->sm_interface.expired());
44 44
45 auto sm = std::make_shared<SM>(self, kernel); 45 auto sm = std::make_shared<SM>(self, kernel);
46 sm->InstallAsNamedPort(); 46 sm->InstallAsNamedPort(kernel);
47 self->sm_interface = sm; 47 self->sm_interface = sm;
48 self->controller_interface = std::make_unique<Controller>(); 48 self->controller_interface = std::make_unique<Controller>();
49} 49}
50 50
51ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService( 51ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService(std::string name,
52 std::string name, unsigned int max_sessions) { 52 u32 max_sessions) {
53 53
54 CASCADE_CODE(ValidateServiceName(name)); 54 CASCADE_CODE(ValidateServiceName(name));
55 55
@@ -58,7 +58,6 @@ ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService(
58 return ERR_ALREADY_REGISTERED; 58 return ERR_ALREADY_REGISTERED;
59 } 59 }
60 60
61 auto& kernel = Core::System::GetInstance().Kernel();
62 auto [server_port, client_port] = 61 auto [server_port, client_port] =
63 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name); 62 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name);
64 63
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index b06d2f103..6790c86f0 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -9,6 +9,7 @@
9#include <type_traits> 9#include <type_traits>
10#include <unordered_map> 10#include <unordered_map>
11 11
12#include "common/concepts.h"
12#include "core/hle/kernel/client_port.h" 13#include "core/hle/kernel/client_port.h"
13#include "core/hle/kernel/object.h" 14#include "core/hle/kernel/object.h"
14#include "core/hle/kernel/server_port.h" 15#include "core/hle/kernel/server_port.h"
@@ -47,19 +48,17 @@ class ServiceManager {
47public: 48public:
48 static void InstallInterfaces(std::shared_ptr<ServiceManager> self, Kernel::KernelCore& kernel); 49 static void InstallInterfaces(std::shared_ptr<ServiceManager> self, Kernel::KernelCore& kernel);
49 50
50 ServiceManager(); 51 explicit ServiceManager(Kernel::KernelCore& kernel_);
51 ~ServiceManager(); 52 ~ServiceManager();
52 53
53 ResultVal<std::shared_ptr<Kernel::ServerPort>> RegisterService(std::string name, 54 ResultVal<std::shared_ptr<Kernel::ServerPort>> RegisterService(std::string name,
54 unsigned int max_sessions); 55 u32 max_sessions);
55 ResultCode UnregisterService(const std::string& name); 56 ResultCode UnregisterService(const std::string& name);
56 ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name); 57 ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name);
57 ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name); 58 ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name);
58 59
59 template <typename T> 60 template <Common::DerivedFrom<Kernel::SessionRequestHandler> T>
60 std::shared_ptr<T> GetService(const std::string& service_name) const { 61 std::shared_ptr<T> GetService(const std::string& service_name) const {
61 static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>,
62 "Not a base of ServiceFrameworkBase");
63 auto service = registered_services.find(service_name); 62 auto service = registered_services.find(service_name);
64 if (service == registered_services.end()) { 63 if (service == registered_services.end()) {
65 LOG_DEBUG(Service, "Can't find service: {}", service_name); 64 LOG_DEBUG(Service, "Can't find service: {}", service_name);
@@ -80,6 +79,9 @@ private:
80 79
81 /// Map of registered services, retrieved using GetServicePort or ConnectToService. 80 /// Map of registered services, retrieved using GetServicePort or ConnectToService.
82 std::unordered_map<std::string, std::shared_ptr<Kernel::ClientPort>> registered_services; 81 std::unordered_map<std::string, std::shared_ptr<Kernel::ClientPort>> registered_services;
82
83 /// Kernel context
84 Kernel::KernelCore& kernel;
83}; 85};
84 86
85} // namespace Service::SM 87} // namespace Service::SM
diff --git a/src/core/hle/service/sockets/blocking_worker.h b/src/core/hle/service/sockets/blocking_worker.h
new file mode 100644
index 000000000..2d53e52b6
--- /dev/null
+++ b/src/core/hle/service/sockets/blocking_worker.h
@@ -0,0 +1,161 @@
1// Copyright 2020 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 <memory>
9#include <string>
10#include <string_view>
11#include <thread>
12#include <variant>
13#include <vector>
14
15#include <fmt/format.h>
16
17#include "common/assert.h"
18#include "common/microprofile.h"
19#include "common/thread.h"
20#include "core/core.h"
21#include "core/hle/kernel/hle_ipc.h"
22#include "core/hle/kernel/kernel.h"
23#include "core/hle/kernel/thread.h"
24#include "core/hle/kernel/writable_event.h"
25
26namespace Service::Sockets {
27
28/**
29 * Worker abstraction to execute blocking calls on host without blocking the guest thread
30 *
31 * @tparam Service Service where the work is executed
32 * @tparam Types Types of work to execute
33 */
34template <class Service, class... Types>
35class BlockingWorker {
36 using This = BlockingWorker<Service, Types...>;
37 using WorkVariant = std::variant<std::monostate, Types...>;
38
39public:
40 /// Create a new worker
41 static std::unique_ptr<This> Create(Core::System& system, Service* service,
42 std::string_view name) {
43 return std::unique_ptr<This>(new This(system, service, name));
44 }
45
46 ~BlockingWorker() {
47 while (!is_available.load(std::memory_order_relaxed)) {
48 // Busy wait until work is finished
49 std::this_thread::yield();
50 }
51 // Monostate means to exit the thread
52 work = std::monostate{};
53 work_event.Set();
54 thread.join();
55 }
56
57 /**
58 * Try to capture the worker to send work after a success
59 * @returns True when the worker has been successfully captured
60 */
61 bool TryCapture() {
62 bool expected = true;
63 return is_available.compare_exchange_weak(expected, false, std::memory_order_relaxed,
64 std::memory_order_relaxed);
65 }
66
67 /**
68 * Send work to this worker abstraction
69 * @see TryCapture must be called before attempting to call this function
70 */
71 template <class Work>
72 void SendWork(Work new_work) {
73 ASSERT_MSG(!is_available, "Trying to send work on a worker that's not captured");
74 work = std::move(new_work);
75 work_event.Set();
76 }
77
78 /// Generate a callback for @see SleepClientThread
79 template <class Work>
80 auto Callback() {
81 return [this](std::shared_ptr<Kernel::Thread>, Kernel::HLERequestContext& ctx,
82 Kernel::ThreadWakeupReason reason) {
83 ASSERT(reason == Kernel::ThreadWakeupReason::Signal);
84 std::get<Work>(work).Response(ctx);
85 is_available.store(true);
86 };
87 }
88
89 /// Get kernel event that will be signalled by the worker when the host operation finishes
90 std::shared_ptr<Kernel::WritableEvent> KernelEvent() const {
91 return kernel_event;
92 }
93
94private:
95 explicit BlockingWorker(Core::System& system, Service* service, std::string_view name) {
96 auto pair = Kernel::WritableEvent::CreateEventPair(system.Kernel(), std::string(name));
97 kernel_event = std::move(pair.writable);
98 thread = std::thread([this, &system, service, name] { Run(system, service, name); });
99 }
100
101 void Run(Core::System& system, Service* service, std::string_view name) {
102 system.RegisterHostThread();
103
104 const std::string thread_name = fmt::format("yuzu:{}", name);
105 MicroProfileOnThreadCreate(thread_name.c_str());
106 Common::SetCurrentThreadName(thread_name.c_str());
107
108 bool keep_running = true;
109 while (keep_running) {
110 work_event.Wait();
111
112 const auto visit_fn = [service, &keep_running]<typename T>(T&& w) {
113 if constexpr (std::is_same_v<std::decay_t<T>, std::monostate>) {
114 keep_running = false;
115 } else {
116 w.Execute(service);
117 }
118 };
119 std::visit(visit_fn, work);
120
121 kernel_event->Signal();
122 }
123 }
124
125 std::thread thread;
126 WorkVariant work;
127 Common::Event work_event;
128 std::shared_ptr<Kernel::WritableEvent> kernel_event;
129 std::atomic_bool is_available{true};
130};
131
132template <class Service, class... Types>
133class BlockingWorkerPool {
134 using Worker = BlockingWorker<Service, Types...>;
135
136public:
137 explicit BlockingWorkerPool(Core::System& system_, Service* service_)
138 : system{system_}, service{service_} {}
139
140 /// Returns a captured worker thread, creating new ones if necessary
141 Worker* CaptureWorker() {
142 for (auto& worker : workers) {
143 if (worker->TryCapture()) {
144 return worker.get();
145 }
146 }
147 auto new_worker = Worker::Create(system, service, fmt::format("BSD:{}", workers.size()));
148 [[maybe_unused]] const bool success = new_worker->TryCapture();
149 ASSERT(success);
150
151 return workers.emplace_back(std::move(new_worker)).get();
152 }
153
154private:
155 Core::System& system;
156 Service* const service;
157
158 std::vector<std::unique_ptr<Worker>> workers;
159};
160
161} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 8d4952c0e..a74be9370 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -2,18 +2,138 @@
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 <array>
6#include <memory>
7#include <string>
8#include <utility>
9#include <vector>
10
11#include <fmt/format.h>
12
13#include "common/microprofile.h"
14#include "common/thread.h"
5#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
16#include "core/hle/kernel/thread.h"
6#include "core/hle/service/sockets/bsd.h" 17#include "core/hle/service/sockets/bsd.h"
18#include "core/hle/service/sockets/sockets_translate.h"
19#include "core/network/network.h"
20#include "core/network/sockets.h"
7 21
8namespace Service::Sockets { 22namespace Service::Sockets {
9 23
24namespace {
25
26bool IsConnectionBased(Type type) {
27 switch (type) {
28 case Type::STREAM:
29 return true;
30 case Type::DGRAM:
31 return false;
32 default:
33 UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type));
34 return false;
35 }
36}
37
38} // Anonymous namespace
39
40void BSD::PollWork::Execute(BSD* bsd) {
41 std::tie(ret, bsd_errno) = bsd->PollImpl(write_buffer, read_buffer, nfds, timeout);
42}
43
44void BSD::PollWork::Response(Kernel::HLERequestContext& ctx) {
45 ctx.WriteBuffer(write_buffer);
46
47 IPC::ResponseBuilder rb{ctx, 4};
48 rb.Push(RESULT_SUCCESS);
49 rb.Push<s32>(ret);
50 rb.PushEnum(bsd_errno);
51}
52
53void BSD::AcceptWork::Execute(BSD* bsd) {
54 std::tie(ret, bsd_errno) = bsd->AcceptImpl(fd, write_buffer);
55}
56
57void BSD::AcceptWork::Response(Kernel::HLERequestContext& ctx) {
58 ctx.WriteBuffer(write_buffer);
59
60 IPC::ResponseBuilder rb{ctx, 5};
61 rb.Push(RESULT_SUCCESS);
62 rb.Push<s32>(ret);
63 rb.PushEnum(bsd_errno);
64 rb.Push<u32>(static_cast<u32>(write_buffer.size()));
65}
66
67void BSD::ConnectWork::Execute(BSD* bsd) {
68 bsd_errno = bsd->ConnectImpl(fd, addr);
69}
70
71void BSD::ConnectWork::Response(Kernel::HLERequestContext& ctx) {
72 IPC::ResponseBuilder rb{ctx, 4};
73 rb.Push(RESULT_SUCCESS);
74 rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
75 rb.PushEnum(bsd_errno);
76}
77
78void BSD::RecvWork::Execute(BSD* bsd) {
79 std::tie(ret, bsd_errno) = bsd->RecvImpl(fd, flags, message);
80}
81
82void BSD::RecvWork::Response(Kernel::HLERequestContext& ctx) {
83 ctx.WriteBuffer(message);
84
85 IPC::ResponseBuilder rb{ctx, 4};
86 rb.Push(RESULT_SUCCESS);
87 rb.Push<s32>(ret);
88 rb.PushEnum(bsd_errno);
89}
90
91void BSD::RecvFromWork::Execute(BSD* bsd) {
92 std::tie(ret, bsd_errno) = bsd->RecvFromImpl(fd, flags, message, addr);
93}
94
95void BSD::RecvFromWork::Response(Kernel::HLERequestContext& ctx) {
96 ctx.WriteBuffer(message, 0);
97 if (!addr.empty()) {
98 ctx.WriteBuffer(addr, 1);
99 }
100
101 IPC::ResponseBuilder rb{ctx, 5};
102 rb.Push(RESULT_SUCCESS);
103 rb.Push<s32>(ret);
104 rb.PushEnum(bsd_errno);
105 rb.Push<u32>(static_cast<u32>(addr.size()));
106}
107
108void BSD::SendWork::Execute(BSD* bsd) {
109 std::tie(ret, bsd_errno) = bsd->SendImpl(fd, flags, message);
110}
111
112void BSD::SendWork::Response(Kernel::HLERequestContext& ctx) {
113 IPC::ResponseBuilder rb{ctx, 4};
114 rb.Push(RESULT_SUCCESS);
115 rb.Push<s32>(ret);
116 rb.PushEnum(bsd_errno);
117}
118
119void BSD::SendToWork::Execute(BSD* bsd) {
120 std::tie(ret, bsd_errno) = bsd->SendToImpl(fd, flags, message, addr);
121}
122
123void BSD::SendToWork::Response(Kernel::HLERequestContext& ctx) {
124 IPC::ResponseBuilder rb{ctx, 4};
125 rb.Push(RESULT_SUCCESS);
126 rb.Push<s32>(ret);
127 rb.PushEnum(bsd_errno);
128}
129
10void BSD::RegisterClient(Kernel::HLERequestContext& ctx) { 130void BSD::RegisterClient(Kernel::HLERequestContext& ctx) {
11 LOG_WARNING(Service, "(STUBBED) called"); 131 LOG_WARNING(Service, "(STUBBED) called");
12 132
13 IPC::ResponseBuilder rb{ctx, 3}; 133 IPC::ResponseBuilder rb{ctx, 3};
14 134
15 rb.Push(RESULT_SUCCESS); 135 rb.Push(RESULT_SUCCESS);
16 rb.Push<u32>(0); // bsd errno 136 rb.Push<s32>(0); // bsd errno
17} 137}
18 138
19void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) { 139void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
@@ -26,20 +146,19 @@ void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
26 146
27void BSD::Socket(Kernel::HLERequestContext& ctx) { 147void BSD::Socket(Kernel::HLERequestContext& ctx) {
28 IPC::RequestParser rp{ctx}; 148 IPC::RequestParser rp{ctx};
149 const u32 domain = rp.Pop<u32>();
150 const u32 type = rp.Pop<u32>();
151 const u32 protocol = rp.Pop<u32>();
29 152
30 u32 domain = rp.Pop<u32>(); 153 LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
31 u32 type = rp.Pop<u32>();
32 u32 protocol = rp.Pop<u32>();
33
34 LOG_WARNING(Service, "(STUBBED) called domain={} type={} protocol={}", domain, type, protocol);
35 154
36 u32 fd = next_fd++; 155 const auto [fd, bsd_errno] = SocketImpl(static_cast<Domain>(domain), static_cast<Type>(type),
156 static_cast<Protocol>(protocol));
37 157
38 IPC::ResponseBuilder rb{ctx, 4}; 158 IPC::ResponseBuilder rb{ctx, 4};
39
40 rb.Push(RESULT_SUCCESS); 159 rb.Push(RESULT_SUCCESS);
41 rb.Push<u32>(fd); 160 rb.Push<s32>(fd);
42 rb.Push<u32>(0); // bsd errno 161 rb.PushEnum(bsd_errno);
43} 162}
44 163
45void BSD::Select(Kernel::HLERequestContext& ctx) { 164void BSD::Select(Kernel::HLERequestContext& ctx) {
@@ -52,67 +171,664 @@ void BSD::Select(Kernel::HLERequestContext& ctx) {
52 rb.Push<u32>(0); // bsd errno 171 rb.Push<u32>(0); // bsd errno
53} 172}
54 173
174void BSD::Poll(Kernel::HLERequestContext& ctx) {
175 IPC::RequestParser rp{ctx};
176 const s32 nfds = rp.Pop<s32>();
177 const s32 timeout = rp.Pop<s32>();
178
179 LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout);
180
181 ExecuteWork(ctx, "BSD:Poll", timeout != 0,
182 PollWork{
183 .nfds = nfds,
184 .timeout = timeout,
185 .read_buffer = ctx.ReadBuffer(),
186 .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
187 });
188}
189
190void BSD::Accept(Kernel::HLERequestContext& ctx) {
191 IPC::RequestParser rp{ctx};
192 const s32 fd = rp.Pop<s32>();
193
194 LOG_DEBUG(Service, "called. fd={}", fd);
195
196 ExecuteWork(ctx, "BSD:Accept", IsBlockingSocket(fd),
197 AcceptWork{
198 .fd = fd,
199 .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
200 });
201}
202
55void BSD::Bind(Kernel::HLERequestContext& ctx) { 203void BSD::Bind(Kernel::HLERequestContext& ctx) {
56 LOG_WARNING(Service, "(STUBBED) called"); 204 IPC::RequestParser rp{ctx};
205 const s32 fd = rp.Pop<s32>();
57 206
58 IPC::ResponseBuilder rb{ctx, 4}; 207 LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
59 208
60 rb.Push(RESULT_SUCCESS); 209 BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer()));
61 rb.Push<u32>(0); // ret
62 rb.Push<u32>(0); // bsd errno
63} 210}
64 211
65void BSD::Connect(Kernel::HLERequestContext& ctx) { 212void BSD::Connect(Kernel::HLERequestContext& ctx) {
66 LOG_WARNING(Service, "(STUBBED) called"); 213 IPC::RequestParser rp{ctx};
214 const s32 fd = rp.Pop<s32>();
67 215
68 IPC::ResponseBuilder rb{ctx, 4}; 216 LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
217
218 ExecuteWork(ctx, "BSD:Connect", IsBlockingSocket(fd),
219 ConnectWork{
220 .fd = fd,
221 .addr = ctx.ReadBuffer(),
222 });
223}
224
225void BSD::GetPeerName(Kernel::HLERequestContext& ctx) {
226 IPC::RequestParser rp{ctx};
227 const s32 fd = rp.Pop<s32>();
228
229 LOG_DEBUG(Service, "called. fd={}", fd);
69 230
231 std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
232 const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
233
234 ctx.WriteBuffer(write_buffer);
235
236 IPC::ResponseBuilder rb{ctx, 5};
70 rb.Push(RESULT_SUCCESS); 237 rb.Push(RESULT_SUCCESS);
71 rb.Push<u32>(0); // ret 238 rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
72 rb.Push<u32>(0); // bsd errno 239 rb.PushEnum(bsd_errno);
240 rb.Push<u32>(static_cast<u32>(write_buffer.size()));
241}
242
243void BSD::GetSockName(Kernel::HLERequestContext& ctx) {
244 IPC::RequestParser rp{ctx};
245 const s32 fd = rp.Pop<s32>();
246
247 LOG_DEBUG(Service, "called. fd={}", fd);
248
249 std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
250 const Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
251
252 ctx.WriteBuffer(write_buffer);
253
254 IPC::ResponseBuilder rb{ctx, 5};
255 rb.Push(RESULT_SUCCESS);
256 rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
257 rb.PushEnum(bsd_errno);
258 rb.Push<u32>(static_cast<u32>(write_buffer.size()));
73} 259}
74 260
75void BSD::Listen(Kernel::HLERequestContext& ctx) { 261void BSD::Listen(Kernel::HLERequestContext& ctx) {
76 LOG_WARNING(Service, "(STUBBED) called"); 262 IPC::RequestParser rp{ctx};
263 const s32 fd = rp.Pop<s32>();
264 const s32 backlog = rp.Pop<s32>();
77 265
78 IPC::ResponseBuilder rb{ctx, 4}; 266 LOG_DEBUG(Service, "called. fd={} backlog={}", fd, backlog);
267
268 BuildErrnoResponse(ctx, ListenImpl(fd, backlog));
269}
270
271void BSD::Fcntl(Kernel::HLERequestContext& ctx) {
272 IPC::RequestParser rp{ctx};
273 const s32 fd = rp.Pop<s32>();
274 const s32 cmd = rp.Pop<s32>();
275 const s32 arg = rp.Pop<s32>();
79 276
277 LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
278
279 const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg);
280
281 IPC::ResponseBuilder rb{ctx, 4};
80 rb.Push(RESULT_SUCCESS); 282 rb.Push(RESULT_SUCCESS);
81 rb.Push<u32>(0); // ret 283 rb.Push<s32>(ret);
82 rb.Push<u32>(0); // bsd errno 284 rb.PushEnum(bsd_errno);
83} 285}
84 286
85void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) { 287void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) {
86 LOG_WARNING(Service, "(STUBBED) called"); 288 IPC::RequestParser rp{ctx};
87 289
88 IPC::ResponseBuilder rb{ctx, 4}; 290 const s32 fd = rp.Pop<s32>();
291 const u32 level = rp.Pop<u32>();
292 const OptName optname = static_cast<OptName>(rp.Pop<u32>());
89 293
90 rb.Push(RESULT_SUCCESS); 294 const std::vector<u8> buffer = ctx.ReadBuffer();
91 rb.Push<u32>(0); // ret 295 const u8* optval = buffer.empty() ? nullptr : buffer.data();
92 rb.Push<u32>(0); // bsd errno 296 size_t optlen = buffer.size();
297
298 std::array<u64, 2> values;
299 if ((optname == OptName::SNDTIMEO || optname == OptName::RCVTIMEO) && buffer.size() == 8) {
300 std::memcpy(values.data(), buffer.data(), sizeof(values));
301 optlen = sizeof(values);
302 optval = reinterpret_cast<const u8*>(values.data());
303 }
304
305 LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level,
306 static_cast<u32>(optname), optlen);
307
308 BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optlen, optval));
309}
310
311void BSD::Shutdown(Kernel::HLERequestContext& ctx) {
312 IPC::RequestParser rp{ctx};
313
314 const s32 fd = rp.Pop<s32>();
315 const s32 how = rp.Pop<s32>();
316
317 LOG_DEBUG(Service, "called. fd={} how={}", fd, how);
318
319 BuildErrnoResponse(ctx, ShutdownImpl(fd, how));
320}
321
322void BSD::Recv(Kernel::HLERequestContext& ctx) {
323 IPC::RequestParser rp{ctx};
324
325 const s32 fd = rp.Pop<s32>();
326 const u32 flags = rp.Pop<u32>();
327
328 LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize());
329
330 ExecuteWork(ctx, "BSD:Recv", IsBlockingSocket(fd),
331 RecvWork{
332 .fd = fd,
333 .flags = flags,
334 .message = std::vector<u8>(ctx.GetWriteBufferSize()),
335 });
336}
337
338void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {
339 IPC::RequestParser rp{ctx};
340
341 const s32 fd = rp.Pop<s32>();
342 const u32 flags = rp.Pop<u32>();
343
344 LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags,
345 ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1));
346
347 ExecuteWork(ctx, "BSD:RecvFrom", IsBlockingSocket(fd),
348 RecvFromWork{
349 .fd = fd,
350 .flags = flags,
351 .message = std::vector<u8>(ctx.GetWriteBufferSize(0)),
352 .addr = std::vector<u8>(ctx.GetWriteBufferSize(1)),
353 });
354}
355
356void BSD::Send(Kernel::HLERequestContext& ctx) {
357 IPC::RequestParser rp{ctx};
358
359 const s32 fd = rp.Pop<s32>();
360 const u32 flags = rp.Pop<u32>();
361
362 LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize());
363
364 ExecuteWork(ctx, "BSD:Send", IsBlockingSocket(fd),
365 SendWork{
366 .fd = fd,
367 .flags = flags,
368 .message = ctx.ReadBuffer(),
369 });
93} 370}
94 371
95void BSD::SendTo(Kernel::HLERequestContext& ctx) { 372void BSD::SendTo(Kernel::HLERequestContext& ctx) {
96 LOG_WARNING(Service, "(STUBBED) called"); 373 IPC::RequestParser rp{ctx};
374 const s32 fd = rp.Pop<s32>();
375 const u32 flags = rp.Pop<u32>();
376
377 LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags,
378 ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1));
379
380 ExecuteWork(ctx, "BSD:SendTo", IsBlockingSocket(fd),
381 SendToWork{
382 .fd = fd,
383 .flags = flags,
384 .message = ctx.ReadBuffer(0),
385 .addr = ctx.ReadBuffer(1),
386 });
387}
97 388
98 IPC::ResponseBuilder rb{ctx, 4}; 389void BSD::Write(Kernel::HLERequestContext& ctx) {
390 IPC::RequestParser rp{ctx};
391 const s32 fd = rp.Pop<s32>();
99 392
100 rb.Push(RESULT_SUCCESS); 393 LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize());
101 rb.Push<u32>(0); // ret 394
102 rb.Push<u32>(0); // bsd errno 395 ExecuteWork(ctx, "BSD:Write", IsBlockingSocket(fd),
396 SendWork{
397 .fd = fd,
398 .flags = 0,
399 .message = ctx.ReadBuffer(),
400 });
103} 401}
104 402
105void BSD::Close(Kernel::HLERequestContext& ctx) { 403void BSD::Close(Kernel::HLERequestContext& ctx) {
106 LOG_WARNING(Service, "(STUBBED) called"); 404 IPC::RequestParser rp{ctx};
405 const s32 fd = rp.Pop<s32>();
406
407 LOG_DEBUG(Service, "called. fd={}", fd);
408
409 BuildErrnoResponse(ctx, CloseImpl(fd));
410}
411
412template <typename Work>
413void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason,
414 bool is_blocking, Work work) {
415 if (!is_blocking) {
416 work.Execute(this);
417 work.Response(ctx);
418 return;
419 }
420
421 // Signal a dummy response to make IPC validation happy
422 // This will be overwritten by the SleepClientThread callback
423 work.Response(ctx);
424
425 auto worker = worker_pool.CaptureWorker();
426
427 ctx.SleepClientThread(std::string(sleep_reason), std::numeric_limits<u64>::max(),
428 worker->Callback<Work>(), worker->KernelEvent());
429
430 worker->SendWork(std::move(work));
431}
432
433std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
434 if (type == Type::SEQPACKET) {
435 UNIMPLEMENTED_MSG("SOCK_SEQPACKET errno management");
436 } else if (type == Type::RAW && (domain != Domain::INET || protocol != Protocol::ICMP)) {
437 UNIMPLEMENTED_MSG("SOCK_RAW errno management");
438 }
439
440 [[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0;
441 UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
442 type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);
443
444 const s32 fd = FindFreeFileDescriptorHandle();
445 if (fd < 0) {
446 LOG_ERROR(Service, "No more file descriptors available");
447 return {-1, Errno::MFILE};
448 }
449
450 FileDescriptor& descriptor = file_descriptors[fd].emplace();
451 // ENONMEM might be thrown here
452
453 LOG_INFO(Service, "New socket fd={}", fd);
454
455 descriptor.socket = std::make_unique<Network::Socket>();
456 descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol));
457 descriptor.is_connection_based = IsConnectionBased(type);
458
459 return {fd, Errno::SUCCESS};
460}
107 461
462std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
463 s32 nfds, s32 timeout) {
464 if (write_buffer.size() < nfds * sizeof(PollFD)) {
465 return {-1, Errno::INVAL};
466 }
467
468 if (nfds == 0) {
469 // When no entries are provided, -1 is returned with errno zero
470 return {-1, Errno::SUCCESS};
471 }
472
473 const size_t length = std::min(read_buffer.size(), write_buffer.size());
474 std::vector<PollFD> fds(nfds);
475 std::memcpy(fds.data(), read_buffer.data(), length);
476
477 if (timeout >= 0) {
478 const s64 seconds = timeout / 1000;
479 const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000);
480
481 if (seconds < 0) {
482 return {-1, Errno::INVAL};
483 }
484 if (nanoseconds > 999'999'999) {
485 return {-1, Errno::INVAL};
486 }
487 } else if (timeout != -1) {
488 return {-1, Errno::INVAL};
489 }
490
491 for (PollFD& pollfd : fds) {
492 ASSERT(pollfd.revents == 0);
493
494 if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) {
495 LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
496 pollfd.revents = 0;
497 return {0, Errno::SUCCESS};
498 }
499
500 const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
501 if (!descriptor) {
502 LOG_ERROR(Service, "File descriptor handle={} is not allocated", pollfd.fd);
503 pollfd.revents = POLL_NVAL;
504 return {0, Errno::SUCCESS};
505 }
506 }
507
508 std::vector<Network::PollFD> host_pollfds(fds.size());
509 std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [this](PollFD pollfd) {
510 Network::PollFD result;
511 result.socket = file_descriptors[pollfd.fd]->socket.get();
512 result.events = TranslatePollEventsToHost(pollfd.events);
513 result.revents = 0;
514 return result;
515 });
516
517 const auto result = Network::Poll(host_pollfds, timeout);
518
519 const size_t num = host_pollfds.size();
520 for (size_t i = 0; i < num; ++i) {
521 fds[i].revents = TranslatePollEventsToGuest(host_pollfds[i].revents);
522 }
523 std::memcpy(write_buffer.data(), fds.data(), length);
524
525 return Translate(result);
526}
527
528std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
529 if (!IsFileDescriptorValid(fd)) {
530 return {-1, Errno::BADF};
531 }
532
533 const s32 new_fd = FindFreeFileDescriptorHandle();
534 if (new_fd < 0) {
535 LOG_ERROR(Service, "No more file descriptors available");
536 return {-1, Errno::MFILE};
537 }
538
539 FileDescriptor& descriptor = *file_descriptors[fd];
540 auto [result, bsd_errno] = descriptor.socket->Accept();
541 if (bsd_errno != Network::Errno::SUCCESS) {
542 return {-1, Translate(bsd_errno)};
543 }
544
545 FileDescriptor& new_descriptor = file_descriptors[new_fd].emplace();
546 new_descriptor.socket = std::move(result.socket);
547 new_descriptor.is_connection_based = descriptor.is_connection_based;
548
549 ASSERT(write_buffer.size() == sizeof(SockAddrIn));
550 const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
551 std::memcpy(write_buffer.data(), &guest_addr_in, sizeof(guest_addr_in));
552
553 return {new_fd, Errno::SUCCESS};
554}
555
556Errno BSD::BindImpl(s32 fd, const std::vector<u8>& addr) {
557 if (!IsFileDescriptorValid(fd)) {
558 return Errno::BADF;
559 }
560 ASSERT(addr.size() == sizeof(SockAddrIn));
561 SockAddrIn addr_in;
562 std::memcpy(&addr_in, addr.data(), sizeof(addr_in));
563
564 return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
565}
566
567Errno BSD::ConnectImpl(s32 fd, const std::vector<u8>& addr) {
568 if (!IsFileDescriptorValid(fd)) {
569 return Errno::BADF;
570 }
571
572 UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn));
573 SockAddrIn addr_in;
574 std::memcpy(&addr_in, addr.data(), sizeof(addr_in));
575
576 return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
577}
578
579Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
580 if (!IsFileDescriptorValid(fd)) {
581 return Errno::BADF;
582 }
583
584 const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
585 if (bsd_errno != Network::Errno::SUCCESS) {
586 return Translate(bsd_errno);
587 }
588 const SockAddrIn guest_addrin = Translate(addr_in);
589
590 ASSERT(write_buffer.size() == sizeof(guest_addrin));
591 std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin));
592 return Translate(bsd_errno);
593}
594
595Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
596 if (!IsFileDescriptorValid(fd)) {
597 return Errno::BADF;
598 }
599
600 const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
601 if (bsd_errno != Network::Errno::SUCCESS) {
602 return Translate(bsd_errno);
603 }
604 const SockAddrIn guest_addrin = Translate(addr_in);
605
606 ASSERT(write_buffer.size() == sizeof(guest_addrin));
607 std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin));
608 return Translate(bsd_errno);
609}
610
611Errno BSD::ListenImpl(s32 fd, s32 backlog) {
612 if (!IsFileDescriptorValid(fd)) {
613 return Errno::BADF;
614 }
615 return Translate(file_descriptors[fd]->socket->Listen(backlog));
616}
617
618std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
619 if (!IsFileDescriptorValid(fd)) {
620 return {-1, Errno::BADF};
621 }
622
623 FileDescriptor& descriptor = *file_descriptors[fd];
624
625 switch (cmd) {
626 case FcntlCmd::GETFL:
627 ASSERT(arg == 0);
628 return {descriptor.flags, Errno::SUCCESS};
629 case FcntlCmd::SETFL: {
630 const bool enable = (arg & FLAG_O_NONBLOCK) != 0;
631 const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
632 if (bsd_errno != Errno::SUCCESS) {
633 return {-1, bsd_errno};
634 }
635 descriptor.flags = arg;
636 return {0, Errno::SUCCESS};
637 }
638 default:
639 UNIMPLEMENTED_MSG("Unimplemented cmd={}", static_cast<int>(cmd));
640 return {-1, Errno::SUCCESS};
641 }
642}
643
644Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) {
645 UNIMPLEMENTED_IF(level != 0xffff); // SOL_SOCKET
646
647 if (!IsFileDescriptorValid(fd)) {
648 return Errno::BADF;
649 }
650
651 Network::Socket* const socket = file_descriptors[fd]->socket.get();
652
653 if (optname == OptName::LINGER) {
654 ASSERT(optlen == sizeof(Linger));
655 Linger linger;
656 std::memcpy(&linger, optval, sizeof(linger));
657 ASSERT(linger.onoff == 0 || linger.onoff == 1);
658
659 return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
660 }
661
662 ASSERT(optlen == sizeof(u32));
663 u32 value;
664 std::memcpy(&value, optval, sizeof(value));
665
666 switch (optname) {
667 case OptName::REUSEADDR:
668 ASSERT(value == 0 || value == 1);
669 return Translate(socket->SetReuseAddr(value != 0));
670 case OptName::BROADCAST:
671 ASSERT(value == 0 || value == 1);
672 return Translate(socket->SetBroadcast(value != 0));
673 case OptName::SNDBUF:
674 return Translate(socket->SetSndBuf(value));
675 case OptName::RCVBUF:
676 return Translate(socket->SetRcvBuf(value));
677 case OptName::SNDTIMEO:
678 return Translate(socket->SetSndTimeo(value));
679 case OptName::RCVTIMEO:
680 return Translate(socket->SetRcvTimeo(value));
681 default:
682 UNIMPLEMENTED_MSG("Unimplemented optname={}", static_cast<int>(optname));
683 return Errno::SUCCESS;
684 }
685}
686
687Errno BSD::ShutdownImpl(s32 fd, s32 how) {
688 if (!IsFileDescriptorValid(fd)) {
689 return Errno::BADF;
690 }
691 const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how));
692 return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
693}
694
695std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
696 if (!IsFileDescriptorValid(fd)) {
697 return {-1, Errno::BADF};
698 }
699 return Translate(file_descriptors[fd]->socket->Recv(flags, message));
700}
701
702std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
703 std::vector<u8>& addr) {
704 if (!IsFileDescriptorValid(fd)) {
705 return {-1, Errno::BADF};
706 }
707
708 FileDescriptor& descriptor = *file_descriptors[fd];
709
710 Network::SockAddrIn addr_in{};
711 Network::SockAddrIn* p_addr_in = nullptr;
712 if (descriptor.is_connection_based) {
713 // Connection based file descriptors (e.g. TCP) zero addr
714 addr.clear();
715 } else {
716 p_addr_in = &addr_in;
717 }
718
719 // Apply flags
720 if ((flags & FLAG_MSG_DONTWAIT) != 0) {
721 flags &= ~FLAG_MSG_DONTWAIT;
722 if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
723 descriptor.socket->SetNonBlock(true);
724 }
725 }
726
727 const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in));
728
729 // Restore original state
730 if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
731 descriptor.socket->SetNonBlock(false);
732 }
733
734 if (p_addr_in) {
735 if (ret < 0) {
736 addr.clear();
737 } else {
738 ASSERT(addr.size() == sizeof(SockAddrIn));
739 const SockAddrIn result = Translate(addr_in);
740 std::memcpy(addr.data(), &result, sizeof(result));
741 }
742 }
743
744 return {ret, bsd_errno};
745}
746
747std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, const std::vector<u8>& message) {
748 if (!IsFileDescriptorValid(fd)) {
749 return {-1, Errno::BADF};
750 }
751 return Translate(file_descriptors[fd]->socket->Send(message, flags));
752}
753
754std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message,
755 const std::vector<u8>& addr) {
756 if (!IsFileDescriptorValid(fd)) {
757 return {-1, Errno::BADF};
758 }
759
760 Network::SockAddrIn addr_in;
761 Network::SockAddrIn* p_addr_in = nullptr;
762 if (!addr.empty()) {
763 ASSERT(addr.size() == sizeof(SockAddrIn));
764 SockAddrIn guest_addr_in;
765 std::memcpy(&guest_addr_in, addr.data(), sizeof(guest_addr_in));
766 addr_in = Translate(guest_addr_in);
767 p_addr_in = &addr_in;
768 }
769
770 return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
771}
772
773Errno BSD::CloseImpl(s32 fd) {
774 if (!IsFileDescriptorValid(fd)) {
775 return Errno::BADF;
776 }
777
778 const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
779 if (bsd_errno != Errno::SUCCESS) {
780 return bsd_errno;
781 }
782
783 LOG_INFO(Service, "Close socket fd={}", fd);
784
785 file_descriptors[fd].reset();
786 return bsd_errno;
787}
788
789s32 BSD::FindFreeFileDescriptorHandle() noexcept {
790 for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
791 if (!file_descriptors[fd]) {
792 return fd;
793 }
794 }
795 return -1;
796}
797
798bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
799 if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
800 LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
801 return false;
802 }
803 if (!file_descriptors[fd]) {
804 LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd);
805 return false;
806 }
807 return true;
808}
809
810bool BSD::IsBlockingSocket(s32 fd) const noexcept {
811 // Inform invalid sockets as non-blocking
812 // This way we avoid using a worker thread as it will fail without blocking host
813 if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
814 return false;
815 }
816 if (!file_descriptors[fd]) {
817 return false;
818 }
819 return (file_descriptors[fd]->flags & FLAG_O_NONBLOCK) != 0;
820}
821
822void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept {
108 IPC::ResponseBuilder rb{ctx, 4}; 823 IPC::ResponseBuilder rb{ctx, 4};
109 824
110 rb.Push(RESULT_SUCCESS); 825 rb.Push(RESULT_SUCCESS);
111 rb.Push<u32>(0); // ret 826 rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
112 rb.Push<u32>(0); // bsd errno 827 rb.PushEnum(bsd_errno);
113} 828}
114 829
115BSD::BSD(const char* name) : ServiceFramework(name) { 830BSD::BSD(Core::System& system, const char* name)
831 : ServiceFramework(name), worker_pool{system, this} {
116 // clang-format off 832 // clang-format off
117 static const FunctionInfo functions[] = { 833 static const FunctionInfo functions[] = {
118 {0, &BSD::RegisterClient, "RegisterClient"}, 834 {0, &BSD::RegisterClient, "RegisterClient"},
@@ -121,25 +837,25 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
121 {3, nullptr, "SocketExempt"}, 837 {3, nullptr, "SocketExempt"},
122 {4, nullptr, "Open"}, 838 {4, nullptr, "Open"},
123 {5, &BSD::Select, "Select"}, 839 {5, &BSD::Select, "Select"},
124 {6, nullptr, "Poll"}, 840 {6, &BSD::Poll, "Poll"},
125 {7, nullptr, "Sysctl"}, 841 {7, nullptr, "Sysctl"},
126 {8, nullptr, "Recv"}, 842 {8, &BSD::Recv, "Recv"},
127 {9, nullptr, "RecvFrom"}, 843 {9, &BSD::RecvFrom, "RecvFrom"},
128 {10, nullptr, "Send"}, 844 {10, &BSD::Send, "Send"},
129 {11, &BSD::SendTo, "SendTo"}, 845 {11, &BSD::SendTo, "SendTo"},
130 {12, nullptr, "Accept"}, 846 {12, &BSD::Accept, "Accept"},
131 {13, &BSD::Bind, "Bind"}, 847 {13, &BSD::Bind, "Bind"},
132 {14, &BSD::Connect, "Connect"}, 848 {14, &BSD::Connect, "Connect"},
133 {15, nullptr, "GetPeerName"}, 849 {15, &BSD::GetPeerName, "GetPeerName"},
134 {16, nullptr, "GetSockName"}, 850 {16, &BSD::GetSockName, "GetSockName"},
135 {17, nullptr, "GetSockOpt"}, 851 {17, nullptr, "GetSockOpt"},
136 {18, &BSD::Listen, "Listen"}, 852 {18, &BSD::Listen, "Listen"},
137 {19, nullptr, "Ioctl"}, 853 {19, nullptr, "Ioctl"},
138 {20, nullptr, "Fcntl"}, 854 {20, &BSD::Fcntl, "Fcntl"},
139 {21, &BSD::SetSockOpt, "SetSockOpt"}, 855 {21, &BSD::SetSockOpt, "SetSockOpt"},
140 {22, nullptr, "Shutdown"}, 856 {22, &BSD::Shutdown, "Shutdown"},
141 {23, nullptr, "ShutdownAllSockets"}, 857 {23, nullptr, "ShutdownAllSockets"},
142 {24, nullptr, "Write"}, 858 {24, &BSD::Write, "Write"},
143 {25, nullptr, "Read"}, 859 {25, nullptr, "Read"},
144 {26, &BSD::Close, "Close"}, 860 {26, &BSD::Close, "Close"},
145 {27, nullptr, "DuplicateSocket"}, 861 {27, nullptr, "DuplicateSocket"},
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 3098e3baf..357531951 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -4,30 +4,174 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
8#include <string_view>
9#include <vector>
10
11#include "common/common_types.h"
7#include "core/hle/kernel/hle_ipc.h" 12#include "core/hle/kernel/hle_ipc.h"
8#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
14#include "core/hle/service/sockets/blocking_worker.h"
15#include "core/hle/service/sockets/sockets.h"
16
17namespace Core {
18class System;
19}
20
21namespace Network {
22class Socket;
23}
9 24
10namespace Service::Sockets { 25namespace Service::Sockets {
11 26
12class BSD final : public ServiceFramework<BSD> { 27class BSD final : public ServiceFramework<BSD> {
13public: 28public:
14 explicit BSD(const char* name); 29 explicit BSD(Core::System& system, const char* name);
15 ~BSD() override; 30 ~BSD() override;
16 31
17private: 32private:
33 /// Maximum number of file descriptors
34 static constexpr size_t MAX_FD = 128;
35
36 struct FileDescriptor {
37 std::unique_ptr<Network::Socket> socket;
38 s32 flags = 0;
39 bool is_connection_based = false;
40 };
41
42 struct PollWork {
43 void Execute(BSD* bsd);
44 void Response(Kernel::HLERequestContext& ctx);
45
46 s32 nfds;
47 s32 timeout;
48 std::vector<u8> read_buffer;
49 std::vector<u8> write_buffer;
50 s32 ret{};
51 Errno bsd_errno{};
52 };
53
54 struct AcceptWork {
55 void Execute(BSD* bsd);
56 void Response(Kernel::HLERequestContext& ctx);
57
58 s32 fd;
59 std::vector<u8> write_buffer;
60 s32 ret{};
61 Errno bsd_errno{};
62 };
63
64 struct ConnectWork {
65 void Execute(BSD* bsd);
66 void Response(Kernel::HLERequestContext& ctx);
67
68 s32 fd;
69 std::vector<u8> addr;
70 Errno bsd_errno{};
71 };
72
73 struct RecvWork {
74 void Execute(BSD* bsd);
75 void Response(Kernel::HLERequestContext& ctx);
76
77 s32 fd;
78 u32 flags;
79 std::vector<u8> message;
80 s32 ret{};
81 Errno bsd_errno{};
82 };
83
84 struct RecvFromWork {
85 void Execute(BSD* bsd);
86 void Response(Kernel::HLERequestContext& ctx);
87
88 s32 fd;
89 u32 flags;
90 std::vector<u8> message;
91 std::vector<u8> addr;
92 s32 ret{};
93 Errno bsd_errno{};
94 };
95
96 struct SendWork {
97 void Execute(BSD* bsd);
98 void Response(Kernel::HLERequestContext& ctx);
99
100 s32 fd;
101 u32 flags;
102 std::vector<u8> message;
103 s32 ret{};
104 Errno bsd_errno{};
105 };
106
107 struct SendToWork {
108 void Execute(BSD* bsd);
109 void Response(Kernel::HLERequestContext& ctx);
110
111 s32 fd;
112 u32 flags;
113 std::vector<u8> message;
114 std::vector<u8> addr;
115 s32 ret{};
116 Errno bsd_errno{};
117 };
118
18 void RegisterClient(Kernel::HLERequestContext& ctx); 119 void RegisterClient(Kernel::HLERequestContext& ctx);
19 void StartMonitoring(Kernel::HLERequestContext& ctx); 120 void StartMonitoring(Kernel::HLERequestContext& ctx);
20 void Socket(Kernel::HLERequestContext& ctx); 121 void Socket(Kernel::HLERequestContext& ctx);
21 void Select(Kernel::HLERequestContext& ctx); 122 void Select(Kernel::HLERequestContext& ctx);
123 void Poll(Kernel::HLERequestContext& ctx);
124 void Accept(Kernel::HLERequestContext& ctx);
22 void Bind(Kernel::HLERequestContext& ctx); 125 void Bind(Kernel::HLERequestContext& ctx);
23 void Connect(Kernel::HLERequestContext& ctx); 126 void Connect(Kernel::HLERequestContext& ctx);
127 void GetPeerName(Kernel::HLERequestContext& ctx);
128 void GetSockName(Kernel::HLERequestContext& ctx);
24 void Listen(Kernel::HLERequestContext& ctx); 129 void Listen(Kernel::HLERequestContext& ctx);
130 void Fcntl(Kernel::HLERequestContext& ctx);
25 void SetSockOpt(Kernel::HLERequestContext& ctx); 131 void SetSockOpt(Kernel::HLERequestContext& ctx);
132 void Shutdown(Kernel::HLERequestContext& ctx);
133 void Recv(Kernel::HLERequestContext& ctx);
134 void RecvFrom(Kernel::HLERequestContext& ctx);
135 void Send(Kernel::HLERequestContext& ctx);
26 void SendTo(Kernel::HLERequestContext& ctx); 136 void SendTo(Kernel::HLERequestContext& ctx);
137 void Write(Kernel::HLERequestContext& ctx);
27 void Close(Kernel::HLERequestContext& ctx); 138 void Close(Kernel::HLERequestContext& ctx);
28 139
29 /// Id to use for the next open file descriptor. 140 template <typename Work>
30 u32 next_fd = 1; 141 void ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason,
142 bool is_blocking, Work work);
143
144 std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
145 std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
146 s32 nfds, s32 timeout);
147 std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
148 Errno BindImpl(s32 fd, const std::vector<u8>& addr);
149 Errno ConnectImpl(s32 fd, const std::vector<u8>& addr);
150 Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
151 Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
152 Errno ListenImpl(s32 fd, s32 backlog);
153 std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
154 Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval);
155 Errno ShutdownImpl(s32 fd, s32 how);
156 std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
157 std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
158 std::vector<u8>& addr);
159 std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, const std::vector<u8>& message);
160 std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message,
161 const std::vector<u8>& addr);
162 Errno CloseImpl(s32 fd);
163
164 s32 FindFreeFileDescriptorHandle() noexcept;
165 bool IsFileDescriptorValid(s32 fd) const noexcept;
166 bool IsBlockingSocket(s32 fd) const noexcept;
167
168 void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
169
170 std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
171
172 BlockingWorkerPool<BSD, PollWork, AcceptWork, ConnectWork, RecvWork, RecvFromWork, SendWork,
173 SendToWork>
174 worker_pool;
31}; 175};
32 176
33class BSDCFG final : public ServiceFramework<BSDCFG> { 177class BSDCFG final : public ServiceFramework<BSDCFG> {
diff --git a/src/core/hle/service/sockets/sockets.cpp b/src/core/hle/service/sockets/sockets.cpp
index 08d2d306a..1d27f7906 100644
--- a/src/core/hle/service/sockets/sockets.cpp
+++ b/src/core/hle/service/sockets/sockets.cpp
@@ -10,9 +10,9 @@
10 10
11namespace Service::Sockets { 11namespace Service::Sockets {
12 12
13void InstallInterfaces(SM::ServiceManager& service_manager) { 13void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
14 std::make_shared<BSD>("bsd:s")->InstallAsService(service_manager); 14 std::make_shared<BSD>(system, "bsd:s")->InstallAsService(service_manager);
15 std::make_shared<BSD>("bsd:u")->InstallAsService(service_manager); 15 std::make_shared<BSD>(system, "bsd:u")->InstallAsService(service_manager);
16 std::make_shared<BSDCFG>()->InstallAsService(service_manager); 16 std::make_shared<BSDCFG>()->InstallAsService(service_manager);
17 17
18 std::make_shared<ETHC_C>()->InstallAsService(service_manager); 18 std::make_shared<ETHC_C>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h
index ca8a6a7e0..89a410076 100644
--- a/src/core/hle/service/sockets/sockets.h
+++ b/src/core/hle/service/sockets/sockets.h
@@ -4,11 +4,94 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
10namespace Core {
11class System;
12}
13
9namespace Service::Sockets { 14namespace Service::Sockets {
10 15
16enum class Errno : u32 {
17 SUCCESS = 0,
18 BADF = 9,
19 AGAIN = 11,
20 INVAL = 22,
21 MFILE = 24,
22 NOTCONN = 107,
23};
24
25enum class Domain : u32 {
26 INET = 2,
27};
28
29enum class Type : u32 {
30 STREAM = 1,
31 DGRAM = 2,
32 RAW = 3,
33 SEQPACKET = 5,
34};
35
36enum class Protocol : u32 {
37 UNSPECIFIED = 0,
38 ICMP = 1,
39 TCP = 6,
40 UDP = 17,
41};
42
43enum class OptName : u32 {
44 REUSEADDR = 0x4,
45 BROADCAST = 0x20,
46 LINGER = 0x80,
47 SNDBUF = 0x1001,
48 RCVBUF = 0x1002,
49 SNDTIMEO = 0x1005,
50 RCVTIMEO = 0x1006,
51};
52
53enum class ShutdownHow : s32 {
54 RD = 0,
55 WR = 1,
56 RDWR = 2,
57};
58
59enum class FcntlCmd : s32 {
60 GETFL = 3,
61 SETFL = 4,
62};
63
64struct SockAddrIn {
65 u8 len;
66 u8 family;
67 u16 portno;
68 std::array<u8, 4> ip;
69 std::array<u8, 8> zeroes;
70};
71
72struct PollFD {
73 s32 fd;
74 u16 events;
75 u16 revents;
76};
77
78struct Linger {
79 u32 onoff;
80 u32 linger;
81};
82
83constexpr u16 POLL_IN = 0x01;
84constexpr u16 POLL_PRI = 0x02;
85constexpr u16 POLL_OUT = 0x04;
86constexpr u16 POLL_ERR = 0x08;
87constexpr u16 POLL_HUP = 0x10;
88constexpr u16 POLL_NVAL = 0x20;
89
90constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
91
92constexpr u32 FLAG_O_NONBLOCK = 0x800;
93
11/// Registers all Sockets services with the specified service manager. 94/// Registers all Sockets services with the specified service manager.
12void InstallInterfaces(SM::ServiceManager& service_manager); 95void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
13 96
14} // namespace Service::Sockets 97} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp
new file mode 100644
index 000000000..139743e1d
--- /dev/null
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -0,0 +1,165 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <utility>
6
7#include "common/assert.h"
8#include "common/common_types.h"
9#include "core/hle/service/sockets/sockets.h"
10#include "core/hle/service/sockets/sockets_translate.h"
11#include "core/network/network.h"
12
13namespace Service::Sockets {
14
15Errno Translate(Network::Errno value) {
16 switch (value) {
17 case Network::Errno::SUCCESS:
18 return Errno::SUCCESS;
19 case Network::Errno::BADF:
20 return Errno::BADF;
21 case Network::Errno::AGAIN:
22 return Errno::AGAIN;
23 case Network::Errno::INVAL:
24 return Errno::INVAL;
25 case Network::Errno::MFILE:
26 return Errno::MFILE;
27 case Network::Errno::NOTCONN:
28 return Errno::NOTCONN;
29 default:
30 UNIMPLEMENTED_MSG("Unimplemented errno={}", static_cast<int>(value));
31 return Errno::SUCCESS;
32 }
33}
34
35std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
36 return {value.first, Translate(value.second)};
37}
38
39Network::Domain Translate(Domain domain) {
40 switch (domain) {
41 case Domain::INET:
42 return Network::Domain::INET;
43 default:
44 UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain));
45 return {};
46 }
47}
48
49Domain Translate(Network::Domain domain) {
50 switch (domain) {
51 case Network::Domain::INET:
52 return Domain::INET;
53 default:
54 UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain));
55 return {};
56 }
57}
58
59Network::Type Translate(Type type) {
60 switch (type) {
61 case Type::STREAM:
62 return Network::Type::STREAM;
63 case Type::DGRAM:
64 return Network::Type::DGRAM;
65 default:
66 UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type));
67 }
68}
69
70Network::Protocol Translate(Type type, Protocol protocol) {
71 switch (protocol) {
72 case Protocol::UNSPECIFIED:
73 LOG_WARNING(Service, "Unspecified protocol, assuming protocol from type");
74 switch (type) {
75 case Type::DGRAM:
76 return Network::Protocol::UDP;
77 case Type::STREAM:
78 return Network::Protocol::TCP;
79 default:
80 return Network::Protocol::TCP;
81 }
82 case Protocol::TCP:
83 return Network::Protocol::TCP;
84 case Protocol::UDP:
85 return Network::Protocol::UDP;
86 default:
87 UNIMPLEMENTED_MSG("Unimplemented protocol={}", static_cast<int>(protocol));
88 return Network::Protocol::TCP;
89 }
90}
91
92u16 TranslatePollEventsToHost(u16 flags) {
93 u16 result = 0;
94 const auto translate = [&result, &flags](u16 from, u16 to) {
95 if ((flags & from) != 0) {
96 flags &= ~from;
97 result |= to;
98 }
99 };
100 translate(POLL_IN, Network::POLL_IN);
101 translate(POLL_PRI, Network::POLL_PRI);
102 translate(POLL_OUT, Network::POLL_OUT);
103 translate(POLL_ERR, Network::POLL_ERR);
104 translate(POLL_HUP, Network::POLL_HUP);
105 translate(POLL_NVAL, Network::POLL_NVAL);
106
107 UNIMPLEMENTED_IF_MSG(flags != 0, "Unimplemented flags={}", flags);
108 return result;
109}
110
111u16 TranslatePollEventsToGuest(u16 flags) {
112 u16 result = 0;
113 const auto translate = [&result, &flags](u16 from, u16 to) {
114 if ((flags & from) != 0) {
115 flags &= ~from;
116 result |= to;
117 }
118 };
119
120 translate(Network::POLL_IN, POLL_IN);
121 translate(Network::POLL_PRI, POLL_PRI);
122 translate(Network::POLL_OUT, POLL_OUT);
123 translate(Network::POLL_ERR, POLL_ERR);
124 translate(Network::POLL_HUP, POLL_HUP);
125 translate(Network::POLL_NVAL, POLL_NVAL);
126
127 UNIMPLEMENTED_IF_MSG(flags != 0, "Unimplemented flags={}", flags);
128 return result;
129}
130
131Network::SockAddrIn Translate(SockAddrIn value) {
132 ASSERT(value.len == 0 || value.len == sizeof(value));
133
134 return {
135 .family = Translate(static_cast<Domain>(value.family)),
136 .ip = value.ip,
137 .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
138 };
139}
140
141SockAddrIn Translate(Network::SockAddrIn value) {
142 return {
143 .len = sizeof(SockAddrIn),
144 .family = static_cast<u8>(Translate(value.family)),
145 .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
146 .ip = value.ip,
147 .zeroes = {},
148 };
149}
150
151Network::ShutdownHow Translate(ShutdownHow how) {
152 switch (how) {
153 case ShutdownHow::RD:
154 return Network::ShutdownHow::RD;
155 case ShutdownHow::WR:
156 return Network::ShutdownHow::WR;
157 case ShutdownHow::RDWR:
158 return Network::ShutdownHow::RDWR;
159 default:
160 UNIMPLEMENTED_MSG("Unimplemented how={}", static_cast<int>(how));
161 return {};
162 }
163}
164
165} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h
new file mode 100644
index 000000000..8ed041e31
--- /dev/null
+++ b/src/core/hle/service/sockets/sockets_translate.h
@@ -0,0 +1,48 @@
1// Copyright 2020 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 <utility>
8
9#include "common/common_types.h"
10#include "core/hle/service/sockets/sockets.h"
11#include "core/network/network.h"
12
13namespace Service::Sockets {
14
15/// Translate abstract errno to guest errno
16Errno Translate(Network::Errno value);
17
18/// Translate abstract return value errno pair to guest return value errno pair
19std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
20
21/// Translate guest domain to abstract domain
22Network::Domain Translate(Domain domain);
23
24/// Translate abstract domain to guest domain
25Domain Translate(Network::Domain domain);
26
27/// Translate guest type to abstract type
28Network::Type Translate(Type type);
29
30/// Translate guest protocol to abstract protocol
31Network::Protocol Translate(Type type, Protocol protocol);
32
33/// Translate abstract poll event flags to guest poll event flags
34u16 TranslatePollEventsToHost(u16 flags);
35
36/// Translate guest poll event flags to abstract poll event flags
37u16 TranslatePollEventsToGuest(u16 flags);
38
39/// Translate guest socket address structure to abstract socket address structure
40Network::SockAddrIn Translate(SockAddrIn value);
41
42/// Translate abstract socket address structure to guest socket address structure
43SockAddrIn Translate(Network::SockAddrIn value);
44
45/// Translate guest shutdown mode to abstract shutdown mode
46Network::ShutdownHow Translate(ShutdownHow how);
47
48} // namespace Service::Sockets
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 13e4b3818..ee4fa4b48 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -290,7 +290,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
290 290
291 IPC::ResponseBuilder rb{ctx, 2}; 291 IPC::ResponseBuilder rb{ctx, 2};
292 rb.Push(RESULT_SUCCESS); 292 rb.Push(RESULT_SUCCESS);
293 ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot)); 293 ctx.WriteBuffer(clock_snapshot);
294} 294}
295 295
296void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) { 296void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) {
@@ -313,7 +313,7 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques
313 313
314 IPC::ResponseBuilder rb{ctx, 2}; 314 IPC::ResponseBuilder rb{ctx, 2};
315 rb.Push(RESULT_SUCCESS); 315 rb.Push(RESULT_SUCCESS);
316 ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot)); 316 ctx.WriteBuffer(clock_snapshot);
317} 317}
318 318
319void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser( 319void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
index c070d6e97..320672add 100644
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -73,10 +73,8 @@ TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::
73 73
74 std::string location_name; 74 std::string location_name;
75 const auto timezone_setting = Settings::GetTimeZoneString(); 75 const auto timezone_setting = Settings::GetTimeZoneString();
76 if (timezone_setting == "auto") { 76 if (timezone_setting == "auto" || timezone_setting == "default") {
77 location_name = Common::TimeZone::GetDefaultTimeZone(); 77 location_name = Common::TimeZone::GetDefaultTimeZone();
78 } else if (timezone_setting == "default") {
79 location_name = location_name;
80 } else { 78 } else {
81 location_name = timezone_setting; 79 location_name = timezone_setting;
82 } 80 }
diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp
index db57ae069..ff3a10b3e 100644
--- a/src/core/hle/service/time/time_zone_service.cpp
+++ b/src/core/hle/service/time/time_zone_service.cpp
@@ -142,7 +142,7 @@ void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) {
142 IPC::ResponseBuilder rb{ctx, 3}; 142 IPC::ResponseBuilder rb{ctx, 3};
143 rb.Push(RESULT_SUCCESS); 143 rb.Push(RESULT_SUCCESS);
144 rb.PushRaw<u32>(1); // Number of times we're returning 144 rb.PushRaw<u32>(1); // Number of times we're returning
145 ctx.WriteBuffer(&posix_time, sizeof(s64)); 145 ctx.WriteBuffer(posix_time);
146} 146}
147 147
148void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { 148void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
@@ -164,7 +164,7 @@ void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
164 IPC::ResponseBuilder rb{ctx, 3}; 164 IPC::ResponseBuilder rb{ctx, 3};
165 rb.Push(RESULT_SUCCESS); 165 rb.Push(RESULT_SUCCESS);
166 rb.PushRaw<u32>(1); // Number of times we're returning 166 rb.PushRaw<u32>(1); // Number of times we're returning
167 ctx.WriteBuffer(&posix_time, sizeof(s64)); 167 ctx.WriteBuffer(posix_time);
168} 168}
169 169
170} // namespace Service::Time 170} // namespace Service::Time
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index ea7b4ae13..480d34725 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -511,7 +511,7 @@ private:
511 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, 511 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
512 static_cast<u32>(transaction), flags); 512 static_cast<u32>(transaction), flags);
513 513
514 nv_flinger->Lock(); 514 const auto guard = nv_flinger->Lock();
515 auto& buffer_queue = nv_flinger->FindBufferQueue(id); 515 auto& buffer_queue = nv_flinger->FindBufferQueue(id);
516 516
517 switch (transaction) { 517 switch (transaction) {
@@ -548,10 +548,10 @@ private:
548 // Wait the current thread until a buffer becomes available 548 // Wait the current thread until a buffer becomes available
549 ctx.SleepClientThread( 549 ctx.SleepClientThread(
550 "IHOSBinderDriver::DequeueBuffer", UINT64_MAX, 550 "IHOSBinderDriver::DequeueBuffer", UINT64_MAX,
551 [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, 551 [=, this](std::shared_ptr<Kernel::Thread> thread,
552 Kernel::ThreadWakeupReason reason) { 552 Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) {
553 // Repeat TransactParcel DequeueBuffer when a buffer is available 553 // Repeat TransactParcel DequeueBuffer when a buffer is available
554 nv_flinger->Lock(); 554 const auto guard = nv_flinger->Lock();
555 auto& buffer_queue = nv_flinger->FindBufferQueue(id); 555 auto& buffer_queue = nv_flinger->FindBufferQueue(id);
556 auto result = buffer_queue.DequeueBuffer(width, height); 556 auto result = buffer_queue.DequeueBuffer(width, height);
557 ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); 557 ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
@@ -1199,6 +1199,23 @@ private:
1199 } 1199 }
1200 } 1200 }
1201 1201
1202 void GetIndirectLayerImageRequiredMemoryInfo(Kernel::HLERequestContext& ctx) {
1203 IPC::RequestParser rp{ctx};
1204 const auto width = rp.Pop<u64>();
1205 const auto height = rp.Pop<u64>();
1206 LOG_DEBUG(Service_VI, "called width={}, height={}", width, height);
1207
1208 constexpr std::size_t base_size = 0x20000;
1209 constexpr std::size_t alignment = 0x1000;
1210 const auto texture_size = width * height * 4;
1211 const auto out_size = (texture_size + base_size - 1) / base_size * base_size;
1212
1213 IPC::ResponseBuilder rb{ctx, 6};
1214 rb.Push(RESULT_SUCCESS);
1215 rb.Push(out_size);
1216 rb.Push(alignment);
1217 }
1218
1202 static ResultVal<ConvertedScaleMode> ConvertScalingModeImpl(NintendoScaleMode mode) { 1219 static ResultVal<ConvertedScaleMode> ConvertScalingModeImpl(NintendoScaleMode mode) {
1203 switch (mode) { 1220 switch (mode) {
1204 case NintendoScaleMode::None: 1221 case NintendoScaleMode::None:
@@ -1243,7 +1260,8 @@ IApplicationDisplayService::IApplicationDisplayService(
1243 {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"}, 1260 {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
1244 {2450, nullptr, "GetIndirectLayerImageMap"}, 1261 {2450, nullptr, "GetIndirectLayerImageMap"},
1245 {2451, nullptr, "GetIndirectLayerImageCropMap"}, 1262 {2451, nullptr, "GetIndirectLayerImageCropMap"},
1246 {2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"}, 1263 {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
1264 "GetIndirectLayerImageRequiredMemoryInfo"},
1247 {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"}, 1265 {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
1248 {5203, nullptr, "GetDisplayVsyncEventForDebug"}, 1266 {5203, nullptr, "GetDisplayVsyncEventForDebug"},
1249 }; 1267 };
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 134e83412..394a1bf26 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -89,7 +89,7 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::Virtua
89} 89}
90 90
91AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load( 91AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load(
92 Kernel::Process& process) { 92 Kernel::Process& process, Core::System& system) {
93 if (is_loaded) { 93 if (is_loaded) {
94 return {ResultStatus::ErrorAlreadyLoaded, {}}; 94 return {ResultStatus::ErrorAlreadyLoaded, {}};
95 } 95 }
@@ -141,9 +141,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
141 continue; 141 continue;
142 } 142 }
143 143
144 const bool should_pass_arguments{std::strcmp(module, "rtld") == 0}; 144 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
145 const auto tentative_next_load_addr{AppLoader_NSO::LoadModule( 145 const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
146 process, *module_file, code_size, should_pass_arguments, false)}; 146 process, system, *module_file, code_size, should_pass_arguments, false);
147 if (!tentative_next_load_addr) { 147 if (!tentative_next_load_addr) {
148 return {ResultStatus::ErrorLoadingNSO, {}}; 148 return {ResultStatus::ErrorLoadingNSO, {}};
149 } 149 }
@@ -168,9 +168,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
168 } 168 }
169 169
170 const VAddr load_addr{next_load_addr}; 170 const VAddr load_addr{next_load_addr};
171 const bool should_pass_arguments{std::strcmp(module, "rtld") == 0}; 171 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
172 const auto tentative_next_load_addr{AppLoader_NSO::LoadModule( 172 const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
173 process, *module_file, load_addr, should_pass_arguments, true, pm)}; 173 process, system, *module_file, load_addr, should_pass_arguments, true, pm);
174 if (!tentative_next_load_addr) { 174 if (!tentative_next_load_addr) {
175 return {ResultStatus::ErrorLoadingNSO, {}}; 175 return {ResultStatus::ErrorLoadingNSO, {}};
176 } 176 }
@@ -192,8 +192,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
192 // Register the RomFS if a ".romfs" file was found 192 // Register the RomFS if a ".romfs" file was found
193 if (romfs_iter != files.end() && *romfs_iter != nullptr) { 193 if (romfs_iter != files.end() && *romfs_iter != nullptr) {
194 romfs = *romfs_iter; 194 romfs = *romfs_iter;
195 Core::System::GetInstance().GetFileSystemController().RegisterRomFS( 195 system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
196 std::make_unique<FileSys::RomFSFactory>(*this)); 196 *this, system.GetContentProvider(), system.GetFileSystemController()));
197 } 197 }
198 198
199 is_loaded = true; 199 is_loaded = true;
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 1c0a354a4..35d340317 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -9,6 +9,10 @@
9#include "core/file_sys/program_metadata.h" 9#include "core/file_sys/program_metadata.h"
10#include "core/loader/loader.h" 10#include "core/loader/loader.h"
11 11
12namespace Core {
13class System;
14}
15
12namespace Loader { 16namespace Loader {
13 17
14/** 18/**
@@ -37,7 +41,7 @@ public:
37 return IdentifyType(file); 41 return IdentifyType(file);
38 } 42 }
39 43
40 LoadResult Load(Kernel::Process& process) override; 44 LoadResult Load(Kernel::Process& process, Core::System& system) override;
41 45
42 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 46 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
43 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 47 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 8f7615115..dca1fcb18 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -383,7 +383,8 @@ FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) {
383 return FileType::Error; 383 return FileType::Error;
384} 384}
385 385
386AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) { 386AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process,
387 [[maybe_unused]] Core::System& system) {
387 if (is_loaded) { 388 if (is_loaded) {
388 return {ResultStatus::ErrorAlreadyLoaded, {}}; 389 return {ResultStatus::ErrorAlreadyLoaded, {}};
389 } 390 }
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index 7ef7770a6..3527933ad 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -8,6 +8,10 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/loader/loader.h" 9#include "core/loader/loader.h"
10 10
11namespace Core {
12class System;
13}
14
11namespace Loader { 15namespace Loader {
12 16
13/// Loads an ELF/AXF file 17/// Loads an ELF/AXF file
@@ -26,7 +30,7 @@ public:
26 return IdentifyType(file); 30 return IdentifyType(file);
27 } 31 }
28 32
29 LoadResult Load(Kernel::Process& process) override; 33 LoadResult Load(Kernel::Process& process, Core::System& system) override;
30}; 34};
31 35
32} // namespace Loader 36} // namespace Loader
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index 40fa03ad1..5981bcd21 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -43,7 +43,8 @@ FileType AppLoader_KIP::GetFileType() const {
43 : FileType::Error; 43 : FileType::Error;
44} 44}
45 45
46AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) { 46AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process,
47 [[maybe_unused]] Core::System& system) {
47 if (is_loaded) { 48 if (is_loaded) {
48 return {ResultStatus::ErrorAlreadyLoaded, {}}; 49 return {ResultStatus::ErrorAlreadyLoaded, {}};
49 } 50 }
diff --git a/src/core/loader/kip.h b/src/core/loader/kip.h
index 12ca40269..dee05a7b5 100644
--- a/src/core/loader/kip.h
+++ b/src/core/loader/kip.h
@@ -6,6 +6,10 @@
6 6
7#include "core/loader/loader.h" 7#include "core/loader/loader.h"
8 8
9namespace Core {
10class System;
11}
12
9namespace FileSys { 13namespace FileSys {
10class KIP; 14class KIP;
11} 15}
@@ -26,7 +30,7 @@ public:
26 30
27 FileType GetFileType() const override; 31 FileType GetFileType() const override;
28 32
29 LoadResult Load(Kernel::Process& process) override; 33 LoadResult Load(Kernel::Process& process, Core::System& system) override;
30 34
31private: 35private:
32 std::unique_ptr<FileSys::KIP> kip; 36 std::unique_ptr<FileSys::KIP> kip;
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 59ca7091a..9bc3a8840 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -3,8 +3,10 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include <optional>
6#include <ostream> 7#include <ostream>
7#include <string> 8#include <string>
9#include "common/concepts.h"
8#include "common/file_util.h" 10#include "common/file_util.h"
9#include "common/logging/log.h" 11#include "common/logging/log.h"
10#include "common/string_util.h" 12#include "common/string_util.h"
@@ -21,27 +23,41 @@
21 23
22namespace Loader { 24namespace Loader {
23 25
24FileType IdentifyFile(FileSys::VirtualFile file) { 26namespace {
25 FileType type;
26
27#define CHECK_TYPE(loader) \
28 type = AppLoader_##loader::IdentifyType(file); \
29 if (FileType::Error != type) \
30 return type;
31 27
32 CHECK_TYPE(DeconstructedRomDirectory) 28template <Common::DerivedFrom<AppLoader> T>
33 CHECK_TYPE(ELF) 29std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
34 CHECK_TYPE(NSO) 30 const auto file_type = T::IdentifyType(file);
35 CHECK_TYPE(NRO) 31 if (file_type != FileType::Error) {
36 CHECK_TYPE(NCA) 32 return file_type;
37 CHECK_TYPE(XCI) 33 }
38 CHECK_TYPE(NAX) 34 return std::nullopt;
39 CHECK_TYPE(NSP) 35}
40 CHECK_TYPE(KIP)
41 36
42#undef CHECK_TYPE 37} // namespace
43 38
44 return FileType::Unknown; 39FileType IdentifyFile(FileSys::VirtualFile file) {
40 if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) {
41 return *romdir_type;
42 } else if (const auto elf_type = IdentifyFileLoader<AppLoader_ELF>(file)) {
43 return *elf_type;
44 } else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) {
45 return *nso_type;
46 } else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) {
47 return *nro_type;
48 } else if (const auto nca_type = IdentifyFileLoader<AppLoader_NCA>(file)) {
49 return *nca_type;
50 } else if (const auto xci_type = IdentifyFileLoader<AppLoader_XCI>(file)) {
51 return *xci_type;
52 } else if (const auto nax_type = IdentifyFileLoader<AppLoader_NAX>(file)) {
53 return *nax_type;
54 } else if (const auto nsp_type = IdentifyFileLoader<AppLoader_NSP>(file)) {
55 return *nsp_type;
56 } else if (const auto kip_type = IdentifyFileLoader<AppLoader_KIP>(file)) {
57 return *kip_type;
58 } else {
59 return FileType::Unknown;
60 }
45} 61}
46 62
47FileType GuessFromFilename(const std::string& name) { 63FileType GuessFromFilename(const std::string& name) {
@@ -51,7 +67,7 @@ FileType GuessFromFilename(const std::string& name) {
51 return FileType::NCA; 67 return FileType::NCA;
52 68
53 const std::string extension = 69 const std::string extension =
54 Common::ToLower(std::string(FileUtil::GetExtensionFromFilename(name))); 70 Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
55 71
56 if (extension == "elf") 72 if (extension == "elf")
57 return FileType::ELF; 73 return FileType::ELF;
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 227ecc704..ac60b097a 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -15,6 +15,10 @@
15#include "core/file_sys/control_metadata.h" 15#include "core/file_sys/control_metadata.h"
16#include "core/file_sys/vfs.h" 16#include "core/file_sys/vfs.h"
17 17
18namespace Core {
19class System;
20}
21
18namespace FileSys { 22namespace FileSys {
19class NACP; 23class NACP;
20} // namespace FileSys 24} // namespace FileSys
@@ -154,9 +158,10 @@ public:
154 /** 158 /**
155 * Load the application and return the created Process instance 159 * Load the application and return the created Process instance
156 * @param process The newly created process. 160 * @param process The newly created process.
161 * @param system The system that this process is being loaded under.
157 * @return The status result of the operation. 162 * @return The status result of the operation.
158 */ 163 */
159 virtual LoadResult Load(Kernel::Process& process) = 0; 164 virtual LoadResult Load(Kernel::Process& process, Core::System& system) = 0;
160 165
161 /** 166 /**
162 * Get the code (typically .code section) of the application 167 * Get the code (typically .code section) of the application
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index a152981a0..49028177b 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -41,7 +41,8 @@ FileType AppLoader_NAX::GetFileType() const {
41 return IdentifyTypeImpl(*nax); 41 return IdentifyTypeImpl(*nax);
42} 42}
43 43
44AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) { 44AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process,
45 [[maybe_unused]] Core::System& system) {
45 if (is_loaded) { 46 if (is_loaded) {
46 return {ResultStatus::ErrorAlreadyLoaded, {}}; 47 return {ResultStatus::ErrorAlreadyLoaded, {}};
47 } 48 }
@@ -65,7 +66,7 @@ AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) {
65 return {nca_status, {}}; 66 return {nca_status, {}};
66 } 67 }
67 68
68 const auto result = nca_loader->Load(process); 69 const auto result = nca_loader->Load(process, system);
69 if (result.first != ResultStatus::Success) { 70 if (result.first != ResultStatus::Success) {
70 return result; 71 return result;
71 } 72 }
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index eaec9bf58..c2b7722b5 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -8,10 +8,12 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/loader/loader.h" 9#include "core/loader/loader.h"
10 10
11namespace FileSys { 11namespace Core {
12class System;
13}
12 14
15namespace FileSys {
13class NAX; 16class NAX;
14
15} // namespace FileSys 17} // namespace FileSys
16 18
17namespace Loader { 19namespace Loader {
@@ -33,7 +35,7 @@ public:
33 35
34 FileType GetFileType() const override; 36 FileType GetFileType() const override;
35 37
36 LoadResult Load(Kernel::Process& process) override; 38 LoadResult Load(Kernel::Process& process, Core::System& system) override;
37 39
38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 40 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
39 u64 ReadRomFSIVFCOffset() const override; 41 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 5a0469978..fa694de37 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -31,7 +31,7 @@ FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
31 return FileType::Error; 31 return FileType::Error;
32} 32}
33 33
34AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) { 34AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process, Core::System& system) {
35 if (is_loaded) { 35 if (is_loaded) {
36 return {ResultStatus::ErrorAlreadyLoaded, {}}; 36 return {ResultStatus::ErrorAlreadyLoaded, {}};
37 } 37 }
@@ -52,14 +52,14 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
52 52
53 directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true); 53 directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true);
54 54
55 const auto load_result = directory_loader->Load(process); 55 const auto load_result = directory_loader->Load(process, system);
56 if (load_result.first != ResultStatus::Success) { 56 if (load_result.first != ResultStatus::Success) {
57 return load_result; 57 return load_result;
58 } 58 }
59 59
60 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) { 60 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
61 Core::System::GetInstance().GetFileSystemController().RegisterRomFS( 61 system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
62 std::make_unique<FileSys::RomFSFactory>(*this)); 62 *this, system.GetContentProvider(), system.GetFileSystemController()));
63 } 63 }
64 64
65 is_loaded = true; 65 is_loaded = true;
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index e47dc0e47..711070294 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -8,6 +8,10 @@
8#include "core/file_sys/vfs.h" 8#include "core/file_sys/vfs.h"
9#include "core/loader/loader.h" 9#include "core/loader/loader.h"
10 10
11namespace Core {
12class System;
13}
14
11namespace FileSys { 15namespace FileSys {
12class NCA; 16class NCA;
13} 17}
@@ -33,7 +37,7 @@ public:
33 return IdentifyType(file); 37 return IdentifyType(file);
34 } 38 }
35 39
36 LoadResult Load(Kernel::Process& process) override; 40 LoadResult Load(Kernel::Process& process, Core::System& system) override;
37 41
38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 42 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
39 u64 ReadRomFSIVFCOffset() const override; 43 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 906544bc9..9fb5eddad 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -208,7 +208,7 @@ bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& fi
208 return LoadNroImpl(process, file.ReadAllBytes(), file.GetName()); 208 return LoadNroImpl(process, file.ReadAllBytes(), file.GetName());
209} 209}
210 210
211AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) { 211AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process, Core::System& system) {
212 if (is_loaded) { 212 if (is_loaded) {
213 return {ResultStatus::ErrorAlreadyLoaded, {}}; 213 return {ResultStatus::ErrorAlreadyLoaded, {}};
214 } 214 }
@@ -218,8 +218,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
218 } 218 }
219 219
220 if (romfs != nullptr) { 220 if (romfs != nullptr) {
221 Core::System::GetInstance().GetFileSystemController().RegisterRomFS( 221 system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
222 std::make_unique<FileSys::RomFSFactory>(*this)); 222 *this, system.GetContentProvider(), system.GetFileSystemController()));
223 } 223 }
224 224
225 is_loaded = true; 225 is_loaded = true;
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 4593d48fb..a2aab2ecc 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -10,6 +10,10 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/loader/loader.h" 11#include "core/loader/loader.h"
12 12
13namespace Core {
14class System;
15}
16
13namespace FileSys { 17namespace FileSys {
14class NACP; 18class NACP;
15} 19}
@@ -37,7 +41,7 @@ public:
37 return IdentifyType(file); 41 return IdentifyType(file);
38 } 42 }
39 43
40 LoadResult Load(Kernel::Process& process) override; 44 LoadResult Load(Kernel::Process& process, Core::System& system) override;
41 45
42 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 46 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
43 ResultStatus ReadProgramId(u64& out_program_id) override; 47 ResultStatus ReadProgramId(u64& out_program_id) override;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 575330a86..1e70f6e11 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -71,21 +71,21 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
71 return FileType::NSO; 71 return FileType::NSO;
72} 72}
73 73
74std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, 74std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::System& system,
75 const FileSys::VfsFile& file, VAddr load_base, 75 const FileSys::VfsFile& file, VAddr load_base,
76 bool should_pass_arguments, bool load_into_process, 76 bool should_pass_arguments, bool load_into_process,
77 std::optional<FileSys::PatchManager> pm) { 77 std::optional<FileSys::PatchManager> pm) {
78 if (file.GetSize() < sizeof(NSOHeader)) { 78 if (file.GetSize() < sizeof(NSOHeader)) {
79 return {}; 79 return std::nullopt;
80 } 80 }
81 81
82 NSOHeader nso_header{}; 82 NSOHeader nso_header{};
83 if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) { 83 if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) {
84 return {}; 84 return std::nullopt;
85 } 85 }
86 86
87 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) { 87 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
88 return {}; 88 return std::nullopt;
89 } 89 }
90 90
91 // Build program image 91 // Build program image
@@ -148,7 +148,6 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
148 148
149 // Apply cheats if they exist and the program has a valid title ID 149 // Apply cheats if they exist and the program has a valid title ID
150 if (pm) { 150 if (pm) {
151 auto& system = Core::System::GetInstance();
152 system.SetCurrentProcessBuildID(nso_header.build_id); 151 system.SetCurrentProcessBuildID(nso_header.build_id);
153 const auto cheats = pm->CreateCheatList(system, nso_header.build_id); 152 const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
154 if (!cheats.empty()) { 153 if (!cheats.empty()) {
@@ -166,7 +165,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
166 return load_base + image_size; 165 return load_base + image_size;
167} 166}
168 167
169AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) { 168AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process, Core::System& system) {
170 if (is_loaded) { 169 if (is_loaded) {
171 return {ResultStatus::ErrorAlreadyLoaded, {}}; 170 return {ResultStatus::ErrorAlreadyLoaded, {}};
172 } 171 }
@@ -175,7 +174,7 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
175 174
176 // Load module 175 // Load module
177 const VAddr base_address = process.PageTable().GetCodeRegionStart(); 176 const VAddr base_address = process.PageTable().GetCodeRegionStart();
178 if (!LoadModule(process, *file, base_address, true, true)) { 177 if (!LoadModule(process, system, *file, base_address, true, true)) {
179 return {ResultStatus::ErrorLoadingNSO, {}}; 178 return {ResultStatus::ErrorLoadingNSO, {}};
180 } 179 }
181 180
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index b210830f0..4bd47787d 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -12,6 +12,10 @@
12#include "core/file_sys/patch_manager.h" 12#include "core/file_sys/patch_manager.h"
13#include "core/loader/loader.h" 13#include "core/loader/loader.h"
14 14
15namespace Core {
16class System;
17}
18
15namespace Kernel { 19namespace Kernel {
16class Process; 20class Process;
17} 21}
@@ -80,12 +84,12 @@ public:
80 return IdentifyType(file); 84 return IdentifyType(file);
81 } 85 }
82 86
83 static std::optional<VAddr> LoadModule(Kernel::Process& process, const FileSys::VfsFile& file, 87 static std::optional<VAddr> LoadModule(Kernel::Process& process, Core::System& system,
84 VAddr load_base, bool should_pass_arguments, 88 const FileSys::VfsFile& file, VAddr load_base,
85 bool load_into_process, 89 bool should_pass_arguments, bool load_into_process,
86 std::optional<FileSys::PatchManager> pm = {}); 90 std::optional<FileSys::PatchManager> pm = {});
87 91
88 LoadResult Load(Kernel::Process& process) override; 92 LoadResult Load(Kernel::Process& process, Core::System& system) override;
89 93
90 ResultStatus ReadNSOModules(Modules& modules) override; 94 ResultStatus ReadNSOModules(Modules& modules) override;
91 95
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 13950fc08..15e528fa8 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -71,7 +71,7 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
71 return FileType::Error; 71 return FileType::Error;
72} 72}
73 73
74AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) { 74AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process, Core::System& system) {
75 if (is_loaded) { 75 if (is_loaded) {
76 return {ResultStatus::ErrorAlreadyLoaded, {}}; 76 return {ResultStatus::ErrorAlreadyLoaded, {}};
77 } 77 }
@@ -99,15 +99,14 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
99 return {ResultStatus::ErrorNSPMissingProgramNCA, {}}; 99 return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
100 } 100 }
101 101
102 const auto result = secondary_loader->Load(process); 102 const auto result = secondary_loader->Load(process, system);
103 if (result.first != ResultStatus::Success) { 103 if (result.first != ResultStatus::Success) {
104 return result; 104 return result;
105 } 105 }
106 106
107 FileSys::VirtualFile update_raw; 107 FileSys::VirtualFile update_raw;
108 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { 108 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
109 Core::System::GetInstance().GetFileSystemController().SetPackedUpdate( 109 system.GetFileSystemController().SetPackedUpdate(std::move(update_raw));
110 std::move(update_raw));
111 } 110 }
112 111
113 is_loaded = true; 112 is_loaded = true;
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 868b028d3..b27deb686 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -9,6 +9,10 @@
9#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
10#include "core/loader/loader.h" 10#include "core/loader/loader.h"
11 11
12namespace Core {
13class System;
14}
15
12namespace FileSys { 16namespace FileSys {
13class NACP; 17class NACP;
14class NSP; 18class NSP;
@@ -35,7 +39,7 @@ public:
35 return IdentifyType(file); 39 return IdentifyType(file);
36 } 40 }
37 41
38 LoadResult Load(Kernel::Process& process) override; 42 LoadResult Load(Kernel::Process& process, Core::System& system) override;
39 43
40 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; 44 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
41 u64 ReadRomFSIVFCOffset() const override; 45 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 7186ad1ff..25e83af0f 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -49,7 +49,7 @@ FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) {
49 return FileType::Error; 49 return FileType::Error;
50} 50}
51 51
52AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) { 52AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process, Core::System& system) {
53 if (is_loaded) { 53 if (is_loaded) {
54 return {ResultStatus::ErrorAlreadyLoaded, {}}; 54 return {ResultStatus::ErrorAlreadyLoaded, {}};
55 } 55 }
@@ -66,15 +66,14 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
66 return {ResultStatus::ErrorMissingProductionKeyFile, {}}; 66 return {ResultStatus::ErrorMissingProductionKeyFile, {}};
67 } 67 }
68 68
69 const auto result = nca_loader->Load(process); 69 const auto result = nca_loader->Load(process, system);
70 if (result.first != ResultStatus::Success) { 70 if (result.first != ResultStatus::Success) {
71 return result; 71 return result;
72 } 72 }
73 73
74 FileSys::VirtualFile update_raw; 74 FileSys::VirtualFile update_raw;
75 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { 75 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
76 Core::System::GetInstance().GetFileSystemController().SetPackedUpdate( 76 system.GetFileSystemController().SetPackedUpdate(std::move(update_raw));
77 std::move(update_raw));
78 } 77 }
79 78
80 is_loaded = true; 79 is_loaded = true;
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 618ae2f47..04aea286f 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -9,6 +9,10 @@
9#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
10#include "core/loader/loader.h" 10#include "core/loader/loader.h"
11 11
12namespace Core {
13class System;
14}
15
12namespace FileSys { 16namespace FileSys {
13class NACP; 17class NACP;
14class XCI; 18class XCI;
@@ -35,7 +39,7 @@ public:
35 return IdentifyType(file); 39 return IdentifyType(file);
36 } 40 }
37 41
38 LoadResult Load(Kernel::Process& process) override; 42 LoadResult Load(Kernel::Process& process, Core::System& system) override;
39 43
40 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; 44 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
41 u64 ReadRomFSIVFCOffset() const override; 45 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 2c5588933..c3f4829d7 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -567,7 +567,7 @@ struct Memory::Impl {
567 * @param page_table The page table to use to perform the mapping. 567 * @param page_table The page table to use to perform the mapping.
568 * @param base The base address to begin mapping at. 568 * @param base The base address to begin mapping at.
569 * @param size The total size of the range in bytes. 569 * @param size The total size of the range in bytes.
570 * @param memory The memory to map. 570 * @param target The target address to begin mapping from.
571 * @param type The page type to map the memory as. 571 * @param type The page type to map the memory as.
572 */ 572 */
573 void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target, 573 void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target,
@@ -704,7 +704,7 @@ struct Memory::Impl {
704 u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; 704 u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
705 if (page_pointer != nullptr) { 705 if (page_pointer != nullptr) {
706 // NOTE: Avoid adding any extra logic to this fast-path block 706 // NOTE: Avoid adding any extra logic to this fast-path block
707 T volatile* pointer = reinterpret_cast<T volatile*>(&page_pointer[vaddr]); 707 auto* pointer = reinterpret_cast<volatile T*>(&page_pointer[vaddr]);
708 return Common::AtomicCompareAndSwap(pointer, data, expected); 708 return Common::AtomicCompareAndSwap(pointer, data, expected);
709 } 709 }
710 710
@@ -720,9 +720,8 @@ struct Memory::Impl {
720 case Common::PageType::RasterizerCachedMemory: { 720 case Common::PageType::RasterizerCachedMemory: {
721 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; 721 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
722 system.GPU().InvalidateRegion(vaddr, sizeof(T)); 722 system.GPU().InvalidateRegion(vaddr, sizeof(T));
723 T volatile* pointer = reinterpret_cast<T volatile*>(&host_ptr); 723 auto* pointer = reinterpret_cast<volatile T*>(&host_ptr);
724 return Common::AtomicCompareAndSwap(pointer, data, expected); 724 return Common::AtomicCompareAndSwap(pointer, data, expected);
725 break;
726 } 725 }
727 default: 726 default:
728 UNREACHABLE(); 727 UNREACHABLE();
@@ -734,7 +733,7 @@ struct Memory::Impl {
734 u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; 733 u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
735 if (page_pointer != nullptr) { 734 if (page_pointer != nullptr) {
736 // NOTE: Avoid adding any extra logic to this fast-path block 735 // NOTE: Avoid adding any extra logic to this fast-path block
737 u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&page_pointer[vaddr]); 736 auto* pointer = reinterpret_cast<volatile u64*>(&page_pointer[vaddr]);
738 return Common::AtomicCompareAndSwap(pointer, data, expected); 737 return Common::AtomicCompareAndSwap(pointer, data, expected);
739 } 738 }
740 739
@@ -750,9 +749,8 @@ struct Memory::Impl {
750 case Common::PageType::RasterizerCachedMemory: { 749 case Common::PageType::RasterizerCachedMemory: {
751 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; 750 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
752 system.GPU().InvalidateRegion(vaddr, sizeof(u128)); 751 system.GPU().InvalidateRegion(vaddr, sizeof(u128));
753 u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&host_ptr); 752 auto* pointer = reinterpret_cast<volatile u64*>(&host_ptr);
754 return Common::AtomicCompareAndSwap(pointer, data, expected); 753 return Common::AtomicCompareAndSwap(pointer, data, expected);
755 break;
756 } 754 }
757 default: 755 default:
758 UNREACHABLE(); 756 UNREACHABLE();
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 53d27859b..29284a42d 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -19,10 +19,24 @@
19#include "core/memory/cheat_engine.h" 19#include "core/memory/cheat_engine.h"
20 20
21namespace Core::Memory { 21namespace Core::Memory {
22 22namespace {
23constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(1000000000 / 12); 23constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
24constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; 24constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
25 25
26std::string_view ExtractName(std::string_view data, std::size_t start_index, char match) {
27 auto end_index = start_index;
28 while (data[end_index] != match) {
29 ++end_index;
30 if (end_index > data.size() ||
31 (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) {
32 return {};
33 }
34 }
35
36 return data.substr(start_index, end_index - start_index);
37}
38} // Anonymous namespace
39
26StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) 40StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata)
27 : metadata(metadata), system(system) {} 41 : metadata(metadata), system(system) {}
28 42
@@ -42,7 +56,7 @@ u64 StandardVmCallbacks::HidKeysDown() {
42 if (applet_resource == nullptr) { 56 if (applet_resource == nullptr) {
43 LOG_WARNING(CheatEngine, 57 LOG_WARNING(CheatEngine,
44 "Attempted to read input state, but applet resource is not initialized!"); 58 "Attempted to read input state, but applet resource is not initialized!");
45 return false; 59 return 0;
46 } 60 }
47 61
48 const auto press_state = 62 const auto press_state =
@@ -82,26 +96,9 @@ CheatParser::~CheatParser() = default;
82 96
83TextCheatParser::~TextCheatParser() = default; 97TextCheatParser::~TextCheatParser() = default;
84 98
85namespace { 99std::vector<CheatEntry> TextCheatParser::Parse(std::string_view data) const {
86template <char match>
87std::string_view ExtractName(std::string_view data, std::size_t start_index) {
88 auto end_index = start_index;
89 while (data[end_index] != match) {
90 ++end_index;
91 if (end_index > data.size() ||
92 (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) {
93 return {};
94 }
95 }
96
97 return data.substr(start_index, end_index - start_index);
98}
99} // Anonymous namespace
100
101std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
102 std::string_view data) const {
103 std::vector<CheatEntry> out(1); 100 std::vector<CheatEntry> out(1);
104 std::optional<u64> current_entry = std::nullopt; 101 std::optional<u64> current_entry;
105 102
106 for (std::size_t i = 0; i < data.size(); ++i) { 103 for (std::size_t i = 0; i < data.size(); ++i) {
107 if (::isspace(data[i])) { 104 if (::isspace(data[i])) {
@@ -115,7 +112,7 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
115 return {}; 112 return {};
116 } 113 }
117 114
118 const auto name = ExtractName<'}'>(data, i + 1); 115 const auto name = ExtractName(data, i + 1, '}');
119 if (name.empty()) { 116 if (name.empty()) {
120 return {}; 117 return {};
121 } 118 }
@@ -132,7 +129,7 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
132 current_entry = out.size(); 129 current_entry = out.size();
133 out.emplace_back(); 130 out.emplace_back();
134 131
135 const auto name = ExtractName<']'>(data, i + 1); 132 const auto name = ExtractName(data, i + 1, ']');
136 if (name.empty()) { 133 if (name.empty()) {
137 return {}; 134 return {};
138 } 135 }
@@ -190,24 +187,38 @@ CheatEngine::~CheatEngine() {
190void CheatEngine::Initialize() { 187void CheatEngine::Initialize() {
191 event = Core::Timing::CreateEvent( 188 event = Core::Timing::CreateEvent(
192 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), 189 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
193 [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); }); 190 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
194 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); 191 FrameCallback(user_data, ns_late);
192 });
193 core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event);
195 194
196 metadata.process_id = system.CurrentProcess()->GetProcessID(); 195 metadata.process_id = system.CurrentProcess()->GetProcessID();
197 metadata.title_id = system.CurrentProcess()->GetTitleID(); 196 metadata.title_id = system.CurrentProcess()->GetTitleID();
198 197
199 const auto& page_table = system.CurrentProcess()->PageTable(); 198 const auto& page_table = system.CurrentProcess()->PageTable();
200 metadata.heap_extents = {page_table.GetHeapRegionStart(), page_table.GetHeapRegionSize()}; 199 metadata.heap_extents = {
201 metadata.address_space_extents = {page_table.GetAddressSpaceStart(), 200 .base = page_table.GetHeapRegionStart(),
202 page_table.GetAddressSpaceSize()}; 201 .size = page_table.GetHeapRegionSize(),
203 metadata.alias_extents = {page_table.GetAliasCodeRegionStart(), 202 };
204 page_table.GetAliasCodeRegionSize()}; 203
204 metadata.address_space_extents = {
205 .base = page_table.GetAddressSpaceStart(),
206 .size = page_table.GetAddressSpaceSize(),
207 };
208
209 metadata.alias_extents = {
210 .base = page_table.GetAliasCodeRegionStart(),
211 .size = page_table.GetAliasCodeRegionSize(),
212 };
205 213
206 is_pending_reload.exchange(true); 214 is_pending_reload.exchange(true);
207} 215}
208 216
209void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) { 217void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
210 metadata.main_nso_extents = {main_region_begin, main_region_size}; 218 metadata.main_nso_extents = {
219 .base = main_region_begin,
220 .size = main_region_size,
221 };
211} 222}
212 223
213void CheatEngine::Reload(std::vector<CheatEntry> cheats) { 224void CheatEngine::Reload(std::vector<CheatEntry> cheats) {
@@ -217,7 +228,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> cheats) {
217 228
218MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); 229MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
219 230
220void CheatEngine::FrameCallback(u64 userdata, s64 ns_late) { 231void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
221 if (is_pending_reload.exchange(false)) { 232 if (is_pending_reload.exchange(false)) {
222 vm.LoadProgram(cheats); 233 vm.LoadProgram(cheats);
223 } 234 }
@@ -230,7 +241,7 @@ void CheatEngine::FrameCallback(u64 userdata, s64 ns_late) {
230 241
231 vm.Execute(metadata); 242 vm.Execute(metadata);
232 243
233 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - ns_late, event); 244 core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event);
234} 245}
235 246
236} // namespace Core::Memory 247} // namespace Core::Memory
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
index 2649423f8..a31002346 100644
--- a/src/core/memory/cheat_engine.h
+++ b/src/core/memory/cheat_engine.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <atomic>
8#include <chrono>
8#include <memory> 9#include <memory>
9#include <vector> 10#include <vector>
10#include "common/common_types.h" 11#include "common/common_types.h"
@@ -46,8 +47,7 @@ class CheatParser {
46public: 47public:
47 virtual ~CheatParser(); 48 virtual ~CheatParser();
48 49
49 virtual std::vector<CheatEntry> Parse(const Core::System& system, 50 [[nodiscard]] virtual std::vector<CheatEntry> Parse(std::string_view data) const = 0;
50 std::string_view data) const = 0;
51}; 51};
52 52
53// CheatParser implementation that parses text files 53// CheatParser implementation that parses text files
@@ -55,7 +55,7 @@ class TextCheatParser final : public CheatParser {
55public: 55public:
56 ~TextCheatParser() override; 56 ~TextCheatParser() override;
57 57
58 std::vector<CheatEntry> Parse(const Core::System& system, std::string_view data) const override; 58 [[nodiscard]] std::vector<CheatEntry> Parse(std::string_view data) const override;
59}; 59};
60 60
61// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming 61// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
@@ -71,7 +71,7 @@ public:
71 void Reload(std::vector<CheatEntry> cheats); 71 void Reload(std::vector<CheatEntry> cheats);
72 72
73private: 73private:
74 void FrameCallback(u64 userdata, s64 cycles_late); 74 void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
75 75
76 DmntCheatVm vm; 76 DmntCheatVm vm;
77 CheatProcessMetadata metadata; 77 CheatProcessMetadata metadata;
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp
index 2e7da23fe..48be80c12 100644
--- a/src/core/memory/dmnt_cheat_vm.cpp
+++ b/src/core/memory/dmnt_cheat_vm.cpp
@@ -313,30 +313,32 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
313 313
314 switch (opcode_type) { 314 switch (opcode_type) {
315 case CheatVmOpcodeType::StoreStatic: { 315 case CheatVmOpcodeType::StoreStatic: {
316 StoreStaticOpcode store_static{};
317 // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY) 316 // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
318 // Read additional words. 317 // Read additional words.
319 const u32 second_dword = GetNextDword(); 318 const u32 second_dword = GetNextDword();
320 store_static.bit_width = (first_dword >> 24) & 0xF; 319 const u32 bit_width = (first_dword >> 24) & 0xF;
321 store_static.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); 320
322 store_static.offset_register = ((first_dword >> 16) & 0xF); 321 opcode.opcode = StoreStaticOpcode{
323 store_static.rel_address = 322 .bit_width = bit_width,
324 (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); 323 .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF),
325 store_static.value = GetNextVmInt(store_static.bit_width); 324 .offset_register = (first_dword >> 16) & 0xF,
326 opcode.opcode = store_static; 325 .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword,
326 .value = GetNextVmInt(bit_width),
327 };
327 } break; 328 } break;
328 case CheatVmOpcodeType::BeginConditionalBlock: { 329 case CheatVmOpcodeType::BeginConditionalBlock: {
329 BeginConditionalOpcode begin_cond{};
330 // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY) 330 // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
331 // Read additional words. 331 // Read additional words.
332 const u32 second_dword = GetNextDword(); 332 const u32 second_dword = GetNextDword();
333 begin_cond.bit_width = (first_dword >> 24) & 0xF; 333 const u32 bit_width = (first_dword >> 24) & 0xF;
334 begin_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); 334
335 begin_cond.cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF); 335 opcode.opcode = BeginConditionalOpcode{
336 begin_cond.rel_address = 336 .bit_width = bit_width,
337 (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); 337 .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF),
338 begin_cond.value = GetNextVmInt(begin_cond.bit_width); 338 .cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF),
339 opcode.opcode = begin_cond; 339 .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword,
340 .value = GetNextVmInt(bit_width),
341 };
340 } break; 342 } break;
341 case CheatVmOpcodeType::EndConditionalBlock: { 343 case CheatVmOpcodeType::EndConditionalBlock: {
342 // 20000000 344 // 20000000
@@ -344,12 +346,14 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
344 opcode.opcode = EndConditionalOpcode{}; 346 opcode.opcode = EndConditionalOpcode{};
345 } break; 347 } break;
346 case CheatVmOpcodeType::ControlLoop: { 348 case CheatVmOpcodeType::ControlLoop: {
347 ControlLoopOpcode ctrl_loop{};
348 // 300R0000 VVVVVVVV 349 // 300R0000 VVVVVVVV
349 // 310R0000 350 // 310R0000
350 // Parse register, whether loop start or loop end. 351 // Parse register, whether loop start or loop end.
351 ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0; 352 ControlLoopOpcode ctrl_loop{
352 ctrl_loop.reg_index = ((first_dword >> 20) & 0xF); 353 .start_loop = ((first_dword >> 24) & 0xF) == 0,
354 .reg_index = (first_dword >> 20) & 0xF,
355 .num_iters = 0,
356 };
353 357
354 // Read number of iters if loop start. 358 // Read number of iters if loop start.
355 if (ctrl_loop.start_loop) { 359 if (ctrl_loop.start_loop) {
@@ -358,66 +362,65 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
358 opcode.opcode = ctrl_loop; 362 opcode.opcode = ctrl_loop;
359 } break; 363 } break;
360 case CheatVmOpcodeType::LoadRegisterStatic: { 364 case CheatVmOpcodeType::LoadRegisterStatic: {
361 LoadRegisterStaticOpcode ldr_static{};
362 // 400R0000 VVVVVVVV VVVVVVVV 365 // 400R0000 VVVVVVVV VVVVVVVV
363 // Read additional words. 366 // Read additional words.
364 ldr_static.reg_index = ((first_dword >> 16) & 0xF); 367 opcode.opcode = LoadRegisterStaticOpcode{
365 ldr_static.value = 368 .reg_index = (first_dword >> 16) & 0xF,
366 (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword()); 369 .value = (static_cast<u64>(GetNextDword()) << 32) | GetNextDword(),
367 opcode.opcode = ldr_static; 370 };
368 } break; 371 } break;
369 case CheatVmOpcodeType::LoadRegisterMemory: { 372 case CheatVmOpcodeType::LoadRegisterMemory: {
370 LoadRegisterMemoryOpcode ldr_memory{};
371 // 5TMRI0AA AAAAAAAA 373 // 5TMRI0AA AAAAAAAA
372 // Read additional words. 374 // Read additional words.
373 const u32 second_dword = GetNextDword(); 375 const u32 second_dword = GetNextDword();
374 ldr_memory.bit_width = (first_dword >> 24) & 0xF; 376 opcode.opcode = LoadRegisterMemoryOpcode{
375 ldr_memory.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); 377 .bit_width = (first_dword >> 24) & 0xF,
376 ldr_memory.reg_index = ((first_dword >> 16) & 0xF); 378 .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF),
377 ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0; 379 .reg_index = ((first_dword >> 16) & 0xF),
378 ldr_memory.rel_address = 380 .load_from_reg = ((first_dword >> 12) & 0xF) != 0,
379 (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); 381 .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword,
380 opcode.opcode = ldr_memory; 382 };
381 } break; 383 } break;
382 case CheatVmOpcodeType::StoreStaticToAddress: { 384 case CheatVmOpcodeType::StoreStaticToAddress: {
383 StoreStaticToAddressOpcode str_static{};
384 // 6T0RIor0 VVVVVVVV VVVVVVVV 385 // 6T0RIor0 VVVVVVVV VVVVVVVV
385 // Read additional words. 386 // Read additional words.
386 str_static.bit_width = (first_dword >> 24) & 0xF; 387 opcode.opcode = StoreStaticToAddressOpcode{
387 str_static.reg_index = ((first_dword >> 16) & 0xF); 388 .bit_width = (first_dword >> 24) & 0xF,
388 str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0; 389 .reg_index = (first_dword >> 16) & 0xF,
389 str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0; 390 .increment_reg = ((first_dword >> 12) & 0xF) != 0,
390 str_static.offset_reg_index = ((first_dword >> 4) & 0xF); 391 .add_offset_reg = ((first_dword >> 8) & 0xF) != 0,
391 str_static.value = 392 .offset_reg_index = (first_dword >> 4) & 0xF,
392 (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword()); 393 .value = (static_cast<u64>(GetNextDword()) << 32) | GetNextDword(),
393 opcode.opcode = str_static; 394 };
394 } break; 395 } break;
395 case CheatVmOpcodeType::PerformArithmeticStatic: { 396 case CheatVmOpcodeType::PerformArithmeticStatic: {
396 PerformArithmeticStaticOpcode perform_math_static{};
397 // 7T0RC000 VVVVVVVV 397 // 7T0RC000 VVVVVVVV
398 // Read additional words. 398 // Read additional words.
399 perform_math_static.bit_width = (first_dword >> 24) & 0xF; 399 opcode.opcode = PerformArithmeticStaticOpcode{
400 perform_math_static.reg_index = ((first_dword >> 16) & 0xF); 400 .bit_width = (first_dword >> 24) & 0xF,
401 perform_math_static.math_type = 401 .reg_index = ((first_dword >> 16) & 0xF),
402 static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF); 402 .math_type = static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF),
403 perform_math_static.value = GetNextDword(); 403 .value = GetNextDword(),
404 opcode.opcode = perform_math_static; 404 };
405 } break; 405 } break;
406 case CheatVmOpcodeType::BeginKeypressConditionalBlock: { 406 case CheatVmOpcodeType::BeginKeypressConditionalBlock: {
407 BeginKeypressConditionalOpcode begin_keypress_cond{};
408 // 8kkkkkkk 407 // 8kkkkkkk
409 // Just parse the mask. 408 // Just parse the mask.
410 begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF; 409 opcode.opcode = BeginKeypressConditionalOpcode{
411 opcode.opcode = begin_keypress_cond; 410 .key_mask = first_dword & 0x0FFFFFFF,
411 };
412 } break; 412 } break;
413 case CheatVmOpcodeType::PerformArithmeticRegister: { 413 case CheatVmOpcodeType::PerformArithmeticRegister: {
414 PerformArithmeticRegisterOpcode perform_math_reg{};
415 // 9TCRSIs0 (VVVVVVVV (VVVVVVVV)) 414 // 9TCRSIs0 (VVVVVVVV (VVVVVVVV))
416 perform_math_reg.bit_width = (first_dword >> 24) & 0xF; 415 PerformArithmeticRegisterOpcode perform_math_reg{
417 perform_math_reg.math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF); 416 .bit_width = (first_dword >> 24) & 0xF,
418 perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF); 417 .math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF),
419 perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF); 418 .dst_reg_index = (first_dword >> 16) & 0xF,
420 perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0; 419 .src_reg_1_index = (first_dword >> 12) & 0xF,
420 .src_reg_2_index = 0,
421 .has_immediate = ((first_dword >> 8) & 0xF) != 0,
422 .value = {},
423 };
421 if (perform_math_reg.has_immediate) { 424 if (perform_math_reg.has_immediate) {
422 perform_math_reg.src_reg_2_index = 0; 425 perform_math_reg.src_reg_2_index = 0;
423 perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width); 426 perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width);
@@ -427,7 +430,6 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
427 opcode.opcode = perform_math_reg; 430 opcode.opcode = perform_math_reg;
428 } break; 431 } break;
429 case CheatVmOpcodeType::StoreRegisterToAddress: { 432 case CheatVmOpcodeType::StoreRegisterToAddress: {
430 StoreRegisterToAddressOpcode str_register{};
431 // ATSRIOxa (aaaaaaaa) 433 // ATSRIOxa (aaaaaaaa)
432 // A = opcode 10 434 // A = opcode 10
433 // T = bit width 435 // T = bit width
@@ -439,20 +441,23 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
439 // Relative Address 441 // Relative Address
440 // x = offset register (for offset type 1), memory type (for offset type 3) 442 // x = offset register (for offset type 1), memory type (for offset type 3)
441 // a = relative address (for offset type 2+3) 443 // a = relative address (for offset type 2+3)
442 str_register.bit_width = (first_dword >> 24) & 0xF; 444 StoreRegisterToAddressOpcode str_register{
443 str_register.str_reg_index = ((first_dword >> 20) & 0xF); 445 .bit_width = (first_dword >> 24) & 0xF,
444 str_register.addr_reg_index = ((first_dword >> 16) & 0xF); 446 .str_reg_index = (first_dword >> 20) & 0xF,
445 str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0; 447 .addr_reg_index = (first_dword >> 16) & 0xF,
446 str_register.ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF)); 448 .increment_reg = ((first_dword >> 12) & 0xF) != 0,
447 str_register.ofs_reg_index = ((first_dword >> 4) & 0xF); 449 .ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF)),
450 .mem_type = MemoryAccessType::MainNso,
451 .ofs_reg_index = (first_dword >> 4) & 0xF,
452 .rel_address = 0,
453 };
448 switch (str_register.ofs_type) { 454 switch (str_register.ofs_type) {
449 case StoreRegisterOffsetType::None: 455 case StoreRegisterOffsetType::None:
450 case StoreRegisterOffsetType::Reg: 456 case StoreRegisterOffsetType::Reg:
451 // Nothing more to do 457 // Nothing more to do
452 break; 458 break;
453 case StoreRegisterOffsetType::Imm: 459 case StoreRegisterOffsetType::Imm:
454 str_register.rel_address = 460 str_register.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
455 ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
456 break; 461 break;
457 case StoreRegisterOffsetType::MemReg: 462 case StoreRegisterOffsetType::MemReg:
458 str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); 463 str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
@@ -460,8 +465,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
460 case StoreRegisterOffsetType::MemImm: 465 case StoreRegisterOffsetType::MemImm:
461 case StoreRegisterOffsetType::MemImmReg: 466 case StoreRegisterOffsetType::MemImmReg:
462 str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); 467 str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
463 str_register.rel_address = 468 str_register.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
464 ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
465 break; 469 break;
466 default: 470 default:
467 str_register.ofs_type = StoreRegisterOffsetType::None; 471 str_register.ofs_type = StoreRegisterOffsetType::None;
@@ -470,7 +474,6 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
470 opcode.opcode = str_register; 474 opcode.opcode = str_register;
471 } break; 475 } break;
472 case CheatVmOpcodeType::BeginRegisterConditionalBlock: { 476 case CheatVmOpcodeType::BeginRegisterConditionalBlock: {
473 BeginRegisterConditionalOpcode begin_reg_cond{};
474 // C0TcSX## 477 // C0TcSX##
475 // C0TcS0Ma aaaaaaaa 478 // C0TcS0Ma aaaaaaaa
476 // C0TcS1Mr 479 // C0TcS1Mr
@@ -492,11 +495,19 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
492 // r = offset register. 495 // r = offset register.
493 // X = other register. 496 // X = other register.
494 // V = value. 497 // V = value.
495 begin_reg_cond.bit_width = (first_dword >> 20) & 0xF; 498
496 begin_reg_cond.cond_type = 499 BeginRegisterConditionalOpcode begin_reg_cond{
497 static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF); 500 .bit_width = (first_dword >> 20) & 0xF,
498 begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF); 501 .cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF),
499 begin_reg_cond.comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF); 502 .val_reg_index = (first_dword >> 12) & 0xF,
503 .comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF),
504 .mem_type = MemoryAccessType::MainNso,
505 .addr_reg_index = 0,
506 .other_reg_index = 0,
507 .ofs_reg_index = 0,
508 .rel_address = 0,
509 .value = {},
510 };
500 511
501 switch (begin_reg_cond.comp_type) { 512 switch (begin_reg_cond.comp_type) {
502 case CompareRegisterValueType::StaticValue: 513 case CompareRegisterValueType::StaticValue:
@@ -508,26 +519,25 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
508 case CompareRegisterValueType::MemoryRelAddr: 519 case CompareRegisterValueType::MemoryRelAddr:
509 begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); 520 begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
510 begin_reg_cond.rel_address = 521 begin_reg_cond.rel_address =
511 ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); 522 (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
512 break; 523 break;
513 case CompareRegisterValueType::MemoryOfsReg: 524 case CompareRegisterValueType::MemoryOfsReg:
514 begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); 525 begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
515 begin_reg_cond.ofs_reg_index = (first_dword & 0xF); 526 begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
516 break; 527 break;
517 case CompareRegisterValueType::RegisterRelAddr: 528 case CompareRegisterValueType::RegisterRelAddr:
518 begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); 529 begin_reg_cond.addr_reg_index = (first_dword >> 4) & 0xF;
519 begin_reg_cond.rel_address = 530 begin_reg_cond.rel_address =
520 ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); 531 (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
521 break; 532 break;
522 case CompareRegisterValueType::RegisterOfsReg: 533 case CompareRegisterValueType::RegisterOfsReg:
523 begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); 534 begin_reg_cond.addr_reg_index = (first_dword >> 4) & 0xF;
524 begin_reg_cond.ofs_reg_index = (first_dword & 0xF); 535 begin_reg_cond.ofs_reg_index = first_dword & 0xF;
525 break; 536 break;
526 } 537 }
527 opcode.opcode = begin_reg_cond; 538 opcode.opcode = begin_reg_cond;
528 } break; 539 } break;
529 case CheatVmOpcodeType::SaveRestoreRegister: { 540 case CheatVmOpcodeType::SaveRestoreRegister: {
530 SaveRestoreRegisterOpcode save_restore_reg{};
531 // C10D0Sx0 541 // C10D0Sx0
532 // C1 = opcode 0xC1 542 // C1 = opcode 0xC1
533 // D = destination index. 543 // D = destination index.
@@ -535,36 +545,37 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
535 // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring 545 // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring
536 // a register. 546 // a register.
537 // NOTE: If we add more save slots later, current encoding is backwards compatible. 547 // NOTE: If we add more save slots later, current encoding is backwards compatible.
538 save_restore_reg.dst_index = (first_dword >> 16) & 0xF; 548 opcode.opcode = SaveRestoreRegisterOpcode{
539 save_restore_reg.src_index = (first_dword >> 8) & 0xF; 549 .dst_index = (first_dword >> 16) & 0xF,
540 save_restore_reg.op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF); 550 .src_index = (first_dword >> 8) & 0xF,
541 opcode.opcode = save_restore_reg; 551 .op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF),
552 };
542 } break; 553 } break;
543 case CheatVmOpcodeType::SaveRestoreRegisterMask: { 554 case CheatVmOpcodeType::SaveRestoreRegisterMask: {
544 SaveRestoreRegisterMaskOpcode save_restore_regmask{};
545 // C2x0XXXX 555 // C2x0XXXX
546 // C2 = opcode 0xC2 556 // C2 = opcode 0xC2
547 // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring. 557 // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring.
548 // X = 16-bit bitmask, bit i --> save or restore register i. 558 // X = 16-bit bitmask, bit i --> save or restore register i.
549 save_restore_regmask.op_type = 559 SaveRestoreRegisterMaskOpcode save_restore_regmask{
550 static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF); 560 .op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF),
561 .should_operate = {},
562 };
551 for (std::size_t i = 0; i < NumRegisters; i++) { 563 for (std::size_t i = 0; i < NumRegisters; i++) {
552 save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0; 564 save_restore_regmask.should_operate[i] = (first_dword & (1U << i)) != 0;
553 } 565 }
554 opcode.opcode = save_restore_regmask; 566 opcode.opcode = save_restore_regmask;
555 } break; 567 } break;
556 case CheatVmOpcodeType::ReadWriteStaticRegister: { 568 case CheatVmOpcodeType::ReadWriteStaticRegister: {
557 ReadWriteStaticRegisterOpcode rw_static_reg{};
558 // C3000XXx 569 // C3000XXx
559 // C3 = opcode 0xC3. 570 // C3 = opcode 0xC3.
560 // XX = static register index. 571 // XX = static register index.
561 // x = register index. 572 // x = register index.
562 rw_static_reg.static_idx = ((first_dword >> 4) & 0xFF); 573 opcode.opcode = ReadWriteStaticRegisterOpcode{
563 rw_static_reg.idx = (first_dword & 0xF); 574 .static_idx = (first_dword >> 4) & 0xFF,
564 opcode.opcode = rw_static_reg; 575 .idx = first_dword & 0xF,
576 };
565 } break; 577 } break;
566 case CheatVmOpcodeType::DebugLog: { 578 case CheatVmOpcodeType::DebugLog: {
567 DebugLogOpcode debug_log{};
568 // FFFTIX## 579 // FFFTIX##
569 // FFFTI0Ma aaaaaaaa 580 // FFFTI0Ma aaaaaaaa
570 // FFFTI1Mr 581 // FFFTI1Mr
@@ -583,31 +594,36 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
583 // a = relative address. 594 // a = relative address.
584 // r = offset register. 595 // r = offset register.
585 // X = value register. 596 // X = value register.
586 debug_log.bit_width = (first_dword >> 16) & 0xF; 597 DebugLogOpcode debug_log{
587 debug_log.log_id = ((first_dword >> 12) & 0xF); 598 .bit_width = (first_dword >> 16) & 0xF,
588 debug_log.val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF); 599 .log_id = (first_dword >> 12) & 0xF,
600 .val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF),
601 .mem_type = MemoryAccessType::MainNso,
602 .addr_reg_index = 0,
603 .val_reg_index = 0,
604 .ofs_reg_index = 0,
605 .rel_address = 0,
606 };
589 607
590 switch (debug_log.val_type) { 608 switch (debug_log.val_type) {
591 case DebugLogValueType::RegisterValue: 609 case DebugLogValueType::RegisterValue:
592 debug_log.val_reg_index = ((first_dword >> 4) & 0xF); 610 debug_log.val_reg_index = (first_dword >> 4) & 0xF;
593 break; 611 break;
594 case DebugLogValueType::MemoryRelAddr: 612 case DebugLogValueType::MemoryRelAddr:
595 debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); 613 debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
596 debug_log.rel_address = 614 debug_log.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
597 ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
598 break; 615 break;
599 case DebugLogValueType::MemoryOfsReg: 616 case DebugLogValueType::MemoryOfsReg:
600 debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); 617 debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
601 debug_log.ofs_reg_index = (first_dword & 0xF); 618 debug_log.ofs_reg_index = first_dword & 0xF;
602 break; 619 break;
603 case DebugLogValueType::RegisterRelAddr: 620 case DebugLogValueType::RegisterRelAddr:
604 debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); 621 debug_log.addr_reg_index = (first_dword >> 4) & 0xF;
605 debug_log.rel_address = 622 debug_log.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
606 ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
607 break; 623 break;
608 case DebugLogValueType::RegisterOfsReg: 624 case DebugLogValueType::RegisterOfsReg:
609 debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); 625 debug_log.addr_reg_index = (first_dword >> 4) & 0xF;
610 debug_log.ofs_reg_index = (first_dword & 0xF); 626 debug_log.ofs_reg_index = first_dword & 0xF;
611 break; 627 break;
612 } 628 }
613 opcode.opcode = debug_log; 629 opcode.opcode = debug_log;
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
new file mode 100644
index 000000000..56d173b5e
--- /dev/null
+++ b/src/core/network/network.cpp
@@ -0,0 +1,654 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cstring>
7#include <limits>
8#include <utility>
9#include <vector>
10
11#ifdef _WIN32
12#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname
13#include <winsock2.h>
14#elif __unix__
15#include <errno.h>
16#include <fcntl.h>
17#include <netdb.h>
18#include <netinet/in.h>
19#include <poll.h>
20#include <sys/socket.h>
21#include <unistd.h>
22#else
23#error "Unimplemented platform"
24#endif
25
26#include "common/assert.h"
27#include "common/common_types.h"
28#include "common/logging/log.h"
29#include "core/network/network.h"
30#include "core/network/sockets.h"
31
32namespace Network {
33
34namespace {
35
36#ifdef _WIN32
37
38using socklen_t = int;
39
40void Initialize() {
41 WSADATA wsa_data;
42 (void)WSAStartup(MAKEWORD(2, 2), &wsa_data);
43}
44
45void Finalize() {
46 WSACleanup();
47}
48
49constexpr IPv4Address TranslateIPv4(in_addr addr) {
50 auto& bytes = addr.S_un.S_un_b;
51 return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4};
52}
53
54sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
55 sockaddr_in result;
56
57#ifdef __unix__
58 result.sin_len = sizeof(result);
59#endif
60
61 switch (static_cast<Domain>(input.family)) {
62 case Domain::INET:
63 result.sin_family = AF_INET;
64 break;
65 default:
66 UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", static_cast<int>(input.family));
67 result.sin_family = AF_INET;
68 break;
69 }
70
71 result.sin_port = htons(input.portno);
72
73 auto& ip = result.sin_addr.S_un.S_un_b;
74 ip.s_b1 = input.ip[0];
75 ip.s_b2 = input.ip[1];
76 ip.s_b3 = input.ip[2];
77 ip.s_b4 = input.ip[3];
78
79 sockaddr addr;
80 std::memcpy(&addr, &result, sizeof(addr));
81 return addr;
82}
83
84LINGER MakeLinger(bool enable, u32 linger_value) {
85 ASSERT(linger_value <= std::numeric_limits<u_short>::max());
86
87 LINGER value;
88 value.l_onoff = enable ? 1 : 0;
89 value.l_linger = static_cast<u_short>(linger_value);
90 return value;
91}
92
93int LastError() {
94 return WSAGetLastError();
95}
96
97bool EnableNonBlock(SOCKET fd, bool enable) {
98 u_long value = enable ? 1 : 0;
99 return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR;
100}
101
102#elif __unix__ // ^ _WIN32 v __unix__
103
104using SOCKET = int;
105using WSAPOLLFD = pollfd;
106using ULONG = u64;
107
108constexpr SOCKET INVALID_SOCKET = -1;
109constexpr SOCKET SOCKET_ERROR = -1;
110
111constexpr int WSAEWOULDBLOCK = EAGAIN;
112constexpr int WSAENOTCONN = ENOTCONN;
113
114constexpr int SD_RECEIVE = SHUT_RD;
115constexpr int SD_SEND = SHUT_WR;
116constexpr int SD_BOTH = SHUT_RDWR;
117
118void Initialize() {}
119
120void Finalize() {}
121
122constexpr IPv4Address TranslateIPv4(in_addr addr) {
123 const u32 bytes = addr.s_addr;
124 return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8),
125 static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)};
126}
127
128sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
129 sockaddr_in result;
130
131 switch (static_cast<Domain>(input.family)) {
132 case Domain::INET:
133 result.sin_family = AF_INET;
134 break;
135 default:
136 UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", static_cast<int>(input.family));
137 result.sin_family = AF_INET;
138 break;
139 }
140
141 result.sin_port = htons(input.portno);
142
143 result.sin_addr.s_addr = input.ip[0] | input.ip[1] << 8 | input.ip[2] << 16 | input.ip[3] << 24;
144
145 sockaddr addr;
146 std::memcpy(&addr, &result, sizeof(addr));
147 return addr;
148}
149
150int WSAPoll(WSAPOLLFD* fds, ULONG nfds, int timeout) {
151 return poll(fds, nfds, timeout);
152}
153
154int closesocket(SOCKET fd) {
155 return close(fd);
156}
157
158linger MakeLinger(bool enable, u32 linger_value) {
159 linger value;
160 value.l_onoff = enable ? 1 : 0;
161 value.l_linger = linger_value;
162 return value;
163}
164
165int LastError() {
166 return errno;
167}
168
169bool EnableNonBlock(int fd, bool enable) {
170 int flags = fcntl(fd, F_GETFD);
171 if (flags == -1) {
172 return false;
173 }
174 if (enable) {
175 flags |= O_NONBLOCK;
176 } else {
177 flags &= ~O_NONBLOCK;
178 }
179 return fcntl(fd, F_SETFD, flags) == 0;
180}
181
182#endif
183
184int TranslateDomain(Domain domain) {
185 switch (domain) {
186 case Domain::INET:
187 return AF_INET;
188 default:
189 UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain));
190 return 0;
191 }
192}
193
194int TranslateType(Type type) {
195 switch (type) {
196 case Type::STREAM:
197 return SOCK_STREAM;
198 case Type::DGRAM:
199 return SOCK_DGRAM;
200 default:
201 UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type));
202 return 0;
203 }
204}
205
206int TranslateProtocol(Protocol protocol) {
207 switch (protocol) {
208 case Protocol::TCP:
209 return IPPROTO_TCP;
210 case Protocol::UDP:
211 return IPPROTO_UDP;
212 default:
213 UNIMPLEMENTED_MSG("Unimplemented protocol={}", static_cast<int>(protocol));
214 return 0;
215 }
216}
217
218SockAddrIn TranslateToSockAddrIn(sockaddr input_) {
219 sockaddr_in input;
220 std::memcpy(&input, &input_, sizeof(input));
221
222 SockAddrIn result;
223
224 switch (input.sin_family) {
225 case AF_INET:
226 result.family = Domain::INET;
227 break;
228 default:
229 UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.sin_family);
230 result.family = Domain::INET;
231 break;
232 }
233
234 result.portno = ntohs(input.sin_port);
235
236 result.ip = TranslateIPv4(input.sin_addr);
237
238 return result;
239}
240
241u16 TranslatePollEvents(u16 events) {
242 u16 result = 0;
243
244 if (events & POLL_IN) {
245 events &= ~POLL_IN;
246 result |= POLLIN;
247 }
248 if (events & POLL_PRI) {
249 events &= ~POLL_PRI;
250#ifdef _WIN32
251 LOG_WARNING(Service, "Winsock doesn't support POLLPRI");
252#else
253 result |= POLL_PRI;
254#endif
255 }
256 if (events & POLL_OUT) {
257 events &= ~POLL_OUT;
258 result |= POLLOUT;
259 }
260
261 UNIMPLEMENTED_IF_MSG(events != 0, "Unhandled guest events=0x{:x}", events);
262
263 return result;
264}
265
266u16 TranslatePollRevents(u16 revents) {
267 u16 result = 0;
268 const auto translate = [&result, &revents](int host, unsigned guest) {
269 if (revents & host) {
270 revents &= ~host;
271 result |= guest;
272 }
273 };
274
275 translate(POLLIN, POLL_IN);
276 translate(POLLPRI, POLL_PRI);
277 translate(POLLOUT, POLL_OUT);
278 translate(POLLERR, POLL_ERR);
279 translate(POLLHUP, POLL_HUP);
280
281 UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents);
282
283 return result;
284}
285
286template <typename T>
287Errno SetSockOpt(SOCKET fd, int option, T value) {
288 const int result =
289 setsockopt(fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value));
290 if (result != SOCKET_ERROR) {
291 return Errno::SUCCESS;
292 }
293 const int ec = LastError();
294 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
295 return Errno::SUCCESS;
296}
297
298} // Anonymous namespace
299
300NetworkInstance::NetworkInstance() {
301 Initialize();
302}
303
304NetworkInstance::~NetworkInstance() {
305 Finalize();
306}
307
308std::pair<IPv4Address, Errno> GetHostIPv4Address() {
309 std::array<char, 256> name{};
310 if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) {
311 UNIMPLEMENTED_MSG("Unhandled gethostname error");
312 return {IPv4Address{}, Errno::SUCCESS};
313 }
314
315 hostent* const ent = gethostbyname(name.data());
316 if (!ent) {
317 UNIMPLEMENTED_MSG("Unhandled gethostbyname error");
318 return {IPv4Address{}, Errno::SUCCESS};
319 }
320 if (ent->h_addr_list == nullptr) {
321 UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list");
322 return {IPv4Address{}, Errno::SUCCESS};
323 }
324 if (ent->h_length != sizeof(in_addr)) {
325 UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length);
326 }
327
328 in_addr addr;
329 std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr));
330 return {TranslateIPv4(addr), Errno::SUCCESS};
331}
332
333std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
334 const size_t num = pollfds.size();
335
336 std::vector<WSAPOLLFD> host_pollfds(pollfds.size());
337 std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) {
338 WSAPOLLFD result;
339 result.fd = fd.socket->fd;
340 result.events = TranslatePollEvents(fd.events);
341 result.revents = 0;
342 return result;
343 });
344
345 const int result = WSAPoll(host_pollfds.data(), static_cast<ULONG>(num), timeout);
346 if (result == 0) {
347 ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(),
348 [](WSAPOLLFD fd) { return fd.revents == 0; }));
349 return {0, Errno::SUCCESS};
350 }
351
352 for (size_t i = 0; i < num; ++i) {
353 pollfds[i].revents = TranslatePollRevents(host_pollfds[i].revents);
354 }
355
356 if (result > 0) {
357 return {result, Errno::SUCCESS};
358 }
359
360 ASSERT(result == SOCKET_ERROR);
361
362 const int ec = LastError();
363 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
364 return {-1, Errno::SUCCESS};
365}
366
367Socket::~Socket() {
368 if (fd == INVALID_SOCKET) {
369 return;
370 }
371 (void)closesocket(fd);
372 fd = INVALID_SOCKET;
373}
374
375Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {}
376
377Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
378 fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol));
379 if (fd != INVALID_SOCKET) {
380 return Errno::SUCCESS;
381 }
382
383 const int ec = LastError();
384 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
385 return Errno::SUCCESS;
386}
387
388std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
389 sockaddr addr;
390 socklen_t addrlen = sizeof(addr);
391 const SOCKET new_socket = accept(fd, &addr, &addrlen);
392
393 if (new_socket == INVALID_SOCKET) {
394 const int ec = LastError();
395 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
396 return {AcceptResult{}, Errno::SUCCESS};
397 }
398
399 AcceptResult result;
400 result.socket = std::make_unique<Socket>();
401 result.socket->fd = new_socket;
402
403 ASSERT(addrlen == sizeof(sockaddr_in));
404 result.sockaddr_in = TranslateToSockAddrIn(addr);
405
406 return {std::move(result), Errno::SUCCESS};
407}
408
409Errno Socket::Connect(SockAddrIn addr_in) {
410 const sockaddr host_addr_in = TranslateFromSockAddrIn(addr_in);
411 if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != INVALID_SOCKET) {
412 return Errno::SUCCESS;
413 }
414
415 switch (const int ec = LastError()) {
416 case WSAEWOULDBLOCK:
417 LOG_DEBUG(Service, "EAGAIN generated");
418 return Errno::AGAIN;
419 default:
420 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
421 return Errno::SUCCESS;
422 }
423}
424
425std::pair<SockAddrIn, Errno> Socket::GetPeerName() {
426 sockaddr addr;
427 socklen_t addrlen = sizeof(addr);
428 if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) {
429 const int ec = LastError();
430 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
431 return {SockAddrIn{}, Errno::SUCCESS};
432 }
433
434 ASSERT(addrlen == sizeof(sockaddr_in));
435 return {TranslateToSockAddrIn(addr), Errno::SUCCESS};
436}
437
438std::pair<SockAddrIn, Errno> Socket::GetSockName() {
439 sockaddr addr;
440 socklen_t addrlen = sizeof(addr);
441 if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) {
442 const int ec = LastError();
443 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
444 return {SockAddrIn{}, Errno::SUCCESS};
445 }
446
447 ASSERT(addrlen == sizeof(sockaddr_in));
448 return {TranslateToSockAddrIn(addr), Errno::SUCCESS};
449}
450
451Errno Socket::Bind(SockAddrIn addr) {
452 const sockaddr addr_in = TranslateFromSockAddrIn(addr);
453 if (bind(fd, &addr_in, sizeof(addr_in)) != SOCKET_ERROR) {
454 return Errno::SUCCESS;
455 }
456
457 const int ec = LastError();
458 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
459 return Errno::SUCCESS;
460}
461
462Errno Socket::Listen(s32 backlog) {
463 if (listen(fd, backlog) != SOCKET_ERROR) {
464 return Errno::SUCCESS;
465 }
466
467 const int ec = LastError();
468 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
469 return Errno::SUCCESS;
470}
471
472Errno Socket::Shutdown(ShutdownHow how) {
473 int host_how = 0;
474 switch (how) {
475 case ShutdownHow::RD:
476 host_how = SD_RECEIVE;
477 break;
478 case ShutdownHow::WR:
479 host_how = SD_SEND;
480 break;
481 case ShutdownHow::RDWR:
482 host_how = SD_BOTH;
483 break;
484 default:
485 UNIMPLEMENTED_MSG("Unimplemented flag how={}", static_cast<int>(how));
486 return Errno::SUCCESS;
487 }
488 if (shutdown(fd, host_how) != SOCKET_ERROR) {
489 return Errno::SUCCESS;
490 }
491
492 switch (const int ec = LastError()) {
493 case WSAENOTCONN:
494 LOG_ERROR(Service, "ENOTCONN generated");
495 return Errno::NOTCONN;
496 default:
497 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
498 return Errno::SUCCESS;
499 }
500}
501
502std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) {
503 ASSERT(flags == 0);
504 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
505
506 const int result =
507 recv(fd, reinterpret_cast<char*>(message.data()), static_cast<int>(message.size()), 0);
508 if (result != SOCKET_ERROR) {
509 return {result, Errno::SUCCESS};
510 }
511
512 switch (const int ec = LastError()) {
513 case WSAEWOULDBLOCK:
514 LOG_DEBUG(Service, "EAGAIN generated");
515 return {-1, Errno::AGAIN};
516 case WSAENOTCONN:
517 LOG_ERROR(Service, "ENOTCONN generated");
518 return {-1, Errno::NOTCONN};
519 default:
520 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
521 return {0, Errno::SUCCESS};
522 }
523}
524
525std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) {
526 ASSERT(flags == 0);
527 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
528
529 sockaddr addr_in{};
530 socklen_t addrlen = sizeof(addr_in);
531 socklen_t* const p_addrlen = addr ? &addrlen : nullptr;
532 sockaddr* const p_addr_in = addr ? &addr_in : nullptr;
533
534 const int result = recvfrom(fd, reinterpret_cast<char*>(message.data()),
535 static_cast<int>(message.size()), 0, p_addr_in, p_addrlen);
536 if (result != SOCKET_ERROR) {
537 if (addr) {
538 ASSERT(addrlen == sizeof(addr_in));
539 *addr = TranslateToSockAddrIn(addr_in);
540 }
541 return {result, Errno::SUCCESS};
542 }
543
544 switch (const int ec = LastError()) {
545 case WSAEWOULDBLOCK:
546 LOG_DEBUG(Service, "EAGAIN generated");
547 return {-1, Errno::AGAIN};
548 case WSAENOTCONN:
549 LOG_ERROR(Service, "ENOTCONN generated");
550 return {-1, Errno::NOTCONN};
551 default:
552 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
553 return {-1, Errno::SUCCESS};
554 }
555}
556
557std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
558 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
559 ASSERT(flags == 0);
560
561 const int result = send(fd, reinterpret_cast<const char*>(message.data()),
562 static_cast<int>(message.size()), 0);
563 if (result != SOCKET_ERROR) {
564 return {result, Errno::SUCCESS};
565 }
566
567 const int ec = LastError();
568 switch (ec) {
569 case WSAEWOULDBLOCK:
570 LOG_DEBUG(Service, "EAGAIN generated");
571 return {-1, Errno::AGAIN};
572 case WSAENOTCONN:
573 LOG_ERROR(Service, "ENOTCONN generated");
574 return {-1, Errno::NOTCONN};
575 default:
576 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
577 return {-1, Errno::SUCCESS};
578 }
579}
580
581std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
582 const SockAddrIn* addr) {
583 ASSERT(flags == 0);
584
585 const sockaddr* to = nullptr;
586 const int tolen = addr ? 0 : sizeof(sockaddr);
587 sockaddr host_addr_in;
588
589 if (addr) {
590 host_addr_in = TranslateFromSockAddrIn(*addr);
591 to = &host_addr_in;
592 }
593
594 const int result = sendto(fd, reinterpret_cast<const char*>(message.data()),
595 static_cast<int>(message.size()), 0, to, tolen);
596 if (result != SOCKET_ERROR) {
597 return {result, Errno::SUCCESS};
598 }
599
600 const int ec = LastError();
601 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
602 return {-1, Errno::SUCCESS};
603}
604
605Errno Socket::Close() {
606 [[maybe_unused]] const int result = closesocket(fd);
607 ASSERT(result == 0);
608 fd = INVALID_SOCKET;
609
610 return Errno::SUCCESS;
611}
612
613Errno Socket::SetLinger(bool enable, u32 linger) {
614 return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger));
615}
616
617Errno Socket::SetReuseAddr(bool enable) {
618 return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
619}
620
621Errno Socket::SetBroadcast(bool enable) {
622 return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
623}
624
625Errno Socket::SetSndBuf(u32 value) {
626 return SetSockOpt(fd, SO_SNDBUF, value);
627}
628
629Errno Socket::SetRcvBuf(u32 value) {
630 return SetSockOpt(fd, SO_RCVBUF, value);
631}
632
633Errno Socket::SetSndTimeo(u32 value) {
634 return SetSockOpt(fd, SO_SNDTIMEO, value);
635}
636
637Errno Socket::SetRcvTimeo(u32 value) {
638 return SetSockOpt(fd, SO_RCVTIMEO, value);
639}
640
641Errno Socket::SetNonBlock(bool enable) {
642 if (EnableNonBlock(fd, enable)) {
643 return Errno::SUCCESS;
644 }
645 const int ec = LastError();
646 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
647 return Errno::SUCCESS;
648}
649
650bool Socket::IsOpened() const {
651 return fd != INVALID_SOCKET;
652}
653
654} // namespace Network
diff --git a/src/core/network/network.h b/src/core/network/network.h
new file mode 100644
index 000000000..0622e4593
--- /dev/null
+++ b/src/core/network/network.h
@@ -0,0 +1,87 @@
1// Copyright 2020 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 <array>
8#include <utility>
9
10#include "common/common_types.h"
11
12namespace Network {
13
14class Socket;
15
16/// Error code for network functions
17enum class Errno {
18 SUCCESS,
19 BADF,
20 INVAL,
21 MFILE,
22 NOTCONN,
23 AGAIN,
24};
25
26/// Address families
27enum class Domain {
28 INET, ///< Address family for IPv4
29};
30
31/// Socket types
32enum class Type {
33 STREAM,
34 DGRAM,
35 RAW,
36 SEQPACKET,
37};
38
39/// Protocol values for sockets
40enum class Protocol {
41 ICMP,
42 TCP,
43 UDP,
44};
45
46/// Shutdown mode
47enum class ShutdownHow {
48 RD,
49 WR,
50 RDWR,
51};
52
53/// Array of IPv4 address
54using IPv4Address = std::array<u8, 4>;
55
56/// Cross-platform sockaddr structure
57struct SockAddrIn {
58 Domain family;
59 IPv4Address ip;
60 u16 portno;
61};
62
63/// Cross-platform poll fd structure
64struct PollFD {
65 Socket* socket;
66 u16 events;
67 u16 revents;
68};
69
70constexpr u16 POLL_IN = 1 << 0;
71constexpr u16 POLL_PRI = 1 << 1;
72constexpr u16 POLL_OUT = 1 << 2;
73constexpr u16 POLL_ERR = 1 << 3;
74constexpr u16 POLL_HUP = 1 << 4;
75constexpr u16 POLL_NVAL = 1 << 5;
76
77class NetworkInstance {
78public:
79 explicit NetworkInstance();
80 ~NetworkInstance();
81};
82
83/// @brief Returns host's IPv4 address
84/// @return Pair of an array of human ordered IPv4 address (e.g. 192.168.0.1) and an error code
85std::pair<IPv4Address, Errno> GetHostIPv4Address();
86
87} // namespace Network
diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h
new file mode 100644
index 000000000..7bdff0fe4
--- /dev/null
+++ b/src/core/network/sockets.h
@@ -0,0 +1,94 @@
1// Copyright 2020 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 <memory>
8#include <utility>
9
10#if defined(_WIN32)
11#include <winsock.h>
12#elif !defined(__unix__)
13#error "Platform not implemented"
14#endif
15
16#include "common/common_types.h"
17#include "core/network/network.h"
18
19// TODO: C++20 Replace std::vector usages with std::span
20
21namespace Network {
22
23class Socket {
24public:
25 struct AcceptResult {
26 std::unique_ptr<Socket> socket;
27 SockAddrIn sockaddr_in;
28 };
29
30 explicit Socket() = default;
31 ~Socket();
32
33 Socket(const Socket&) = delete;
34 Socket& operator=(const Socket&) = delete;
35
36 Socket(Socket&& rhs) noexcept;
37
38 // Avoid closing sockets implicitly
39 Socket& operator=(Socket&&) noexcept = delete;
40
41 Errno Initialize(Domain domain, Type type, Protocol protocol);
42
43 Errno Close();
44
45 std::pair<AcceptResult, Errno> Accept();
46
47 Errno Connect(SockAddrIn addr_in);
48
49 std::pair<SockAddrIn, Errno> GetPeerName();
50
51 std::pair<SockAddrIn, Errno> GetSockName();
52
53 Errno Bind(SockAddrIn addr);
54
55 Errno Listen(s32 backlog);
56
57 Errno Shutdown(ShutdownHow how);
58
59 std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message);
60
61 std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr);
62
63 std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags);
64
65 std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr);
66
67 Errno SetLinger(bool enable, u32 linger);
68
69 Errno SetReuseAddr(bool enable);
70
71 Errno SetBroadcast(bool enable);
72
73 Errno SetSndBuf(u32 value);
74
75 Errno SetRcvBuf(u32 value);
76
77 Errno SetSndTimeo(u32 value);
78
79 Errno SetRcvTimeo(u32 value);
80
81 Errno SetNonBlock(bool enable);
82
83 bool IsOpened() const;
84
85#if defined(_WIN32)
86 SOCKET fd = INVALID_SOCKET;
87#elif defined(__unix__)
88 int fd = -1;
89#endif
90};
91
92std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
93
94} // namespace Network
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 29339ead7..b93396a80 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -38,11 +38,11 @@ PerfStats::~PerfStats() {
38 std::ostringstream stream; 38 std::ostringstream stream;
39 std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index, 39 std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index,
40 std::ostream_iterator<double>(stream, "\n")); 40 std::ostream_iterator<double>(stream, "\n"));
41 const std::string& path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 41 const std::string& path = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
42 // %F Date format expanded is "%Y-%m-%d" 42 // %F Date format expanded is "%Y-%m-%d"
43 const std::string filename = 43 const std::string filename =
44 fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id); 44 fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id);
45 FileUtil::IOFile file(filename, "w"); 45 Common::FS::IOFile file(filename, "w");
46 file.WriteString(stream.str()); 46 file.WriteString(stream.str());
47} 47}
48 48
@@ -74,15 +74,16 @@ void PerfStats::EndGameFrame() {
74 game_frames += 1; 74 game_frames += 1;
75} 75}
76 76
77double PerfStats::GetMeanFrametime() { 77double PerfStats::GetMeanFrametime() const {
78 std::lock_guard lock{object_mutex}; 78 std::lock_guard lock{object_mutex};
79 79
80 if (current_index <= IgnoreFrames) { 80 if (current_index <= IgnoreFrames) {
81 return 0; 81 return 0;
82 } 82 }
83
83 const double sum = std::accumulate(perf_history.begin() + IgnoreFrames, 84 const double sum = std::accumulate(perf_history.begin() + IgnoreFrames,
84 perf_history.begin() + current_index, 0.0); 85 perf_history.begin() + current_index, 0.0);
85 return sum / (current_index - IgnoreFrames); 86 return sum / static_cast<double>(current_index - IgnoreFrames);
86} 87}
87 88
88PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { 89PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
@@ -94,12 +95,13 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
94 95
95 const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval; 96 const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval;
96 97
97 PerfStatsResults results{}; 98 const PerfStatsResults results{
98 results.system_fps = static_cast<double>(system_frames) / interval; 99 .system_fps = static_cast<double>(system_frames) / interval,
99 results.game_fps = static_cast<double>(game_frames) / interval; 100 .game_fps = static_cast<double>(game_frames) / interval,
100 results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() / 101 .frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() /
101 static_cast<double>(system_frames); 102 static_cast<double>(system_frames),
102 results.emulation_speed = system_us_per_second.count() / 1'000'000.0; 103 .emulation_speed = system_us_per_second.count() / 1'000'000.0,
104 };
103 105
104 // Reset counters 106 // Reset counters
105 reset_point = now; 107 reset_point = now;
@@ -111,7 +113,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
111 return results; 113 return results;
112} 114}
113 115
114double PerfStats::GetLastFrameTimeScale() { 116double PerfStats::GetLastFrameTimeScale() const {
115 std::lock_guard lock{object_mutex}; 117 std::lock_guard lock{object_mutex};
116 118
117 constexpr double FRAME_LENGTH = 1.0 / 60; 119 constexpr double FRAME_LENGTH = 1.0 / 60;
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index d9a64f072..69256b960 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -30,7 +30,6 @@ struct PerfStatsResults {
30class PerfStats { 30class PerfStats {
31public: 31public:
32 explicit PerfStats(u64 title_id); 32 explicit PerfStats(u64 title_id);
33
34 ~PerfStats(); 33 ~PerfStats();
35 34
36 using Clock = std::chrono::high_resolution_clock; 35 using Clock = std::chrono::high_resolution_clock;
@@ -42,18 +41,18 @@ public:
42 PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us); 41 PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us);
43 42
44 /** 43 /**
45 * Returns the Arthimetic Mean of all frametime values stored in the performance history. 44 * Returns the arithmetic mean of all frametime values stored in the performance history.
46 */ 45 */
47 double GetMeanFrametime(); 46 double GetMeanFrametime() const;
48 47
49 /** 48 /**
50 * Gets the ratio between walltime and the emulated time of the previous system frame. This is 49 * Gets the ratio between walltime and the emulated time of the previous system frame. This is
51 * useful for scaling inputs or outputs moving between the two time domains. 50 * useful for scaling inputs or outputs moving between the two time domains.
52 */ 51 */
53 double GetLastFrameTimeScale(); 52 double GetLastFrameTimeScale() const;
54 53
55private: 54private:
56 std::mutex object_mutex{}; 55 mutable std::mutex object_mutex;
57 56
58 /// Title ID for the game that is running. 0 if there is no game running yet 57 /// Title ID for the game that is running. 0 if there is no game running yet
59 u64 title_id{0}; 58 u64 title_id{0};
@@ -61,7 +60,7 @@ private:
61 std::size_t current_index{0}; 60 std::size_t current_index{0};
62 /// Stores an hour of historical frametime data useful for processing and tracking performance 61 /// Stores an hour of historical frametime data useful for processing and tracking performance
63 /// regressions with code changes. 62 /// regressions with code changes.
64 std::array<double, 216000> perf_history = {}; 63 std::array<double, 216000> perf_history{};
65 64
66 /// Point when the cumulative counters were reset 65 /// Point when the cumulative counters were reset
67 Clock::time_point reset_point = Clock::now(); 66 Clock::time_point reset_point = Clock::now();
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index 76cfa5a17..0becdf642 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -28,8 +28,9 @@
28namespace { 28namespace {
29 29
30std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) { 30std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) {
31 return fmt::format("{}{}/{:016X}_{}.json", FileUtil::GetUserPath(FileUtil::UserPath::LogDir), 31 return fmt::format("{}{}/{:016X}_{}.json",
32 type, title_id, timestamp); 32 Common::FS::GetUserPath(Common::FS::UserPath::LogDir), type, title_id,
33 timestamp);
33} 34}
34 35
35std::string GetTimestamp() { 36std::string GetTimestamp() {
@@ -40,13 +41,13 @@ std::string GetTimestamp() {
40using namespace nlohmann; 41using namespace nlohmann;
41 42
42void SaveToFile(json json, const std::string& filename) { 43void SaveToFile(json json, const std::string& filename) {
43 if (!FileUtil::CreateFullPath(filename)) { 44 if (!Common::FS::CreateFullPath(filename)) {
44 LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename); 45 LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename);
45 return; 46 return;
46 } 47 }
47 48
48 std::ofstream file( 49 std::ofstream file(
49 FileUtil::SanitizePath(filename, FileUtil::DirectorySeparator::PlatformDefault)); 50 Common::FS::SanitizePath(filename, Common::FS::DirectorySeparator::PlatformDefault));
50 file << std::setw(4) << json << std::endl; 51 file << std::setw(4) << json << std::endl;
51} 52}
52 53
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index e8a6f2a6e..28d3f9099 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -13,56 +13,6 @@
13 13
14namespace Settings { 14namespace Settings {
15 15
16namespace NativeButton {
17const std::array<const char*, NumButtons> mapping = {{
18 "button_a",
19 "button_b",
20 "button_x",
21 "button_y",
22 "button_lstick",
23 "button_rstick",
24 "button_l",
25 "button_r",
26 "button_zl",
27 "button_zr",
28 "button_plus",
29 "button_minus",
30 "button_dleft",
31 "button_dup",
32 "button_dright",
33 "button_ddown",
34 "button_lstick_left",
35 "button_lstick_up",
36 "button_lstick_right",
37 "button_lstick_down",
38 "button_rstick_left",
39 "button_rstick_up",
40 "button_rstick_right",
41 "button_rstick_down",
42 "button_sl",
43 "button_sr",
44 "button_home",
45 "button_screenshot",
46}};
47}
48
49namespace NativeAnalog {
50const std::array<const char*, NumAnalogs> mapping = {{
51 "lstick",
52 "rstick",
53}};
54}
55
56namespace NativeMouseButton {
57const std::array<const char*, NumMouseButtons> mapping = {{
58 "left",
59 "right",
60 "middle",
61 "forward",
62 "back",
63}};
64}
65
66Values values = {}; 16Values values = {};
67bool configuring_global = true; 17bool configuring_global = true;
68 18
@@ -115,13 +65,14 @@ void LogSettings() {
115 values.use_asynchronous_gpu_emulation.GetValue()); 65 values.use_asynchronous_gpu_emulation.GetValue());
116 log_setting("Renderer_UseVsync", values.use_vsync.GetValue()); 66 log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
117 log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue()); 67 log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue());
68 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
118 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); 69 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
119 log_setting("Audio_OutputEngine", values.sink_id); 70 log_setting("Audio_OutputEngine", values.sink_id);
120 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); 71 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
121 log_setting("Audio_OutputDevice", values.audio_device_id); 72 log_setting("Audio_OutputDevice", values.audio_device_id);
122 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd); 73 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd);
123 log_setting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); 74 log_setting("DataStorage_NandDir", Common::FS::GetUserPath(Common::FS::UserPath::NANDDir));
124 log_setting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)); 75 log_setting("DataStorage_SdmcDir", Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir));
125 log_setting("Debugging_UseGdbstub", values.use_gdbstub); 76 log_setting("Debugging_UseGdbstub", values.use_gdbstub);
126 log_setting("Debugging_GdbstubPort", values.gdbstub_port); 77 log_setting("Debugging_GdbstubPort", values.gdbstub_port);
127 log_setting("Debugging_ProgramArgs", values.program_args); 78 log_setting("Debugging_ProgramArgs", values.program_args);
@@ -170,8 +121,8 @@ void RestoreGlobalState() {
170 values.use_asynchronous_gpu_emulation.SetGlobal(true); 121 values.use_asynchronous_gpu_emulation.SetGlobal(true);
171 values.use_vsync.SetGlobal(true); 122 values.use_vsync.SetGlobal(true);
172 values.use_assembly_shaders.SetGlobal(true); 123 values.use_assembly_shaders.SetGlobal(true);
124 values.use_asynchronous_shaders.SetGlobal(true);
173 values.use_fast_gpu_time.SetGlobal(true); 125 values.use_fast_gpu_time.SetGlobal(true);
174 values.force_30fps_mode.SetGlobal(true);
175 values.bg_red.SetGlobal(true); 126 values.bg_red.SetGlobal(true);
176 values.bg_green.SetGlobal(true); 127 values.bg_green.SetGlobal(true);
177 values.bg_blue.SetGlobal(true); 128 values.bg_blue.SetGlobal(true);
diff --git a/src/core/settings.h b/src/core/settings.h
index a64debd25..9834f44bb 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -12,340 +12,10 @@
12#include <string> 12#include <string>
13#include <vector> 13#include <vector>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "input_common/settings.h"
15 16
16namespace Settings { 17namespace Settings {
17 18
18namespace NativeButton {
19enum Values {
20 A,
21 B,
22 X,
23 Y,
24 LStick,
25 RStick,
26 L,
27 R,
28 ZL,
29 ZR,
30 Plus,
31 Minus,
32
33 DLeft,
34 DUp,
35 DRight,
36 DDown,
37
38 LStick_Left,
39 LStick_Up,
40 LStick_Right,
41 LStick_Down,
42
43 RStick_Left,
44 RStick_Up,
45 RStick_Right,
46 RStick_Down,
47
48 SL,
49 SR,
50
51 Home,
52 Screenshot,
53
54 NumButtons,
55};
56
57constexpr int BUTTON_HID_BEGIN = A;
58constexpr int BUTTON_NS_BEGIN = Home;
59
60constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
61constexpr int BUTTON_NS_END = NumButtons;
62
63constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
64constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
65
66extern const std::array<const char*, NumButtons> mapping;
67
68} // namespace NativeButton
69
70namespace NativeAnalog {
71enum Values {
72 LStick,
73 RStick,
74
75 NumAnalogs,
76};
77
78constexpr int STICK_HID_BEGIN = LStick;
79constexpr int STICK_HID_END = NumAnalogs;
80constexpr int NUM_STICKS_HID = NumAnalogs;
81
82extern const std::array<const char*, NumAnalogs> mapping;
83} // namespace NativeAnalog
84
85namespace NativeMouseButton {
86enum Values {
87 Left,
88 Right,
89 Middle,
90 Forward,
91 Back,
92
93 NumMouseButtons,
94};
95
96constexpr int MOUSE_HID_BEGIN = Left;
97constexpr int MOUSE_HID_END = NumMouseButtons;
98constexpr int NUM_MOUSE_HID = NumMouseButtons;
99
100extern const std::array<const char*, NumMouseButtons> mapping;
101} // namespace NativeMouseButton
102
103namespace NativeKeyboard {
104enum Keys {
105 None,
106 Error,
107
108 A = 4,
109 B,
110 C,
111 D,
112 E,
113 F,
114 G,
115 H,
116 I,
117 J,
118 K,
119 L,
120 M,
121 N,
122 O,
123 P,
124 Q,
125 R,
126 S,
127 T,
128 U,
129 V,
130 W,
131 X,
132 Y,
133 Z,
134 N1,
135 N2,
136 N3,
137 N4,
138 N5,
139 N6,
140 N7,
141 N8,
142 N9,
143 N0,
144 Enter,
145 Escape,
146 Backspace,
147 Tab,
148 Space,
149 Minus,
150 Equal,
151 LeftBrace,
152 RightBrace,
153 Backslash,
154 Tilde,
155 Semicolon,
156 Apostrophe,
157 Grave,
158 Comma,
159 Dot,
160 Slash,
161 CapsLockKey,
162
163 F1,
164 F2,
165 F3,
166 F4,
167 F5,
168 F6,
169 F7,
170 F8,
171 F9,
172 F10,
173 F11,
174 F12,
175
176 SystemRequest,
177 ScrollLockKey,
178 Pause,
179 Insert,
180 Home,
181 PageUp,
182 Delete,
183 End,
184 PageDown,
185 Right,
186 Left,
187 Down,
188 Up,
189
190 NumLockKey,
191 KPSlash,
192 KPAsterisk,
193 KPMinus,
194 KPPlus,
195 KPEnter,
196 KP1,
197 KP2,
198 KP3,
199 KP4,
200 KP5,
201 KP6,
202 KP7,
203 KP8,
204 KP9,
205 KP0,
206 KPDot,
207
208 Key102,
209 Compose,
210 Power,
211 KPEqual,
212
213 F13,
214 F14,
215 F15,
216 F16,
217 F17,
218 F18,
219 F19,
220 F20,
221 F21,
222 F22,
223 F23,
224 F24,
225
226 Open,
227 Help,
228 Properties,
229 Front,
230 Stop,
231 Repeat,
232 Undo,
233 Cut,
234 Copy,
235 Paste,
236 Find,
237 Mute,
238 VolumeUp,
239 VolumeDown,
240 CapsLockActive,
241 NumLockActive,
242 ScrollLockActive,
243 KPComma,
244
245 KPLeftParenthesis,
246 KPRightParenthesis,
247
248 LeftControlKey = 0xE0,
249 LeftShiftKey,
250 LeftAltKey,
251 LeftMetaKey,
252 RightControlKey,
253 RightShiftKey,
254 RightAltKey,
255 RightMetaKey,
256
257 MediaPlayPause,
258 MediaStopCD,
259 MediaPrevious,
260 MediaNext,
261 MediaEject,
262 MediaVolumeUp,
263 MediaVolumeDown,
264 MediaMute,
265 MediaWebsite,
266 MediaBack,
267 MediaForward,
268 MediaStop,
269 MediaFind,
270 MediaScrollUp,
271 MediaScrollDown,
272 MediaEdit,
273 MediaSleep,
274 MediaCoffee,
275 MediaRefresh,
276 MediaCalculator,
277
278 NumKeyboardKeys,
279};
280
281static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
282
283enum Modifiers {
284 LeftControl,
285 LeftShift,
286 LeftAlt,
287 LeftMeta,
288 RightControl,
289 RightShift,
290 RightAlt,
291 RightMeta,
292 CapsLock,
293 ScrollLock,
294 NumLock,
295
296 NumKeyboardMods,
297};
298
299constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
300constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
301constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
302
303constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
304constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
305constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
306
307} // namespace NativeKeyboard
308
309using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
310using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
311using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
312using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
313using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
314
315constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
316constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
317constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
318constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
319
320enum class ControllerType {
321 ProController,
322 DualJoycon,
323 RightJoycon,
324 LeftJoycon,
325};
326
327struct PlayerInput {
328 bool connected;
329 ControllerType type;
330 ButtonsRaw buttons;
331 AnalogsRaw analogs;
332
333 u32 body_color_right;
334 u32 button_color_right;
335 u32 body_color_left;
336 u32 button_color_left;
337};
338
339struct TouchscreenInput {
340 bool enabled;
341 std::string device;
342
343 u32 finger;
344 u32 diameter_x;
345 u32 diameter_y;
346 u32 rotation_angle;
347};
348
349enum class RendererBackend { 19enum class RendererBackend {
350 OpenGL = 0, 20 OpenGL = 0,
351 Vulkan = 1, 21 Vulkan = 1,
@@ -359,7 +29,8 @@ enum class GPUAccuracy : u32 {
359 29
360enum class CPUAccuracy { 30enum class CPUAccuracy {
361 Accurate = 0, 31 Accurate = 0,
362 DebugMode = 1, 32 Unsafe = 1,
33 DebugMode = 2,
363}; 34};
364 35
365extern bool configuring_global; 36extern bool configuring_global;
@@ -396,6 +67,11 @@ private:
396 Type local{}; 67 Type local{};
397}; 68};
398 69
70struct TouchFromButtonMap {
71 std::string name;
72 std::vector<std::string> buttons;
73};
74
399struct Values { 75struct Values {
400 // Audio 76 // Audio
401 std::string audio_device_id; 77 std::string audio_device_id;
@@ -419,6 +95,9 @@ struct Values {
419 bool cpuopt_misc_ir; 95 bool cpuopt_misc_ir;
420 bool cpuopt_reduce_misalign_checks; 96 bool cpuopt_reduce_misalign_checks;
421 97
98 bool cpuopt_unsafe_unfuse_fma;
99 bool cpuopt_unsafe_reduce_fp_error;
100
422 // Renderer 101 // Renderer
423 Setting<RendererBackend> renderer_backend; 102 Setting<RendererBackend> renderer_backend;
424 bool renderer_debug; 103 bool renderer_debug;
@@ -434,7 +113,7 @@ struct Values {
434 Setting<bool> use_asynchronous_gpu_emulation; 113 Setting<bool> use_asynchronous_gpu_emulation;
435 Setting<bool> use_vsync; 114 Setting<bool> use_vsync;
436 Setting<bool> use_assembly_shaders; 115 Setting<bool> use_assembly_shaders;
437 Setting<bool> force_30fps_mode; 116 Setting<bool> use_asynchronous_shaders;
438 Setting<bool> use_fast_gpu_time; 117 Setting<bool> use_fast_gpu_time;
439 118
440 Setting<float> bg_red; 119 Setting<float> bg_red;
@@ -457,6 +136,8 @@ struct Values {
457 // Controls 136 // Controls
458 std::array<PlayerInput, 10> players; 137 std::array<PlayerInput, 10> players;
459 138
139 bool use_docked_mode;
140
460 bool mouse_enabled; 141 bool mouse_enabled;
461 std::string mouse_device; 142 std::string mouse_device;
462 MouseButtonsRaw mouse_buttons; 143 MouseButtonsRaw mouse_buttons;
@@ -469,14 +150,19 @@ struct Values {
469 ButtonsRaw debug_pad_buttons; 150 ButtonsRaw debug_pad_buttons;
470 AnalogsRaw debug_pad_analogs; 151 AnalogsRaw debug_pad_analogs;
471 152
153 bool vibration_enabled;
154
155 bool motion_enabled;
472 std::string motion_device; 156 std::string motion_device;
157 std::string touch_device;
473 TouchscreenInput touchscreen; 158 TouchscreenInput touchscreen;
474 std::atomic_bool is_device_reload_pending{true}; 159 std::atomic_bool is_device_reload_pending{true};
160 bool use_touch_from_button;
161 int touch_from_button_map_index;
475 std::string udp_input_address; 162 std::string udp_input_address;
476 u16 udp_input_port; 163 u16 udp_input_port;
477 u8 udp_pad_index; 164 u8 udp_pad_index;
478 165 std::vector<TouchFromButtonMap> touch_from_button_maps;
479 bool use_docked_mode;
480 166
481 // Data Storage 167 // Data Storage
482 bool use_virtual_sd; 168 bool use_virtual_sd;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 78915e6db..da09c0dbc 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -25,6 +25,8 @@
25 25
26namespace Core { 26namespace Core {
27 27
28namespace Telemetry = Common::Telemetry;
29
28static u64 GenerateTelemetryId() { 30static u64 GenerateTelemetryId() {
29 u64 telemetry_id{}; 31 u64 telemetry_id{};
30 32
@@ -70,12 +72,12 @@ static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) {
70 72
71u64 GetTelemetryId() { 73u64 GetTelemetryId() {
72 u64 telemetry_id{}; 74 u64 telemetry_id{};
73 const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + 75 const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) +
74 "telemetry_id"}; 76 "telemetry_id"};
75 77
76 bool generate_new_id = !FileUtil::Exists(filename); 78 bool generate_new_id = !Common::FS::Exists(filename);
77 if (!generate_new_id) { 79 if (!generate_new_id) {
78 FileUtil::IOFile file(filename, "rb"); 80 Common::FS::IOFile file(filename, "rb");
79 if (!file.IsOpen()) { 81 if (!file.IsOpen()) {
80 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 82 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
81 return {}; 83 return {};
@@ -88,7 +90,7 @@ u64 GetTelemetryId() {
88 } 90 }
89 91
90 if (generate_new_id) { 92 if (generate_new_id) {
91 FileUtil::IOFile file(filename, "wb"); 93 Common::FS::IOFile file(filename, "wb");
92 if (!file.IsOpen()) { 94 if (!file.IsOpen()) {
93 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 95 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
94 return {}; 96 return {};
@@ -102,10 +104,10 @@ u64 GetTelemetryId() {
102 104
103u64 RegenerateTelemetryId() { 105u64 RegenerateTelemetryId() {
104 const u64 new_telemetry_id{GenerateTelemetryId()}; 106 const u64 new_telemetry_id{GenerateTelemetryId()};
105 const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + 107 const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) +
106 "telemetry_id"}; 108 "telemetry_id"};
107 109
108 FileUtil::IOFile file(filename, "wb"); 110 Common::FS::IOFile file(filename, "wb");
109 if (!file.IsOpen()) { 111 if (!file.IsOpen()) {
110 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 112 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
111 return {}; 113 return {};
@@ -207,6 +209,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
207 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue()); 209 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
208 AddField(field_type, "Renderer_UseAssemblyShaders", 210 AddField(field_type, "Renderer_UseAssemblyShaders",
209 Settings::values.use_assembly_shaders.GetValue()); 211 Settings::values.use_assembly_shaders.GetValue());
212 AddField(field_type, "Renderer_UseAsynchronousShaders",
213 Settings::values.use_asynchronous_shaders.GetValue());
210 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); 214 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
211} 215}
212 216
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index 17ac22377..66789d4bd 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -52,7 +52,7 @@ public:
52 * @param value Value for the field to add. 52 * @param value Value for the field to add.
53 */ 53 */
54 template <typename T> 54 template <typename T>
55 void AddField(Telemetry::FieldType type, const char* name, T value) { 55 void AddField(Common::Telemetry::FieldType type, const char* name, T value) {
56 field_collection.AddField(type, name, std::move(value)); 56 field_collection.AddField(type, name, std::move(value));
57 } 57 }
58 58
@@ -63,7 +63,8 @@ public:
63 bool SubmitTestcase(); 63 bool SubmitTestcase();
64 64
65private: 65private:
66 Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session 66 /// Tracks all added fields for the session
67 Common::Telemetry::FieldCollection field_collection;
67}; 68};
68 69
69/** 70/**
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 8b0c50d11..5c674a099 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -14,7 +14,7 @@
14namespace Tools { 14namespace Tools {
15namespace { 15namespace {
16 16
17constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(1000000000 / 60); 17constexpr auto memory_freezer_ns = std::chrono::nanoseconds{1000000000 / 60};
18 18
19u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { 19u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
20 switch (width) { 20 switch (width) {
@@ -57,8 +57,10 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m
57 : core_timing{core_timing_}, memory{memory_} { 57 : core_timing{core_timing_}, memory{memory_} {
58 event = Core::Timing::CreateEvent( 58 event = Core::Timing::CreateEvent(
59 "MemoryFreezer::FrameCallback", 59 "MemoryFreezer::FrameCallback",
60 [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); }); 60 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
61 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); 61 FrameCallback(user_data, ns_late);
62 });
63 core_timing.ScheduleEvent(memory_freezer_ns, event);
62} 64}
63 65
64Freezer::~Freezer() { 66Freezer::~Freezer() {
@@ -68,7 +70,7 @@ Freezer::~Freezer() {
68void Freezer::SetActive(bool active) { 70void Freezer::SetActive(bool active) {
69 if (!this->active.exchange(active)) { 71 if (!this->active.exchange(active)) {
70 FillEntryReads(); 72 FillEntryReads();
71 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); 73 core_timing.ScheduleEvent(memory_freezer_ns, event);
72 LOG_DEBUG(Common_Memory, "Memory freezer activated!"); 74 LOG_DEBUG(Common_Memory, "Memory freezer activated!");
73 } else { 75 } else {
74 LOG_DEBUG(Common_Memory, "Memory freezer deactivated!"); 76 LOG_DEBUG(Common_Memory, "Memory freezer deactivated!");
@@ -105,28 +107,21 @@ void Freezer::Unfreeze(VAddr address) {
105 107
106 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address); 108 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
107 109
108 entries.erase( 110 std::erase_if(entries, [address](const Entry& entry) { return entry.address == address; });
109 std::remove_if(entries.begin(), entries.end(),
110 [&address](const Entry& entry) { return entry.address == address; }),
111 entries.end());
112} 111}
113 112
114bool Freezer::IsFrozen(VAddr address) const { 113bool Freezer::IsFrozen(VAddr address) const {
115 std::lock_guard lock{entries_mutex}; 114 std::lock_guard lock{entries_mutex};
116 115
117 return std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { 116 return FindEntry(address) != entries.cend();
118 return entry.address == address;
119 }) != entries.end();
120} 117}
121 118
122void Freezer::SetFrozenValue(VAddr address, u64 value) { 119void Freezer::SetFrozenValue(VAddr address, u64 value) {
123 std::lock_guard lock{entries_mutex}; 120 std::lock_guard lock{entries_mutex};
124 121
125 const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { 122 const auto iter = FindEntry(address);
126 return entry.address == address;
127 });
128 123
129 if (iter == entries.end()) { 124 if (iter == entries.cend()) {
130 LOG_ERROR(Common_Memory, 125 LOG_ERROR(Common_Memory,
131 "Tried to set freeze value for address={:016X} that is not frozen!", address); 126 "Tried to set freeze value for address={:016X} that is not frozen!", address);
132 return; 127 return;
@@ -141,11 +136,9 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) {
141std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { 136std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
142 std::lock_guard lock{entries_mutex}; 137 std::lock_guard lock{entries_mutex};
143 138
144 const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { 139 const auto iter = FindEntry(address);
145 return entry.address == address;
146 });
147 140
148 if (iter == entries.end()) { 141 if (iter == entries.cend()) {
149 return std::nullopt; 142 return std::nullopt;
150 } 143 }
151 144
@@ -158,7 +151,17 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const {
158 return entries; 151 return entries;
159} 152}
160 153
161void Freezer::FrameCallback(u64 userdata, s64 ns_late) { 154Freezer::Entries::iterator Freezer::FindEntry(VAddr address) {
155 return std::find_if(entries.begin(), entries.end(),
156 [address](const Entry& entry) { return entry.address == address; });
157}
158
159Freezer::Entries::const_iterator Freezer::FindEntry(VAddr address) const {
160 return std::find_if(entries.begin(), entries.end(),
161 [address](const Entry& entry) { return entry.address == address; });
162}
163
164void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
162 if (!IsActive()) { 165 if (!IsActive()) {
163 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); 166 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
164 return; 167 return;
@@ -173,7 +176,7 @@ void Freezer::FrameCallback(u64 userdata, s64 ns_late) {
173 MemoryWriteWidth(memory, entry.width, entry.address, entry.value); 176 MemoryWriteWidth(memory, entry.width, entry.address, entry.value);
174 } 177 }
175 178
176 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - ns_late, event); 179 core_timing.ScheduleEvent(memory_freezer_ns - ns_late, event);
177} 180}
178 181
179void Freezer::FillEntryReads() { 182void Freezer::FillEntryReads() {
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h
index 62fc6aa6c..0fdb701a7 100644
--- a/src/core/tools/freezer.h
+++ b/src/core/tools/freezer.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <atomic>
8#include <chrono>
8#include <memory> 9#include <memory>
9#include <mutex> 10#include <mutex>
10#include <optional> 11#include <optional>
@@ -72,13 +73,18 @@ public:
72 std::vector<Entry> GetEntries() const; 73 std::vector<Entry> GetEntries() const;
73 74
74private: 75private:
75 void FrameCallback(u64 userdata, s64 cycles_late); 76 using Entries = std::vector<Entry>;
77
78 Entries::iterator FindEntry(VAddr address);
79 Entries::const_iterator FindEntry(VAddr address) const;
80
81 void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
76 void FillEntryReads(); 82 void FillEntryReads();
77 83
78 std::atomic_bool active{false}; 84 std::atomic_bool active{false};
79 85
80 mutable std::mutex entries_mutex; 86 mutable std::mutex entries_mutex;
81 std::vector<Entry> entries; 87 Entries entries;
82 88
83 std::shared_ptr<Core::Timing::EventType> event; 89 std::shared_ptr<Core::Timing::EventType> event;
84 Core::Timing::CoreTiming& core_timing; 90 Core::Timing::CoreTiming& core_timing;