summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/alignment.h12
-rw-r--r--src/common/web_result.h1
-rw-r--r--src/core/CMakeLists.txt22
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp7
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h4
-rw-r--r--src/core/core.cpp46
-rw-r--r--src/core/core.h8
-rw-r--r--src/core/core_cpu.cpp14
-rw-r--r--src/core/core_cpu.h17
-rw-r--r--src/core/crypto/key_manager.cpp39
-rw-r--r--src/core/crypto/key_manager.h2
-rw-r--r--src/core/crypto/partition_data_manager.cpp81
-rw-r--r--src/core/crypto/partition_data_manager.h18
-rw-r--r--src/core/file_sys/bis_factory.cpp12
-rw-r--r--src/core/file_sys/bis_factory.h8
-rw-r--r--src/core/file_sys/card_image.cpp10
-rw-r--r--src/core/file_sys/card_image.h2
-rw-r--r--src/core/file_sys/content_archive.cpp533
-rw-r--r--src/core/file_sys/content_archive.h19
-rw-r--r--src/core/file_sys/control_metadata.cpp13
-rw-r--r--src/core/file_sys/control_metadata.h1
-rw-r--r--src/core/file_sys/patch_manager.cpp12
-rw-r--r--src/core/file_sys/registered_cache.cpp14
-rw-r--r--src/core/file_sys/registered_cache.h12
-rw-r--r--src/core/file_sys/savedata_factory.cpp13
-rw-r--r--src/core/file_sys/sdmc_factory.cpp8
-rw-r--r--src/core/file_sys/sdmc_factory.h4
-rw-r--r--src/core/gdbstub/gdbstub.cpp6
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp2
-rw-r--r--src/core/hle/kernel/process.cpp10
-rw-r--r--src/core/hle/kernel/process.h21
-rw-r--r--src/core/hle/kernel/svc.cpp108
-rw-r--r--src/core/hle/kernel/svc.h4
-rw-r--r--src/core/hle/kernel/svc_wrap.h8
-rw-r--r--src/core/hle/kernel/thread.cpp16
-rw-r--r--src/core/hle/kernel/vm_manager.cpp55
-rw-r--r--src/core/hle/kernel/vm_manager.h15
-rw-r--r--src/core/hle/service/am/am.cpp36
-rw-r--r--src/core/hle/service/am/am.h4
-rw-r--r--src/core/hle/service/am/omm.cpp34
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp33
-rw-r--r--src/core/hle/service/aoc/aoc_u.h2
-rw-r--r--src/core/hle/service/audio/audren_u.cpp37
-rw-r--r--src/core/hle/service/es/es.cpp18
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp29
-rw-r--r--src/core/hle/service/filesystem/filesystem.h12
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp30
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h45
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp42
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h56
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h63
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h50
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h50
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp436
-rw-r--r--src/core/hle/service/hid/controllers/npad.h287
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h34
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp65
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h63
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp45
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h60
-rw-r--r--src/core/hle/service/hid/hid.cpp517
-rw-r--r--src/core/hle/service/hid/hid.h402
-rw-r--r--src/core/hle/service/mm/mm_u.cpp50
-rw-r--r--src/core/hle/service/nfp/nfp.cpp2
-rw-r--r--src/core/hle/service/nifm/nifm.cpp1
-rw-r--r--src/core/hle/service/nim/nim.cpp17
-rw-r--r--src/core/hle/service/ns/ns.cpp55
-rw-r--r--src/core/hle/service/ns/pl_u.cpp2
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/hle/service/service.h3
-rw-r--r--src/core/hle/service/set/set_cal.cpp3
-rw-r--r--src/core/hle/service/vi/vi.cpp50
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp22
-rw-r--r--src/core/loader/loader.cpp3
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/nro.cpp10
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp23
-rw-r--r--src/core/loader/nso.h6
-rw-r--r--src/core/loader/xci.cpp3
-rw-r--r--src/core/settings.h2
-rw-r--r--src/core/telemetry_session.cpp4
-rw-r--r--src/video_core/engines/fermi_2d.cpp23
-rw-r--r--src/video_core/engines/kepler_memory.cpp11
-rw-r--r--src/video_core/engines/kepler_memory.h7
-rw-r--r--src/video_core/engines/maxwell_3d.h5
-rw-r--r--src/video_core/engines/maxwell_dma.cpp71
-rw-r--r--src/video_core/engines/maxwell_dma.h12
-rw-r--r--src/video_core/engines/shader_bytecode.h2
-rw-r--r--src/video_core/gpu.cpp4
-rw-r--r--src/video_core/memory_manager.cpp10
-rw-r--r--src/video_core/memory_manager.h1
-rw-r--r--src/video_core/rasterizer_cache.h127
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h9
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp37
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp369
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h104
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h11
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp84
-rw-r--r--src/video_core/textures/decoders.cpp233
-rw-r--r--src/video_core/textures/decoders.h24
-rw-r--r--src/video_core/textures/texture.h1
-rw-r--r--src/web_service/telemetry_json.cpp90
-rw-r--r--src/web_service/telemetry_json.h24
-rw-r--r--src/web_service/verify_login.cpp1
-rw-r--r--src/web_service/web_backend.cpp235
-rw-r--r--src/web_service/web_backend.h58
-rw-r--r--src/yuzu/configuration/config.cpp6
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp4
-rw-r--r--src/yuzu/configuration/configure_graphics.ui4
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp5
-rw-r--r--src/yuzu/debugger/wait_tree.cpp9
-rw-r--r--src/yuzu/game_list_worker.cpp19
-rw-r--r--src/yuzu/game_list_worker.h2
-rw-r--r--src/yuzu/main.cpp44
-rw-r--r--src/yuzu_cmd/config.cpp4
-rw-r--r--src/yuzu_cmd/default_ini.h4
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
122 files changed, 3905 insertions, 1809 deletions
diff --git a/src/common/alignment.h b/src/common/alignment.h
index 225770fab..d94a2291f 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -19,4 +19,16 @@ constexpr T AlignDown(T value, std::size_t size) {
19 return static_cast<T>(value - value % size); 19 return static_cast<T>(value - value % size);
20} 20}
21 21
22template <typename T>
23constexpr bool Is4KBAligned(T value) {
24 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
25 return (value & 0xFFF) == 0;
26}
27
28template <typename T>
29constexpr bool IsWordAligned(T value) {
30 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
31 return (value & 0b11) == 0;
32}
33
22} // namespace Common 34} // namespace Common
diff --git a/src/common/web_result.h b/src/common/web_result.h
index 969926674..8bfa2141d 100644
--- a/src/common/web_result.h
+++ b/src/common/web_result.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <string> 7#include <string>
8#include "common/common_types.h"
8 9
9namespace Common { 10namespace Common {
10struct WebResult { 11struct WebResult {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 4fddaafd1..4755ec822 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -236,6 +236,24 @@ add_library(core STATIC
236 hle/service/hid/irs.h 236 hle/service/hid/irs.h
237 hle/service/hid/xcd.cpp 237 hle/service/hid/xcd.cpp
238 hle/service/hid/xcd.h 238 hle/service/hid/xcd.h
239 hle/service/hid/controllers/controller_base.cpp
240 hle/service/hid/controllers/controller_base.h
241 hle/service/hid/controllers/debug_pad.cpp
242 hle/service/hid/controllers/debug_pad.h
243 hle/service/hid/controllers/gesture.cpp
244 hle/service/hid/controllers/gesture.h
245 hle/service/hid/controllers/keyboard.cpp
246 hle/service/hid/controllers/keyboard.h
247 hle/service/hid/controllers/mouse.cpp
248 hle/service/hid/controllers/mouse.h
249 hle/service/hid/controllers/npad.cpp
250 hle/service/hid/controllers/npad.h
251 hle/service/hid/controllers/stubbed.cpp
252 hle/service/hid/controllers/stubbed.h
253 hle/service/hid/controllers/touchscreen.cpp
254 hle/service/hid/controllers/touchscreen.h
255 hle/service/hid/controllers/xpad.cpp
256 hle/service/hid/controllers/xpad.h
239 hle/service/lbl/lbl.cpp 257 hle/service/lbl/lbl.cpp
240 hle/service/lbl/lbl.h 258 hle/service/lbl/lbl.h
241 hle/service/ldn/ldn.cpp 259 hle/service/ldn/ldn.cpp
@@ -400,8 +418,8 @@ create_target_directory_groups(core)
400target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 418target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
401target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives) 419target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives)
402if (ENABLE_WEB_SERVICE) 420if (ENABLE_WEB_SERVICE)
403 add_definitions(-DENABLE_WEB_SERVICE) 421 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
404 target_link_libraries(core PUBLIC json-headers web_service) 422 target_link_libraries(core PRIVATE web_service)
405endif() 423endif()
406 424
407if (ARCHITECTURE_x86_64) 425if (ARCHITECTURE_x86_64)
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 0762321a9..4d2491870 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -144,7 +144,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
144 144
145 // Multi-process state 145 // Multi-process state
146 config.processor_id = core_index; 146 config.processor_id = core_index;
147 config.global_monitor = &exclusive_monitor->monitor; 147 config.global_monitor = &exclusive_monitor.monitor;
148 148
149 // System registers 149 // System registers
150 config.tpidrro_el0 = &cb->tpidrro_el0; 150 config.tpidrro_el0 = &cb->tpidrro_el0;
@@ -171,10 +171,9 @@ void ARM_Dynarmic::Step() {
171 cb->InterpreterFallback(jit->GetPC(), 1); 171 cb->InterpreterFallback(jit->GetPC(), 1);
172} 172}
173 173
174ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 174ARM_Dynarmic::ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index)
175 std::size_t core_index)
176 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index}, 175 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index},
177 exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} { 176 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
178 ThreadContext ctx{}; 177 ThreadContext ctx{};
179 inner_unicorn.SaveContext(ctx); 178 inner_unicorn.SaveContext(ctx);
180 PageTableChanged(); 179 PageTableChanged();
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 4ee92ee27..512bf8ce9 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -23,7 +23,7 @@ class DynarmicExclusiveMonitor;
23 23
24class ARM_Dynarmic final : public ARM_Interface { 24class ARM_Dynarmic final : public ARM_Interface {
25public: 25public:
26 ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, std::size_t core_index); 26 ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
27 ~ARM_Dynarmic(); 27 ~ARM_Dynarmic();
28 28
29 void MapBackingMemory(VAddr address, std::size_t size, u8* memory, 29 void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
@@ -62,7 +62,7 @@ private:
62 ARM_Unicorn inner_unicorn; 62 ARM_Unicorn inner_unicorn;
63 63
64 std::size_t core_index; 64 std::size_t core_index;
65 std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor; 65 DynarmicExclusiveMonitor& exclusive_monitor;
66 66
67 Memory::PageTable* current_page_table = nullptr; 67 Memory::PageTable* current_page_table = nullptr;
68}; 68};
diff --git a/src/core/core.cpp b/src/core/core.cpp
index e2fb9e038..7cb86ed92 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -71,9 +71,9 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
71} 71}
72 72
73/// Runs a CPU core while the system is powered on 73/// Runs a CPU core while the system is powered on
74void RunCpuCore(std::shared_ptr<Cpu> cpu_state) { 74void RunCpuCore(Cpu& cpu_state) {
75 while (Core::System::GetInstance().IsPoweredOn()) { 75 while (Core::System::GetInstance().IsPoweredOn()) {
76 cpu_state->RunLoop(true); 76 cpu_state.RunLoop(true);
77 } 77 }
78} 78}
79} // Anonymous namespace 79} // Anonymous namespace
@@ -95,7 +95,7 @@ struct System::Impl {
95 status = ResultStatus::Success; 95 status = ResultStatus::Success;
96 96
97 // Update thread_to_cpu in case Core 0 is run from a different host thread 97 // Update thread_to_cpu in case Core 0 is run from a different host thread
98 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 98 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
99 99
100 if (GDBStub::IsServerEnabled()) { 100 if (GDBStub::IsServerEnabled()) {
101 GDBStub::HandlePacket(); 101 GDBStub::HandlePacket();
@@ -139,16 +139,16 @@ struct System::Impl {
139 auto main_process = Kernel::Process::Create(kernel, "main"); 139 auto main_process = Kernel::Process::Create(kernel, "main");
140 kernel.MakeCurrentProcess(main_process.get()); 140 kernel.MakeCurrentProcess(main_process.get());
141 141
142 cpu_barrier = std::make_shared<CpuBarrier>(); 142 cpu_barrier = std::make_unique<CpuBarrier>();
143 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); 143 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
144 for (std::size_t index = 0; index < cpu_cores.size(); ++index) { 144 for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
145 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); 145 cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);
146 } 146 }
147 147
148 telemetry_session = std::make_unique<Core::TelemetrySession>(); 148 telemetry_session = std::make_unique<Core::TelemetrySession>();
149 service_manager = std::make_shared<Service::SM::ServiceManager>(); 149 service_manager = std::make_shared<Service::SM::ServiceManager>();
150 150
151 Service::Init(service_manager, virtual_filesystem); 151 Service::Init(service_manager, *virtual_filesystem);
152 GDBStub::Init(); 152 GDBStub::Init();
153 153
154 renderer = VideoCore::CreateRenderer(emu_window); 154 renderer = VideoCore::CreateRenderer(emu_window);
@@ -160,12 +160,12 @@ struct System::Impl {
160 160
161 // Create threads for CPU cores 1-3, and build thread_to_cpu map 161 // Create threads for CPU cores 1-3, and build thread_to_cpu map
162 // CPU core 0 is run on the main thread 162 // CPU core 0 is run on the main thread
163 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 163 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
164 if (Settings::values.use_multi_core) { 164 if (Settings::values.use_multi_core) {
165 for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) { 165 for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
166 cpu_core_threads[index] = 166 cpu_core_threads[index] =
167 std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]); 167 std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
168 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1]; 168 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();
169 } 169 }
170 } 170 }
171 171
@@ -245,6 +245,7 @@ struct System::Impl {
245 for (auto& cpu_core : cpu_cores) { 245 for (auto& cpu_core : cpu_cores) {
246 cpu_core.reset(); 246 cpu_core.reset();
247 } 247 }
248 cpu_exclusive_monitor.reset();
248 cpu_barrier.reset(); 249 cpu_barrier.reset();
249 250
250 // Shutdown kernel and core timing 251 // Shutdown kernel and core timing
@@ -282,9 +283,9 @@ struct System::Impl {
282 std::unique_ptr<VideoCore::RendererBase> renderer; 283 std::unique_ptr<VideoCore::RendererBase> renderer;
283 std::unique_ptr<Tegra::GPU> gpu_core; 284 std::unique_ptr<Tegra::GPU> gpu_core;
284 std::shared_ptr<Tegra::DebugContext> debug_context; 285 std::shared_ptr<Tegra::DebugContext> debug_context;
285 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor; 286 std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
286 std::shared_ptr<CpuBarrier> cpu_barrier; 287 std::unique_ptr<CpuBarrier> cpu_barrier;
287 std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; 288 std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
288 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; 289 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
289 std::size_t active_core{}; ///< Active core, only used in single thread mode 290 std::size_t active_core{}; ///< Active core, only used in single thread mode
290 291
@@ -298,7 +299,7 @@ struct System::Impl {
298 std::string status_details = ""; 299 std::string status_details = "";
299 300
300 /// Map of guest threads to CPU cores 301 /// Map of guest threads to CPU cores
301 std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu; 302 std::map<std::thread::id, Cpu*> thread_to_cpu;
302 303
303 Core::PerfStats perf_stats; 304 Core::PerfStats perf_stats;
304 Core::FrameLimiter frame_limiter; 305 Core::FrameLimiter frame_limiter;
@@ -354,12 +355,15 @@ std::size_t System::CurrentCoreIndex() {
354} 355}
355 356
356Kernel::Scheduler& System::CurrentScheduler() { 357Kernel::Scheduler& System::CurrentScheduler() {
357 return *CurrentCpuCore().Scheduler(); 358 return CurrentCpuCore().Scheduler();
358} 359}
359 360
360const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_index) { 361Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
361 ASSERT(core_index < NUM_CPU_CORES); 362 return CpuCore(core_index).Scheduler();
362 return impl->cpu_cores[core_index]->Scheduler(); 363}
364
365const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const {
366 return CpuCore(core_index).Scheduler();
363} 367}
364 368
365Kernel::Process* System::CurrentProcess() { 369Kernel::Process* System::CurrentProcess() {
@@ -371,8 +375,7 @@ const Kernel::Process* System::CurrentProcess() const {
371} 375}
372 376
373ARM_Interface& System::ArmInterface(std::size_t core_index) { 377ARM_Interface& System::ArmInterface(std::size_t core_index) {
374 ASSERT(core_index < NUM_CPU_CORES); 378 return CpuCore(core_index).ArmInterface();
375 return impl->cpu_cores[core_index]->ArmInterface();
376} 379}
377 380
378Cpu& System::CpuCore(std::size_t core_index) { 381Cpu& System::CpuCore(std::size_t core_index) {
@@ -380,6 +383,11 @@ Cpu& System::CpuCore(std::size_t core_index) {
380 return *impl->cpu_cores[core_index]; 383 return *impl->cpu_cores[core_index];
381} 384}
382 385
386const Cpu& System::CpuCore(std::size_t core_index) const {
387 ASSERT(core_index < NUM_CPU_CORES);
388 return *impl->cpu_cores[core_index];
389}
390
383ExclusiveMonitor& System::Monitor() { 391ExclusiveMonitor& System::Monitor() {
384 return *impl->cpu_exclusive_monitor; 392 return *impl->cpu_exclusive_monitor;
385} 393}
diff --git a/src/core/core.h b/src/core/core.h
index ea4d53914..173be45f8 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -156,6 +156,9 @@ public:
156 /// Gets a CPU interface to the CPU core with the specified index 156 /// Gets a CPU interface to the CPU core with the specified index
157 Cpu& CpuCore(std::size_t core_index); 157 Cpu& CpuCore(std::size_t core_index);
158 158
159 /// Gets a CPU interface to the CPU core with the specified index
160 const Cpu& CpuCore(std::size_t core_index) const;
161
159 /// Gets the exclusive monitor 162 /// Gets the exclusive monitor
160 ExclusiveMonitor& Monitor(); 163 ExclusiveMonitor& Monitor();
161 164
@@ -172,7 +175,10 @@ public:
172 const VideoCore::RendererBase& Renderer() const; 175 const VideoCore::RendererBase& Renderer() const;
173 176
174 /// Gets the scheduler for the CPU core with the specified index 177 /// Gets the scheduler for the CPU core with the specified index
175 const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index); 178 Kernel::Scheduler& Scheduler(std::size_t core_index);
179
180 /// Gets the scheduler for the CPU core with the specified index
181 const Kernel::Scheduler& Scheduler(std::size_t core_index) const;
176 182
177 /// Provides a pointer to the current process 183 /// Provides a pointer to the current process
178 Kernel::Process* CurrentProcess(); 184 Kernel::Process* CurrentProcess();
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 265f8ed9c..fffda8a99 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -49,10 +49,8 @@ bool CpuBarrier::Rendezvous() {
49 return false; 49 return false;
50} 50}
51 51
52Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 52Cpu::Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index)
53 std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index) 53 : cpu_barrier{cpu_barrier}, core_index{core_index} {
54 : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
55
56 if (Settings::values.use_cpu_jit) { 54 if (Settings::values.use_cpu_jit) {
57#ifdef ARCHITECTURE_x86_64 55#ifdef ARCHITECTURE_x86_64
58 arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index); 56 arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index);
@@ -64,15 +62,15 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
64 arm_interface = std::make_unique<ARM_Unicorn>(); 62 arm_interface = std::make_unique<ARM_Unicorn>();
65 } 63 }
66 64
67 scheduler = std::make_shared<Kernel::Scheduler>(*arm_interface); 65 scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface);
68} 66}
69 67
70Cpu::~Cpu() = default; 68Cpu::~Cpu() = default;
71 69
72std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) { 70std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
73 if (Settings::values.use_cpu_jit) { 71 if (Settings::values.use_cpu_jit) {
74#ifdef ARCHITECTURE_x86_64 72#ifdef ARCHITECTURE_x86_64
75 return std::make_shared<DynarmicExclusiveMonitor>(num_cores); 73 return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
76#else 74#else
77 return nullptr; // TODO(merry): Passthrough exclusive monitor 75 return nullptr; // TODO(merry): Passthrough exclusive monitor
78#endif 76#endif
@@ -83,7 +81,7 @@ std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_core
83 81
84void Cpu::RunLoop(bool tight_loop) { 82void Cpu::RunLoop(bool tight_loop) {
85 // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step 83 // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
86 if (!cpu_barrier->Rendezvous()) { 84 if (!cpu_barrier.Rendezvous()) {
87 // If rendezvous failed, session has been killed 85 // If rendezvous failed, session has been killed
88 return; 86 return;
89 } 87 }
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index ee7e04abc..1d2bdc6cd 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -41,8 +41,7 @@ private:
41 41
42class Cpu { 42class Cpu {
43public: 43public:
44 Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 44 Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index);
45 std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index);
46 ~Cpu(); 45 ~Cpu();
47 46
48 void RunLoop(bool tight_loop = true); 47 void RunLoop(bool tight_loop = true);
@@ -59,8 +58,12 @@ public:
59 return *arm_interface; 58 return *arm_interface;
60 } 59 }
61 60
62 const std::shared_ptr<Kernel::Scheduler>& Scheduler() const { 61 Kernel::Scheduler& Scheduler() {
63 return scheduler; 62 return *scheduler;
63 }
64
65 const Kernel::Scheduler& Scheduler() const {
66 return *scheduler;
64 } 67 }
65 68
66 bool IsMainCore() const { 69 bool IsMainCore() const {
@@ -71,14 +74,14 @@ public:
71 return core_index; 74 return core_index;
72 } 75 }
73 76
74 static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores); 77 static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);
75 78
76private: 79private:
77 void Reschedule(); 80 void Reschedule();
78 81
79 std::unique_ptr<ARM_Interface> arm_interface; 82 std::unique_ptr<ARM_Interface> arm_interface;
80 std::shared_ptr<CpuBarrier> cpu_barrier; 83 CpuBarrier& cpu_barrier;
81 std::shared_ptr<Kernel::Scheduler> scheduler; 84 std::unique_ptr<Kernel::Scheduler> scheduler;
82 85
83 std::atomic<bool> reschedule_pending = false; 86 std::atomic<bool> reschedule_pending = false;
84 std::size_t core_index; 87 std::size_t core_index;
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index a59a7e1f5..fd0786068 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -98,7 +98,7 @@ std::array<u8, 144> DecryptKeyblob(const std::array<u8, 176>& encrypted_keyblob,
98 return keyblob; 98 return keyblob;
99} 99}
100 100
101void KeyManager::DeriveGeneralPurposeKeys(u8 crypto_revision) { 101void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
102 const auto kek_generation_source = 102 const auto kek_generation_source =
103 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)); 103 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
104 const auto key_generation_source = 104 const auto key_generation_source =
@@ -147,31 +147,38 @@ boost::optional<Key128> DeriveSDSeed() {
147 "rb+"); 147 "rb+");
148 if (!save_43.IsOpen()) 148 if (!save_43.IsOpen())
149 return boost::none; 149 return boost::none;
150
150 const FileUtil::IOFile sd_private( 151 const FileUtil::IOFile sd_private(
151 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); 152 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
152 if (!sd_private.IsOpen()) 153 if (!sd_private.IsOpen())
153 return boost::none; 154 return boost::none;
154 155
155 sd_private.Seek(0, SEEK_SET);
156 std::array<u8, 0x10> private_seed{}; 156 std::array<u8, 0x10> private_seed{};
157 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != 0x10) 157 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
158 return boost::none; 158 return boost::none;
159 }
159 160
160 std::array<u8, 0x10> buffer{}; 161 std::array<u8, 0x10> buffer{};
161 std::size_t offset = 0; 162 std::size_t offset = 0;
162 for (; offset + 0x10 < save_43.GetSize(); ++offset) { 163 for (; offset + 0x10 < save_43.GetSize(); ++offset) {
163 save_43.Seek(offset, SEEK_SET); 164 if (!save_43.Seek(offset, SEEK_SET)) {
165 return boost::none;
166 }
167
164 save_43.ReadBytes(buffer.data(), buffer.size()); 168 save_43.ReadBytes(buffer.data(), buffer.size());
165 if (buffer == private_seed) 169 if (buffer == private_seed) {
166 break; 170 break;
171 }
167 } 172 }
168 173
169 if (offset + 0x10 >= save_43.GetSize()) 174 if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
170 return boost::none; 175 return boost::none;
176 }
171 177
172 Key128 seed{}; 178 Key128 seed{};
173 save_43.Seek(offset + 0x10, SEEK_SET); 179 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
174 save_43.ReadBytes(seed.data(), seed.size()); 180 return boost::none;
181 }
175 return seed; 182 return seed;
176} 183}
177 184
@@ -234,7 +241,9 @@ std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
234 return {}; 241 return {};
235 242
236 std::vector<u8> buffer(ticket_save.GetSize()); 243 std::vector<u8> buffer(ticket_save.GetSize());
237 ticket_save.ReadBytes(buffer.data(), buffer.size()); 244 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
245 return {};
246 }
238 247
239 std::vector<TicketRaw> out; 248 std::vector<TicketRaw> out;
240 u32 magic{}; 249 u32 magic{};
@@ -261,6 +270,9 @@ static std::array<u8, size> operator^(const std::array<u8, size>& lhs,
261 270
262template <size_t target_size, size_t in_size> 271template <size_t target_size, size_t in_size>
263static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) { 272static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
273 // Avoids truncation overflow within the loop below.
274 static_assert(target_size <= 0xFF);
275
264 std::array<u8, in_size + 4> seed_exp{}; 276 std::array<u8, in_size + 4> seed_exp{};
265 std::memcpy(seed_exp.data(), seed.data(), in_size); 277 std::memcpy(seed_exp.data(), seed.data(), in_size);
266 278
@@ -268,7 +280,7 @@ static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
268 size_t i = 0; 280 size_t i = 0;
269 while (out.size() < target_size) { 281 while (out.size() < target_size) {
270 out.resize(out.size() + 0x20); 282 out.resize(out.size() + 0x20);
271 seed_exp[in_size + 3] = i; 283 seed_exp[in_size + 3] = static_cast<u8>(i);
272 mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0); 284 mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
273 ++i; 285 ++i;
274 } 286 }
@@ -299,10 +311,11 @@ boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
299 std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority)); 311 std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
300 if (cert_authority == 0) 312 if (cert_authority == 0)
301 return boost::none; 313 return boost::none;
302 if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) 314 if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {
303 LOG_INFO(Crypto, 315 LOG_INFO(Crypto,
304 "Attempting to parse ticket with non-standard certificate authority {:08X}.", 316 "Attempting to parse ticket with non-standard certificate authority {:08X}.",
305 cert_authority); 317 cert_authority);
318 }
306 319
307 Key128 rights_id; 320 Key128 rights_id;
308 std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128)); 321 std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
@@ -871,9 +884,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
871 "/system/save/80000000000000e2", 884 "/system/save/80000000000000e2",
872 "rb+"); 885 "rb+");
873 886
887 const auto blob2 = GetTicketblob(save2);
874 auto res = GetTicketblob(save1); 888 auto res = GetTicketblob(save1);
875 const auto res2 = GetTicketblob(save2); 889 res.insert(res.end(), blob2.begin(), blob2.end());
876 std::copy(res2.begin(), res2.end(), std::back_inserter(res));
877 890
878 for (const auto& raw : res) { 891 for (const auto& raw : res) {
879 const auto pair = ParseTicket(raw, rsa_key); 892 const auto pair = ParseTicket(raw, rsa_key);
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index a41abbdfc..cccb3c0ae 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -175,7 +175,7 @@ private:
175 void WriteKeyToFile(KeyCategory category, std::string_view keyname, 175 void WriteKeyToFile(KeyCategory category, std::string_view keyname,
176 const std::array<u8, Size>& key); 176 const std::array<u8, Size>& key);
177 177
178 void DeriveGeneralPurposeKeys(u8 crypto_revision); 178 void DeriveGeneralPurposeKeys(std::size_t crypto_revision);
179 179
180 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); 180 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
181 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); 181 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index d1c04e98d..ed0775444 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -11,7 +11,6 @@
11#include <array> 11#include <array>
12#include <cctype> 12#include <cctype>
13#include <cstring> 13#include <cstring>
14#include <boost/optional/optional.hpp>
15#include <mbedtls/sha256.h> 14#include <mbedtls/sha256.h>
16#include "common/assert.h" 15#include "common/assert.h"
17#include "common/common_funcs.h" 16#include "common/common_funcs.h"
@@ -19,7 +18,7 @@
19#include "common/hex_util.h" 18#include "common/hex_util.h"
20#include "common/logging/log.h" 19#include "common/logging/log.h"
21#include "common/string_util.h" 20#include "common/string_util.h"
22#include "core/crypto/ctr_encryption_layer.h" 21#include "common/swap.h"
23#include "core/crypto/key_manager.h" 22#include "core/crypto/key_manager.h"
24#include "core/crypto/partition_data_manager.h" 23#include "core/crypto/partition_data_manager.h"
25#include "core/crypto/xts_encryption_layer.h" 24#include "core/crypto/xts_encryption_layer.h"
@@ -302,10 +301,10 @@ FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
302 return nullptr; 301 return nullptr;
303} 302}
304 303
305PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir) 304PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_dir)
306 : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")), 305 : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")),
307 fuses(FindFileInDirWithNames(sysdata_dir, "fuse")), 306 fuses(FindFileInDirWithNames(sysdata_dir, "fuses")),
308 kfuses(FindFileInDirWithNames(sysdata_dir, "kfuse")), 307 kfuses(FindFileInDirWithNames(sysdata_dir, "kfuses")),
309 package2({ 308 package2({
310 FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"), 309 FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"),
311 FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"), 310 FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"),
@@ -314,13 +313,14 @@ PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir)
314 FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"), 313 FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"),
315 FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"), 314 FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"),
316 }), 315 }),
316 prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")),
317 secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")), 317 secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")),
318 package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")), 318 package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")),
319 secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{} 319 secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{}
320 : secure_monitor->ReadAllBytes()), 320 : secure_monitor->ReadAllBytes()),
321 package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{} 321 package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{}
322 : package1_decrypted->ReadAllBytes()), 322 : package1_decrypted->ReadAllBytes()) {
323 prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")) {} 323}
324 324
325PartitionDataManager::~PartitionDataManager() = default; 325PartitionDataManager::~PartitionDataManager() = default;
326 326
@@ -332,18 +332,19 @@ FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const {
332 return boot0; 332 return boot0;
333} 333}
334 334
335std::array<u8, 176> PartitionDataManager::GetEncryptedKeyblob(u8 index) const { 335PartitionDataManager::EncryptedKeyBlob PartitionDataManager::GetEncryptedKeyblob(
336 if (HasBoot0() && index < 32) 336 std::size_t index) const {
337 if (HasBoot0() && index < NUM_ENCRYPTED_KEYBLOBS)
337 return GetEncryptedKeyblobs()[index]; 338 return GetEncryptedKeyblobs()[index];
338 return {}; 339 return {};
339} 340}
340 341
341std::array<std::array<u8, 176>, 32> PartitionDataManager::GetEncryptedKeyblobs() const { 342PartitionDataManager::EncryptedKeyBlobs PartitionDataManager::GetEncryptedKeyblobs() const {
342 if (!HasBoot0()) 343 if (!HasBoot0())
343 return {}; 344 return {};
344 345
345 std::array<std::array<u8, 176>, 32> out{}; 346 EncryptedKeyBlobs out{};
346 for (size_t i = 0; i < 0x20; ++i) 347 for (size_t i = 0; i < out.size(); ++i)
347 boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200); 348 boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200);
348 return out; 349 return out;
349} 350}
@@ -389,7 +390,7 @@ std::array<u8, 16> PartitionDataManager::GetKeyblobMACKeySource() const {
389 return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]); 390 return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]);
390} 391}
391 392
392std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(u8 revision) const { 393std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(std::size_t revision) const {
393 if (keyblob_source_hashes[revision] == SHA256Hash{}) { 394 if (keyblob_source_hashes[revision] == SHA256Hash{}) {
394 LOG_WARNING(Crypto, 395 LOG_WARNING(Crypto,
395 "No keyblob source hash for crypto revision {:02X}! Cannot derive keys...", 396 "No keyblob source hash for crypto revision {:02X}! Cannot derive keys...",
@@ -446,7 +447,7 @@ bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
446 return false; 447 return false;
447} 448}
448 449
449void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2_keys, 450void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& package2_keys,
450 Package2Type type) { 451 Package2Type type) {
451 FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>( 452 FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>(
452 package2[static_cast<size_t>(type)], 453 package2[static_cast<size_t>(type)],
@@ -456,43 +457,38 @@ void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20>
456 if (file->ReadObject(&header) != sizeof(Package2Header)) 457 if (file->ReadObject(&header) != sizeof(Package2Header))
457 return; 458 return;
458 459
459 u8 revision = 0xFF; 460 std::size_t revision = 0xFF;
460 if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) { 461 if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) {
461 for (size_t i = 0; i < package2_keys.size(); ++i) { 462 for (std::size_t i = 0; i < package2_keys.size(); ++i) {
462 if (AttemptDecrypt(package2_keys[i], header)) 463 if (AttemptDecrypt(package2_keys[i], header)) {
463 revision = i; 464 revision = i;
465 }
464 } 466 }
465 } 467 }
466 468
467 if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) 469 if (header.magic != Common::MakeMagic('P', 'K', '2', '1'))
468 return; 470 return;
469 471
470 const std::vector<u8> s1_iv(header.section_ctr[1].begin(), header.section_ctr[1].end());
471
472 const auto a = std::make_shared<FileSys::OffsetVfsFile>( 472 const auto a = std::make_shared<FileSys::OffsetVfsFile>(
473 file, header.section_size[1], header.section_size[0] + sizeof(Package2Header)); 473 file, header.section_size[1], header.section_size[0] + sizeof(Package2Header));
474 474
475 auto c = a->ReadAllBytes(); 475 auto c = a->ReadAllBytes();
476 476
477 AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR); 477 AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR);
478 cipher.SetIV(s1_iv); 478 cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
479 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); 479 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
480 480
481 // package2_decrypted[static_cast<size_t>(type)] = s1;
482
483 INIHeader ini; 481 INIHeader ini;
484 std::memcpy(&ini, c.data(), sizeof(INIHeader)); 482 std::memcpy(&ini, c.data(), sizeof(INIHeader));
485 if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1')) 483 if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
486 return; 484 return;
487 485
488 std::map<u64, KIPHeader> kips{};
489 u64 offset = sizeof(INIHeader); 486 u64 offset = sizeof(INIHeader);
490 for (size_t i = 0; i < ini.process_count; ++i) { 487 for (size_t i = 0; i < ini.process_count; ++i) {
491 KIPHeader kip; 488 KIPHeader kip;
492 std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader)); 489 std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
493 if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1')) 490 if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
494 return; 491 return;
495 kips.emplace(offset, kip);
496 492
497 const auto name = 493 const auto name =
498 Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size()); 494 Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
@@ -503,33 +499,30 @@ void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20>
503 continue; 499 continue;
504 } 500 }
505 501
506 std::vector<u8> text(kip.sections[0].size_compressed); 502 const u64 initial_offset = sizeof(KIPHeader) + offset;
507 std::vector<u8> rodata(kip.sections[1].size_compressed); 503 const auto text_begin = c.cbegin() + initial_offset;
508 std::vector<u8> data(kip.sections[2].size_compressed); 504 const auto text_end = text_begin + kip.sections[0].size_compressed;
505 const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
509 506
510 u64 offset_sec = sizeof(KIPHeader) + offset; 507 const auto rodata_end = text_end + kip.sections[1].size_compressed;
511 std::memcpy(text.data(), c.data() + offset_sec, text.size()); 508 const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
512 offset_sec += text.size();
513 std::memcpy(rodata.data(), c.data() + offset_sec, rodata.size());
514 offset_sec += rodata.size();
515 std::memcpy(data.data(), c.data() + offset_sec, data.size());
516 509
517 offset += sizeof(KIPHeader) + kip.sections[0].size_compressed + 510 const auto data_end = rodata_end + kip.sections[2].size_compressed;
518 kip.sections[1].size_compressed + kip.sections[2].size_compressed; 511 const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
519 512
520 text = DecompressBLZ(text); 513 std::vector<u8> out;
521 rodata = DecompressBLZ(rodata); 514 out.reserve(text.size() + rodata.size() + data.size());
522 data = DecompressBLZ(data); 515 out.insert(out.end(), text.begin(), text.end());
516 out.insert(out.end(), rodata.begin(), rodata.end());
517 out.insert(out.end(), data.begin(), data.end());
523 518
524 std::vector<u8> out(text.size() + rodata.size() + data.size()); 519 offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
525 std::memcpy(out.data(), text.data(), text.size()); 520 kip.sections[1].size_compressed + kip.sections[2].size_compressed;
526 std::memcpy(out.data() + text.size(), rodata.data(), rodata.size());
527 std::memcpy(out.data() + text.size() + rodata.size(), data.data(), data.size());
528 521
529 if (name == "FS") 522 if (name == "FS")
530 package2_fs[static_cast<size_t>(type)] = out; 523 package2_fs[static_cast<size_t>(type)] = std::move(out);
531 else if (name == "spl") 524 else if (name == "spl")
532 package2_spl[static_cast<size_t>(type)] = out; 525 package2_spl[static_cast<size_t>(type)] = std::move(out);
533 } 526 }
534} 527}
535 528
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h
index 45c7fecfa..0ad007c72 100644
--- a/src/core/crypto/partition_data_manager.h
+++ b/src/core/crypto/partition_data_manager.h
@@ -5,9 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <vector> 7#include <vector>
8#include "common/common_funcs.h"
9#include "common/common_types.h" 8#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/file_sys/vfs_types.h" 9#include "core/file_sys/vfs_types.h"
12 10
13namespace Core::Crypto { 11namespace Core::Crypto {
@@ -24,15 +22,20 @@ enum class Package2Type {
24class PartitionDataManager { 22class PartitionDataManager {
25public: 23public:
26 static const u8 MAX_KEYBLOB_SOURCE_HASH; 24 static const u8 MAX_KEYBLOB_SOURCE_HASH;
25 static constexpr std::size_t NUM_ENCRYPTED_KEYBLOBS = 32;
26 static constexpr std::size_t ENCRYPTED_KEYBLOB_SIZE = 0xB0;
27 27
28 explicit PartitionDataManager(FileSys::VirtualDir sysdata_dir); 28 using EncryptedKeyBlob = std::array<u8, ENCRYPTED_KEYBLOB_SIZE>;
29 using EncryptedKeyBlobs = std::array<EncryptedKeyBlob, NUM_ENCRYPTED_KEYBLOBS>;
30
31 explicit PartitionDataManager(const FileSys::VirtualDir& sysdata_dir);
29 ~PartitionDataManager(); 32 ~PartitionDataManager();
30 33
31 // BOOT0 34 // BOOT0
32 bool HasBoot0() const; 35 bool HasBoot0() const;
33 FileSys::VirtualFile GetBoot0Raw() const; 36 FileSys::VirtualFile GetBoot0Raw() const;
34 std::array<u8, 0xB0> GetEncryptedKeyblob(u8 index) const; 37 EncryptedKeyBlob GetEncryptedKeyblob(std::size_t index) const;
35 std::array<std::array<u8, 0xB0>, 0x20> GetEncryptedKeyblobs() const; 38 EncryptedKeyBlobs GetEncryptedKeyblobs() const;
36 std::vector<u8> GetSecureMonitor() const; 39 std::vector<u8> GetSecureMonitor() const;
37 std::array<u8, 0x10> GetPackage2KeySource() const; 40 std::array<u8, 0x10> GetPackage2KeySource() const;
38 std::array<u8, 0x10> GetAESKekGenerationSource() const; 41 std::array<u8, 0x10> GetAESKekGenerationSource() const;
@@ -43,7 +46,7 @@ public:
43 std::vector<u8> GetPackage1Decrypted() const; 46 std::vector<u8> GetPackage1Decrypted() const;
44 std::array<u8, 0x10> GetMasterKeySource() const; 47 std::array<u8, 0x10> GetMasterKeySource() const;
45 std::array<u8, 0x10> GetKeyblobMACKeySource() const; 48 std::array<u8, 0x10> GetKeyblobMACKeySource() const;
46 std::array<u8, 0x10> GetKeyblobKeySource(u8 revision) const; 49 std::array<u8, 0x10> GetKeyblobKeySource(std::size_t revision) const;
47 50
48 // Fuses 51 // Fuses
49 bool HasFuses() const; 52 bool HasFuses() const;
@@ -57,7 +60,8 @@ public:
57 // Package2 60 // Package2
58 bool HasPackage2(Package2Type type = Package2Type::NormalMain) const; 61 bool HasPackage2(Package2Type type = Package2Type::NormalMain) const;
59 FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const; 62 FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const;
60 void DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2, Package2Type type); 63 void DecryptPackage2(const std::array<std::array<u8, 16>, 0x20>& package2_keys,
64 Package2Type type);
61 const std::vector<u8>& GetPackage2FSDecompressed( 65 const std::vector<u8>& GetPackage2FSDecompressed(
62 Package2Type type = Package2Type::NormalMain) const; 66 Package2Type type = Package2Type::NormalMain) const;
63 std::array<u8, 0x10> GetKeyAreaKeyApplicationSource( 67 std::array<u8, 0x10> GetKeyAreaKeyApplicationSource(
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 6102ef476..76a2b7e86 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -10,19 +10,19 @@ namespace FileSys {
10 10
11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_) 11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), 12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
13 sysnand_cache(std::make_shared<RegisteredCache>( 13 sysnand_cache(std::make_unique<RegisteredCache>(
14 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), 14 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
15 usrnand_cache(std::make_shared<RegisteredCache>( 15 usrnand_cache(std::make_unique<RegisteredCache>(
16 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {} 16 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
17 17
18BISFactory::~BISFactory() = default; 18BISFactory::~BISFactory() = default;
19 19
20std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const { 20RegisteredCache* BISFactory::GetSystemNANDContents() const {
21 return sysnand_cache; 21 return sysnand_cache.get();
22} 22}
23 23
24std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const { 24RegisteredCache* BISFactory::GetUserNANDContents() const {
25 return usrnand_cache; 25 return usrnand_cache.get();
26} 26}
27 27
28VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { 28VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index c352e0925..364d309bd 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -20,8 +20,8 @@ public:
20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root); 20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
21 ~BISFactory(); 21 ~BISFactory();
22 22
23 std::shared_ptr<RegisteredCache> GetSystemNANDContents() const; 23 RegisteredCache* GetSystemNANDContents() const;
24 std::shared_ptr<RegisteredCache> GetUserNANDContents() const; 24 RegisteredCache* GetUserNANDContents() const;
25 25
26 VirtualDir GetModificationLoadRoot(u64 title_id) const; 26 VirtualDir GetModificationLoadRoot(u64 title_id) const;
27 27
@@ -29,8 +29,8 @@ private:
29 VirtualDir nand_root; 29 VirtualDir nand_root;
30 VirtualDir load_root; 30 VirtualDir load_root;
31 31
32 std::shared_ptr<RegisteredCache> sysnand_cache; 32 std::unique_ptr<RegisteredCache> sysnand_cache;
33 std::shared_ptr<RegisteredCache> usrnand_cache; 33 std::unique_ptr<RegisteredCache> usrnand_cache;
34}; 34};
35 35
36} // namespace FileSys 36} // namespace FileSys
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 8f5142a07..ecdd7505b 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -122,14 +122,16 @@ u64 XCI::GetProgramTitleID() const {
122 return secure_partition->GetProgramTitleID(); 122 return secure_partition->GetProgramTitleID();
123} 123}
124 124
125std::shared_ptr<NCA> XCI::GetProgramNCA() const { 125bool XCI::HasProgramNCA() const {
126 return program; 126 return program != nullptr;
127} 127}
128 128
129VirtualFile XCI::GetProgramNCAFile() const { 129VirtualFile XCI::GetProgramNCAFile() const {
130 if (GetProgramNCA() == nullptr) 130 if (!HasProgramNCA()) {
131 return nullptr; 131 return nullptr;
132 return GetProgramNCA()->GetBaseFile(); 132 }
133
134 return program->GetBaseFile();
133} 135}
134 136
135const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const { 137const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index ce514dfa0..48cbef666 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -80,7 +80,7 @@ public:
80 80
81 u64 GetProgramTitleID() const; 81 u64 GetProgramTitleID() const;
82 82
83 std::shared_ptr<NCA> GetProgramNCA() const; 83 bool HasProgramNCA() const;
84 VirtualFile GetProgramNCAFile() const; 84 VirtualFile GetProgramNCAFile() const;
85 const std::vector<std::shared_ptr<NCA>>& GetNCAs() const; 85 const std::vector<std::shared_ptr<NCA>>& GetNCAs() const;
86 std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; 86 std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index aa1b3c17d..6c356d85d 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -97,11 +97,288 @@ union NCASectionHeader {
97}; 97};
98static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); 98static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
99 99
100bool IsValidNCA(const NCAHeader& header) { 100static bool IsValidNCA(const NCAHeader& header) {
101 // TODO(DarkLordZach): Add NCA2/NCA0 support. 101 // TODO(DarkLordZach): Add NCA2/NCA0 support.
102 return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); 102 return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
103} 103}
104 104
105NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
106 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
107 if (file == nullptr) {
108 status = Loader::ResultStatus::ErrorNullFile;
109 return;
110 }
111
112 if (sizeof(NCAHeader) != file->ReadObject(&header)) {
113 LOG_ERROR(Loader, "File reader errored out during header read.");
114 status = Loader::ResultStatus::ErrorBadNCAHeader;
115 return;
116 }
117
118 if (!HandlePotentialHeaderDecryption()) {
119 return;
120 }
121
122 has_rights_id = std::any_of(header.rights_id.begin(), header.rights_id.end(),
123 [](char c) { return c != '\0'; });
124
125 const std::vector<NCASectionHeader> sections = ReadSectionHeaders();
126 is_update = std::any_of(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
127 return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
128 });
129
130 if (!ReadSections(sections, bktr_base_ivfc_offset)) {
131 return;
132 }
133
134 status = Loader::ResultStatus::Success;
135}
136
137NCA::~NCA() = default;
138
139bool NCA::CheckSupportedNCA(const NCAHeader& nca_header) {
140 if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
141 status = Loader::ResultStatus::ErrorNCA2;
142 return false;
143 }
144
145 if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
146 status = Loader::ResultStatus::ErrorNCA0;
147 return false;
148 }
149
150 return true;
151}
152
153bool NCA::HandlePotentialHeaderDecryption() {
154 if (IsValidNCA(header)) {
155 return true;
156 }
157
158 if (!CheckSupportedNCA(header)) {
159 return false;
160 }
161
162 NCAHeader dec_header{};
163 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
164 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
165 cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
166 Core::Crypto::Op::Decrypt);
167 if (IsValidNCA(dec_header)) {
168 header = dec_header;
169 encrypted = true;
170 } else {
171 if (!CheckSupportedNCA(dec_header)) {
172 return false;
173 }
174
175 if (keys.HasKey(Core::Crypto::S256KeyType::Header)) {
176 status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
177 } else {
178 status = Loader::ResultStatus::ErrorMissingHeaderKey;
179 }
180 return false;
181 }
182
183 return true;
184}
185
186std::vector<NCASectionHeader> NCA::ReadSectionHeaders() const {
187 const std::ptrdiff_t number_sections =
188 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
189 [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
190
191 std::vector<NCASectionHeader> sections(number_sections);
192 const auto length_sections = SECTION_HEADER_SIZE * number_sections;
193
194 if (encrypted) {
195 auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
196 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
197 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
198 cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
199 Core::Crypto::Op::Decrypt);
200 } else {
201 file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
202 }
203
204 return sections;
205}
206
207bool NCA::ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset) {
208 for (std::size_t i = 0; i < sections.size(); ++i) {
209 const auto& section = sections[i];
210
211 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
212 if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) {
213 return false;
214 }
215 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
216 if (!ReadPFS0Section(section, header.section_tables[i])) {
217 return false;
218 }
219 }
220 }
221
222 return true;
223}
224
225bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
226 u64 bktr_base_ivfc_offset) {
227 const std::size_t base_offset = entry.media_offset * MEDIA_OFFSET_MULTIPLIER;
228 ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
229 const std::size_t romfs_offset = base_offset + ivfc_offset;
230 const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
231 auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
232 auto dec = Decrypt(section, raw, romfs_offset);
233
234 if (dec == nullptr) {
235 if (status != Loader::ResultStatus::Success)
236 return false;
237 if (has_rights_id)
238 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
239 else
240 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
241 return false;
242 }
243
244 if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
245 if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
246 section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
247 status = Loader::ResultStatus::ErrorBadBKTRHeader;
248 return false;
249 }
250
251 if (section.bktr.relocation.offset + section.bktr.relocation.size !=
252 section.bktr.subsection.offset) {
253 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
254 return false;
255 }
256
257 const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
258 if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
259 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
260 return false;
261 }
262
263 const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
264 RelocationBlock relocation_block{};
265 if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
266 sizeof(RelocationBlock)) {
267 status = Loader::ResultStatus::ErrorBadRelocationBlock;
268 return false;
269 }
270 SubsectionBlock subsection_block{};
271 if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
272 sizeof(RelocationBlock)) {
273 status = Loader::ResultStatus::ErrorBadSubsectionBlock;
274 return false;
275 }
276
277 std::vector<RelocationBucketRaw> relocation_buckets_raw(
278 (section.bktr.relocation.size - sizeof(RelocationBlock)) / sizeof(RelocationBucketRaw));
279 if (dec->ReadBytes(relocation_buckets_raw.data(),
280 section.bktr.relocation.size - sizeof(RelocationBlock),
281 section.bktr.relocation.offset + sizeof(RelocationBlock) - offset) !=
282 section.bktr.relocation.size - sizeof(RelocationBlock)) {
283 status = Loader::ResultStatus::ErrorBadRelocationBuckets;
284 return false;
285 }
286
287 std::vector<SubsectionBucketRaw> subsection_buckets_raw(
288 (section.bktr.subsection.size - sizeof(SubsectionBlock)) / sizeof(SubsectionBucketRaw));
289 if (dec->ReadBytes(subsection_buckets_raw.data(),
290 section.bktr.subsection.size - sizeof(SubsectionBlock),
291 section.bktr.subsection.offset + sizeof(SubsectionBlock) - offset) !=
292 section.bktr.subsection.size - sizeof(SubsectionBlock)) {
293 status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
294 return false;
295 }
296
297 std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
298 std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
299 relocation_buckets.begin(), &ConvertRelocationBucketRaw);
300 std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
301 std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
302 subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
303
304 u32 ctr_low;
305 std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
306 subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
307 subsection_buckets.back().entries.push_back({size, {0}, 0});
308
309 boost::optional<Core::Crypto::Key128> key = boost::none;
310 if (encrypted) {
311 if (has_rights_id) {
312 status = Loader::ResultStatus::Success;
313 key = GetTitlekey();
314 if (key == boost::none) {
315 status = Loader::ResultStatus::ErrorMissingTitlekey;
316 return false;
317 }
318 } else {
319 key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
320 if (key == boost::none) {
321 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
322 return false;
323 }
324 }
325 }
326
327 if (bktr_base_romfs == nullptr) {
328 status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
329 return false;
330 }
331
332 auto bktr = std::make_shared<BKTR>(
333 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
334 relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted,
335 encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
336 section.raw.section_ctr);
337
338 // BKTR applies to entire IVFC, so make an offset version to level 6
339 files.push_back(std::make_shared<OffsetVfsFile>(
340 bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
341 } else {
342 files.push_back(std::move(dec));
343 }
344
345 romfs = files.back();
346 return true;
347}
348
349bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry) {
350 const u64 offset = (static_cast<u64>(entry.media_offset) * MEDIA_OFFSET_MULTIPLIER) +
351 section.pfs0.pfs0_header_offset;
352 const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
353
354 auto dec = Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
355 if (dec != nullptr) {
356 auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
357
358 if (npfs->GetStatus() == Loader::ResultStatus::Success) {
359 dirs.push_back(std::move(npfs));
360 if (IsDirectoryExeFS(dirs.back()))
361 exefs = dirs.back();
362 } else {
363 if (has_rights_id)
364 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
365 else
366 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
367 return false;
368 }
369 } else {
370 if (status != Loader::ResultStatus::Success)
371 return false;
372 if (has_rights_id)
373 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
374 else
375 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
376 return false;
377 }
378
379 return true;
380}
381
105u8 NCA::GetCryptoRevision() const { 382u8 NCA::GetCryptoRevision() const {
106 u8 master_key_id = header.crypto_type; 383 u8 master_key_id = header.crypto_type;
107 if (header.crypto_type_2 > master_key_id) 384 if (header.crypto_type_2 > master_key_id)
@@ -133,7 +410,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
133 static_cast<u8>(type)); 410 static_cast<u8>(type));
134 u128 out_128{}; 411 u128 out_128{};
135 memcpy(out_128.data(), out.data(), 16); 412 memcpy(out_128.data(), out.data(), 16);
136 LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}", 413 LOG_TRACE(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",
137 master_key_id, header.key_index, out_128[1], out_128[0]); 414 master_key_id, header.key_index, out_128[1], out_128[0]);
138 415
139 return out; 416 return out;
@@ -167,7 +444,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
167 return titlekey; 444 return titlekey;
168} 445}
169 446
170VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) { 447VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 starting_offset) {
171 if (!encrypted) 448 if (!encrypted)
172 return in; 449 return in;
173 450
@@ -215,256 +492,6 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
215 } 492 }
216} 493}
217 494
218NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
219 : file(std::move(file_)),
220 bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) {
221 status = Loader::ResultStatus::Success;
222
223 if (file == nullptr) {
224 status = Loader::ResultStatus::ErrorNullFile;
225 return;
226 }
227
228 if (sizeof(NCAHeader) != file->ReadObject(&header)) {
229 LOG_ERROR(Loader, "File reader errored out during header read.");
230 status = Loader::ResultStatus::ErrorBadNCAHeader;
231 return;
232 }
233
234 encrypted = false;
235
236 if (!IsValidNCA(header)) {
237 if (header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
238 status = Loader::ResultStatus::ErrorNCA2;
239 return;
240 }
241 if (header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
242 status = Loader::ResultStatus::ErrorNCA0;
243 return;
244 }
245
246 NCAHeader dec_header{};
247 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
248 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
249 cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
250 Core::Crypto::Op::Decrypt);
251 if (IsValidNCA(dec_header)) {
252 header = dec_header;
253 encrypted = true;
254 } else {
255 if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
256 status = Loader::ResultStatus::ErrorNCA2;
257 return;
258 }
259 if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
260 status = Loader::ResultStatus::ErrorNCA0;
261 return;
262 }
263
264 if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
265 status = Loader::ResultStatus::ErrorMissingHeaderKey;
266 else
267 status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
268 return;
269 }
270 }
271
272 has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
273 [](char c) { return c == '\0'; }) != header.rights_id.end();
274
275 const std::ptrdiff_t number_sections =
276 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
277 [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
278
279 std::vector<NCASectionHeader> sections(number_sections);
280 const auto length_sections = SECTION_HEADER_SIZE * number_sections;
281
282 if (encrypted) {
283 auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
284 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
285 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
286 cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
287 Core::Crypto::Op::Decrypt);
288 } else {
289 file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
290 }
291
292 is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
293 return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
294 }) != sections.end();
295 ivfc_offset = 0;
296
297 for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
298 auto section = sections[i];
299
300 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
301 const std::size_t base_offset =
302 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
303 ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
304 const std::size_t romfs_offset = base_offset + ivfc_offset;
305 const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
306 auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
307 auto dec = Decrypt(section, raw, romfs_offset);
308
309 if (dec == nullptr) {
310 if (status != Loader::ResultStatus::Success)
311 return;
312 if (has_rights_id)
313 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
314 else
315 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
316 return;
317 }
318
319 if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
320 if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
321 section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
322 status = Loader::ResultStatus::ErrorBadBKTRHeader;
323 return;
324 }
325
326 if (section.bktr.relocation.offset + section.bktr.relocation.size !=
327 section.bktr.subsection.offset) {
328 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
329 return;
330 }
331
332 const u64 size =
333 MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
334 header.section_tables[i].media_offset);
335 if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
336 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
337 return;
338 }
339
340 const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
341 RelocationBlock relocation_block{};
342 if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
343 sizeof(RelocationBlock)) {
344 status = Loader::ResultStatus::ErrorBadRelocationBlock;
345 return;
346 }
347 SubsectionBlock subsection_block{};
348 if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
349 sizeof(RelocationBlock)) {
350 status = Loader::ResultStatus::ErrorBadSubsectionBlock;
351 return;
352 }
353
354 std::vector<RelocationBucketRaw> relocation_buckets_raw(
355 (section.bktr.relocation.size - sizeof(RelocationBlock)) /
356 sizeof(RelocationBucketRaw));
357 if (dec->ReadBytes(relocation_buckets_raw.data(),
358 section.bktr.relocation.size - sizeof(RelocationBlock),
359 section.bktr.relocation.offset + sizeof(RelocationBlock) -
360 offset) !=
361 section.bktr.relocation.size - sizeof(RelocationBlock)) {
362 status = Loader::ResultStatus::ErrorBadRelocationBuckets;
363 return;
364 }
365
366 std::vector<SubsectionBucketRaw> subsection_buckets_raw(
367 (section.bktr.subsection.size - sizeof(SubsectionBlock)) /
368 sizeof(SubsectionBucketRaw));
369 if (dec->ReadBytes(subsection_buckets_raw.data(),
370 section.bktr.subsection.size - sizeof(SubsectionBlock),
371 section.bktr.subsection.offset + sizeof(SubsectionBlock) -
372 offset) !=
373 section.bktr.subsection.size - sizeof(SubsectionBlock)) {
374 status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
375 return;
376 }
377
378 std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
379 std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
380 relocation_buckets.begin(), &ConvertRelocationBucketRaw);
381 std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
382 std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
383 subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
384
385 u32 ctr_low;
386 std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
387 subsection_buckets.back().entries.push_back(
388 {section.bktr.relocation.offset, {0}, ctr_low});
389 subsection_buckets.back().entries.push_back({size, {0}, 0});
390
391 boost::optional<Core::Crypto::Key128> key = boost::none;
392 if (encrypted) {
393 if (has_rights_id) {
394 status = Loader::ResultStatus::Success;
395 key = GetTitlekey();
396 if (key == boost::none) {
397 status = Loader::ResultStatus::ErrorMissingTitlekey;
398 return;
399 }
400 } else {
401 key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
402 if (key == boost::none) {
403 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
404 return;
405 }
406 }
407 }
408
409 if (bktr_base_romfs == nullptr) {
410 status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
411 return;
412 }
413
414 auto bktr = std::make_shared<BKTR>(
415 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
416 relocation_block, relocation_buckets, subsection_block, subsection_buckets,
417 encrypted, encrypted ? key.get() : Core::Crypto::Key128{}, base_offset,
418 bktr_base_ivfc_offset, section.raw.section_ctr);
419
420 // BKTR applies to entire IVFC, so make an offset version to level 6
421
422 files.push_back(std::make_shared<OffsetVfsFile>(
423 bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
424 romfs = files.back();
425 } else {
426 files.push_back(std::move(dec));
427 romfs = files.back();
428 }
429 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
430 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
431 MEDIA_OFFSET_MULTIPLIER) +
432 section.pfs0.pfs0_header_offset;
433 u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
434 header.section_tables[i].media_offset);
435 auto dec =
436 Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
437 if (dec != nullptr) {
438 auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
439
440 if (npfs->GetStatus() == Loader::ResultStatus::Success) {
441 dirs.push_back(std::move(npfs));
442 if (IsDirectoryExeFS(dirs.back()))
443 exefs = dirs.back();
444 } else {
445 if (has_rights_id)
446 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
447 else
448 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
449 return;
450 }
451 } else {
452 if (status != Loader::ResultStatus::Success)
453 return;
454 if (has_rights_id)
455 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
456 else
457 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
458 return;
459 }
460 }
461 }
462
463 status = Loader::ResultStatus::Success;
464}
465
466NCA::~NCA() = default;
467
468Loader::ResultStatus NCA::GetStatus() const { 495Loader::ResultStatus NCA::GetStatus() const {
469 return status; 496 return status;
470} 497}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index f9f66cae9..1c903cd3f 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -73,8 +73,6 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
73 return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; 73 return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
74} 74}
75 75
76bool IsValidNCA(const NCAHeader& header);
77
78// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. 76// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
79// After construction, use GetStatus to determine if the file is valid and ready to be used. 77// After construction, use GetStatus to determine if the file is valid and ready to be used.
80class NCA : public ReadOnlyVfsDirectory { 78class NCA : public ReadOnlyVfsDirectory {
@@ -106,10 +104,19 @@ protected:
106 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 104 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
107 105
108private: 106private:
107 bool CheckSupportedNCA(const NCAHeader& header);
108 bool HandlePotentialHeaderDecryption();
109
110 std::vector<NCASectionHeader> ReadSectionHeaders() const;
111 bool ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset);
112 bool ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
113 u64 bktr_base_ivfc_offset);
114 bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry);
115
109 u8 GetCryptoRevision() const; 116 u8 GetCryptoRevision() const;
110 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; 117 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
111 boost::optional<Core::Crypto::Key128> GetTitlekey(); 118 boost::optional<Core::Crypto::Key128> GetTitlekey();
112 VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset); 119 VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset);
113 120
114 std::vector<VirtualDir> dirs; 121 std::vector<VirtualDir> dirs;
115 std::vector<VirtualFile> files; 122 std::vector<VirtualFile> files;
@@ -118,15 +125,15 @@ private:
118 VirtualDir exefs = nullptr; 125 VirtualDir exefs = nullptr;
119 VirtualFile file; 126 VirtualFile file;
120 VirtualFile bktr_base_romfs; 127 VirtualFile bktr_base_romfs;
121 u64 ivfc_offset; 128 u64 ivfc_offset = 0;
122 129
123 NCAHeader header{}; 130 NCAHeader header{};
124 bool has_rights_id{}; 131 bool has_rights_id{};
125 132
126 Loader::ResultStatus status{}; 133 Loader::ResultStatus status{};
127 134
128 bool encrypted; 135 bool encrypted = false;
129 bool is_update; 136 bool is_update = false;
130 137
131 Core::Crypto::KeyManager keys; 138 Core::Crypto::KeyManager keys;
132}; 139};
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 5b1177a03..a012c2be9 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -17,11 +17,13 @@ const std::array<const char*, 15> LANGUAGE_NAMES = {
17}; 17};
18 18
19std::string LanguageEntry::GetApplicationName() const { 19std::string LanguageEntry::GetApplicationName() const {
20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200); 20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(),
21 application_name.size());
21} 22}
22 23
23std::string LanguageEntry::GetDeveloperName() const { 24std::string LanguageEntry::GetDeveloperName() const {
24 return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100); 25 return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(),
26 developer_name.size());
25} 27}
26 28
27NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) { 29NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
@@ -56,7 +58,12 @@ u64 NACP::GetTitleId() const {
56 return raw->title_id; 58 return raw->title_id;
57} 59}
58 60
61u64 NACP::GetDLCBaseTitleId() const {
62 return raw->dlc_base_title_id;
63}
64
59std::string NACP::GetVersionString() const { 65std::string NACP::GetVersionString() const {
60 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 0x10); 66 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
67 raw->version_string.size());
61} 68}
62} // namespace FileSys 69} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 43d6f0719..141f7e056 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -79,6 +79,7 @@ public:
79 std::string GetApplicationName(Language language = Language::Default) const; 79 std::string GetApplicationName(Language language = Language::Default) const;
80 std::string GetDeveloperName(Language language = Language::Default) const; 80 std::string GetDeveloperName(Language language = Language::Default) const;
81 u64 GetTitleId() const; 81 u64 GetTitleId() const;
82 u64 GetDLCBaseTitleId() const;
82 std::string GetVersionString() const; 83 std::string GetVersionString() const;
83 84
84private: 85private:
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 019caebe9..0117cb0bf 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -214,8 +214,14 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
214 214
215VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type, 215VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type,
216 VirtualFile update_raw) const { 216 VirtualFile update_raw) const {
217 LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id, 217 const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}",
218 static_cast<u8>(type)); 218 title_id, static_cast<u8>(type))
219 .c_str();
220
221 if (type == ContentRecordType::Program)
222 LOG_INFO(Loader, log_string);
223 else
224 LOG_DEBUG(Loader, log_string);
219 225
220 if (romfs == nullptr) 226 if (romfs == nullptr)
221 return romfs; 227 return romfs;
@@ -346,7 +352,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
346} 352}
347 353
348std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { 354std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
349 const auto& installed{Service::FileSystem::GetUnionContents()}; 355 const auto installed{Service::FileSystem::GetUnionContents()};
350 356
351 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); 357 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
352 if (base_control_nca == nullptr) 358 if (base_control_nca == nullptr)
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index e9b040689..1febb398e 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -308,14 +308,14 @@ VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
308 return GetEntryRaw(entry.title_id, entry.type); 308 return GetEntryRaw(entry.title_id, entry.type);
309} 309}
310 310
311std::shared_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const { 311std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
312 const auto raw = GetEntryRaw(title_id, type); 312 const auto raw = GetEntryRaw(title_id, type);
313 if (raw == nullptr) 313 if (raw == nullptr)
314 return nullptr; 314 return nullptr;
315 return std::make_shared<NCA>(raw); 315 return std::make_unique<NCA>(raw);
316} 316}
317 317
318std::shared_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const { 318std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
319 return GetEntry(entry.title_id, entry.type); 319 return GetEntry(entry.title_id, entry.type);
320} 320}
321 321
@@ -516,7 +516,7 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
516 }) != yuzu_meta.end(); 516 }) != yuzu_meta.end();
517} 517}
518 518
519RegisteredCacheUnion::RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches) 519RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches)
520 : caches(std::move(caches)) {} 520 : caches(std::move(caches)) {}
521 521
522void RegisteredCacheUnion::Refresh() { 522void RegisteredCacheUnion::Refresh() {
@@ -572,14 +572,14 @@ VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const
572 return GetEntryRaw(entry.title_id, entry.type); 572 return GetEntryRaw(entry.title_id, entry.type);
573} 573}
574 574
575std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const { 575std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
576 const auto raw = GetEntryRaw(title_id, type); 576 const auto raw = GetEntryRaw(title_id, type);
577 if (raw == nullptr) 577 if (raw == nullptr)
578 return nullptr; 578 return nullptr;
579 return std::make_shared<NCA>(raw); 579 return std::make_unique<NCA>(raw);
580} 580}
581 581
582std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const { 582std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
583 return GetEntry(entry.title_id, entry.type); 583 return GetEntry(entry.title_id, entry.type);
584} 584}
585 585
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index c0cd59fc5..5ddacba47 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -88,8 +88,8 @@ public:
88 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 88 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
89 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; 89 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
90 90
91 std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 91 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
92 std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; 92 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
93 93
94 std::vector<RegisteredCacheEntry> ListEntries() const; 94 std::vector<RegisteredCacheEntry> ListEntries() const;
95 // If a parameter is not boost::none, it will be filtered for from all entries. 95 // If a parameter is not boost::none, it will be filtered for from all entries.
@@ -142,7 +142,7 @@ private:
142// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface. 142// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.
143class RegisteredCacheUnion { 143class RegisteredCacheUnion {
144public: 144public:
145 explicit RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches); 145 explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches);
146 146
147 void Refresh(); 147 void Refresh();
148 148
@@ -157,8 +157,8 @@ public:
157 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 157 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
158 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; 158 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
159 159
160 std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 160 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
161 std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; 161 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
162 162
163 std::vector<RegisteredCacheEntry> ListEntries() const; 163 std::vector<RegisteredCacheEntry> ListEntries() const;
164 // If a parameter is not boost::none, it will be filtered for from all entries. 164 // If a parameter is not boost::none, it will be filtered for from all entries.
@@ -168,7 +168,7 @@ public:
168 boost::optional<u64> title_id = boost::none) const; 168 boost::optional<u64> title_id = boost::none) const;
169 169
170private: 170private:
171 std::vector<std::shared_ptr<RegisteredCache>> caches; 171 std::vector<RegisteredCache*> caches;
172}; 172};
173 173
174} // namespace FileSys 174} // namespace FileSys
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 47f2ab9e0..ef1aaebbb 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -51,6 +51,13 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
51 meta.title_id); 51 meta.title_id);
52 } 52 }
53 53
54 if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) {
55 LOG_WARNING(Service_FS,
56 "Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is "
57 "non-zero ({:016X}{:016X})",
58 meta.user_id[1], meta.user_id[0]);
59 }
60
54 std::string save_directory = 61 std::string save_directory =
55 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); 62 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
56 63
@@ -92,6 +99,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
92 case SaveDataSpaceId::NandUser: 99 case SaveDataSpaceId::NandUser:
93 out = "/user/"; 100 out = "/user/";
94 break; 101 break;
102 case SaveDataSpaceId::TemporaryStorage:
103 out = "/temp/";
104 break;
95 default: 105 default:
96 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); 106 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
97 } 107 }
@@ -100,10 +110,11 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
100 case SaveDataType::SystemSaveData: 110 case SaveDataType::SystemSaveData:
101 return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); 111 return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
102 case SaveDataType::SaveData: 112 case SaveDataType::SaveData:
113 case SaveDataType::DeviceSaveData:
103 return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], 114 return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
104 title_id); 115 title_id);
105 case SaveDataType::TemporaryStorage: 116 case SaveDataType::TemporaryStorage:
106 return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], 117 return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
107 title_id); 118 title_id);
108 default: 119 default:
109 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); 120 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index d66a9c9a4..bd3a57058 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -10,10 +10,10 @@
10namespace FileSys { 10namespace FileSys {
11 11
12SDMCFactory::SDMCFactory(VirtualDir dir_) 12SDMCFactory::SDMCFactory(VirtualDir dir_)
13 : dir(std::move(dir_)), contents(std::make_shared<RegisteredCache>( 13 : dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>(
14 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), 14 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
15 [](const VirtualFile& file, const NcaID& id) { 15 [](const VirtualFile& file, const NcaID& id) {
16 return std::make_shared<NAX>(file, id)->GetDecrypted(); 16 return NAX{file, id}.GetDecrypted();
17 })) {} 17 })) {}
18 18
19SDMCFactory::~SDMCFactory() = default; 19SDMCFactory::~SDMCFactory() = default;
@@ -22,8 +22,8 @@ ResultVal<VirtualDir> SDMCFactory::Open() {
22 return MakeResult<VirtualDir>(dir); 22 return MakeResult<VirtualDir>(dir);
23} 23}
24 24
25std::shared_ptr<RegisteredCache> SDMCFactory::GetSDMCContents() const { 25RegisteredCache* SDMCFactory::GetSDMCContents() const {
26 return contents; 26 return contents.get();
27} 27}
28 28
29} // namespace FileSys 29} // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index ea12149de..42794ba5b 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -19,12 +19,12 @@ public:
19 ~SDMCFactory(); 19 ~SDMCFactory();
20 20
21 ResultVal<VirtualDir> Open(); 21 ResultVal<VirtualDir> Open();
22 std::shared_ptr<RegisteredCache> GetSDMCContents() const; 22 RegisteredCache* GetSDMCContents() const;
23 23
24private: 24private:
25 VirtualDir dir; 25 VirtualDir dir;
26 26
27 std::shared_ptr<RegisteredCache> contents; 27 std::unique_ptr<RegisteredCache> contents;
28}; 28};
29 29
30} // namespace FileSys 30} // namespace FileSys
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index e961ef121..bdcc889e0 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -207,7 +207,7 @@ void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
207 207
208static Kernel::Thread* FindThreadById(int id) { 208static Kernel::Thread* FindThreadById(int id) {
209 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 209 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
210 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); 210 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
211 for (auto& thread : threads) { 211 for (auto& thread : threads) {
212 if (thread->GetThreadID() == static_cast<u32>(id)) { 212 if (thread->GetThreadID() == static_cast<u32>(id)) {
213 current_core = core; 213 current_core = core;
@@ -597,7 +597,7 @@ static void HandleQuery() {
597 } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { 597 } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
598 std::string val = "m"; 598 std::string val = "m";
599 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 599 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
600 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); 600 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
601 for (const auto& thread : threads) { 601 for (const auto& thread : threads) {
602 val += fmt::format("{:x}", thread->GetThreadID()); 602 val += fmt::format("{:x}", thread->GetThreadID());
603 val += ","; 603 val += ",";
@@ -612,7 +612,7 @@ static void HandleQuery() {
612 buffer += "l<?xml version=\"1.0\"?>"; 612 buffer += "l<?xml version=\"1.0\"?>";
613 buffer += "<threads>"; 613 buffer += "<threads>";
614 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 614 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
615 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); 615 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
616 for (const auto& thread : threads) { 616 for (const auto& thread : threads) {
617 buffer += 617 buffer +=
618 fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*", 618 fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index ebf193930..57157beb4 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -39,7 +39,7 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)
39 std::vector<SharedPtr<Thread>>& waiting_threads, 39 std::vector<SharedPtr<Thread>>& waiting_threads,
40 VAddr arb_addr) { 40 VAddr arb_addr) {
41 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 41 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
42 const auto& thread_list = scheduler->GetThreadList(); 42 const auto& thread_list = scheduler.GetThreadList();
43 43
44 for (const auto& thread : thread_list) { 44 for (const auto& thread : thread_list) {
45 if (thread->GetArbiterWaitAddress() == arb_addr) 45 if (thread->GetArbiterWaitAddress() == arb_addr)
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index c80b2c507..073dd5a7d 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -153,11 +153,11 @@ void Process::PrepareForTermination() {
153 } 153 }
154 }; 154 };
155 155
156 auto& system = Core::System::GetInstance(); 156 const auto& system = Core::System::GetInstance();
157 stop_threads(system.Scheduler(0)->GetThreadList()); 157 stop_threads(system.Scheduler(0).GetThreadList());
158 stop_threads(system.Scheduler(1)->GetThreadList()); 158 stop_threads(system.Scheduler(1).GetThreadList());
159 stop_threads(system.Scheduler(2)->GetThreadList()); 159 stop_threads(system.Scheduler(2).GetThreadList());
160 stop_threads(system.Scheduler(3)->GetThreadList()); 160 stop_threads(system.Scheduler(3).GetThreadList());
161} 161}
162 162
163/** 163/**
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 73ec01e11..f2816943a 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -24,6 +24,7 @@ class ProgramMetadata;
24namespace Kernel { 24namespace Kernel {
25 25
26class KernelCore; 26class KernelCore;
27class ResourceLimit;
27 28
28struct AddressMapping { 29struct AddressMapping {
29 // Address and size must be page-aligned 30 // Address and size must be page-aligned
@@ -57,9 +58,23 @@ union ProcessFlags {
57 BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000). 58 BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
58}; 59};
59 60
60enum class ProcessStatus { Created, Running, Exited }; 61/**
61 62 * Indicates the status of a Process instance.
62class ResourceLimit; 63 *
64 * @note These match the values as used by kernel,
65 * so new entries should only be added if RE
66 * shows that a new value has been introduced.
67 */
68enum class ProcessStatus {
69 Created,
70 CreatedWithDebuggerAttached,
71 Running,
72 WaitingForDebuggerToAttach,
73 DebuggerAttached,
74 Exiting,
75 Exited,
76 DebugBreak,
77};
63 78
64struct CodeSet final { 79struct CodeSet final {
65 struct Segment { 80 struct Segment {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index e406df829..3b8a2e230 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -8,6 +8,7 @@
8#include <mutex> 8#include <mutex>
9#include <vector> 9#include <vector>
10 10
11#include "common/alignment.h"
11#include "common/assert.h" 12#include "common/assert.h"
12#include "common/logging/log.h" 13#include "common/logging/log.h"
13#include "common/microprofile.h" 14#include "common/microprofile.h"
@@ -36,9 +37,6 @@
36 37
37namespace Kernel { 38namespace Kernel {
38namespace { 39namespace {
39constexpr bool Is4KBAligned(VAddr address) {
40 return (address & 0xFFF) == 0;
41}
42 40
43// Checks if address + size is greater than the given address 41// Checks if address + size is greater than the given address
44// This can return false if the size causes an overflow of a 64-bit type 42// This can return false if the size causes an overflow of a 64-bit type
@@ -69,11 +67,11 @@ bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
69// in the same order. 67// in the same order.
70ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr, 68ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr,
71 u64 size) { 69 u64 size) {
72 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { 70 if (!Common::Is4KBAligned(dst_addr) || !Common::Is4KBAligned(src_addr)) {
73 return ERR_INVALID_ADDRESS; 71 return ERR_INVALID_ADDRESS;
74 } 72 }
75 73
76 if (size == 0 || !Is4KBAligned(size)) { 74 if (size == 0 || !Common::Is4KBAligned(size)) {
77 return ERR_INVALID_SIZE; 75 return ERR_INVALID_SIZE;
78 } 76 }
79 77
@@ -352,6 +350,10 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
352 return ERR_INVALID_ADDRESS_STATE; 350 return ERR_INVALID_ADDRESS_STATE;
353 } 351 }
354 352
353 if (!Common::IsWordAligned(mutex_addr)) {
354 return ERR_INVALID_ADDRESS;
355 }
356
355 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); 357 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
356 return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, 358 return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
357 requesting_thread_handle); 359 requesting_thread_handle);
@@ -365,6 +367,10 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
365 return ERR_INVALID_ADDRESS_STATE; 367 return ERR_INVALID_ADDRESS_STATE;
366 } 368 }
367 369
370 if (!Common::IsWordAligned(mutex_addr)) {
371 return ERR_INVALID_ADDRESS;
372 }
373
368 return Mutex::Release(mutex_addr); 374 return Mutex::Release(mutex_addr);
369} 375}
370 376
@@ -389,6 +395,12 @@ static void Break(u32 reason, u64 info1, u64 info2) {
389 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 395 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
390 reason, info1, info2); 396 reason, info1, info2);
391 ASSERT(false); 397 ASSERT(false);
398
399 Core::CurrentProcess()->PrepareForTermination();
400
401 // Kill the current thread
402 GetCurrentThread()->Stop();
403 Core::System::GetInstance().PrepareReschedule();
392 } 404 }
393} 405}
394 406
@@ -442,25 +454,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
442 case GetInfoType::RandomEntropy: 454 case GetInfoType::RandomEntropy:
443 *result = 0; 455 *result = 0;
444 break; 456 break;
445 case GetInfoType::AddressSpaceBaseAddr: 457 case GetInfoType::ASLRRegionBaseAddr:
446 *result = vm_manager.GetCodeRegionBaseAddress(); 458 *result = vm_manager.GetASLRRegionBaseAddress();
447 break; 459 break;
448 case GetInfoType::AddressSpaceSize: { 460 case GetInfoType::ASLRRegionSize:
449 const u64 width = vm_manager.GetAddressSpaceWidth(); 461 *result = vm_manager.GetASLRRegionSize();
450
451 switch (width) {
452 case 32:
453 *result = 0xFFE00000;
454 break;
455 case 36:
456 *result = 0xFF8000000;
457 break;
458 case 39:
459 *result = 0x7FF8000000;
460 break;
461 }
462 break; 462 break;
463 }
464 case GetInfoType::NewMapRegionBaseAddr: 463 case GetInfoType::NewMapRegionBaseAddr:
465 *result = vm_manager.GetNewMapRegionBaseAddress(); 464 *result = vm_manager.GetNewMapRegionBaseAddress();
466 break; 465 break;
@@ -577,14 +576,18 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
577 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", 576 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
578 shared_memory_handle, addr, size, permissions); 577 shared_memory_handle, addr, size, permissions);
579 578
580 if (!Is4KBAligned(addr)) { 579 if (!Common::Is4KBAligned(addr)) {
581 return ERR_INVALID_ADDRESS; 580 return ERR_INVALID_ADDRESS;
582 } 581 }
583 582
584 if (size == 0 || !Is4KBAligned(size)) { 583 if (size == 0 || !Common::Is4KBAligned(size)) {
585 return ERR_INVALID_SIZE; 584 return ERR_INVALID_SIZE;
586 } 585 }
587 586
587 if (!IsValidAddressRange(addr, size)) {
588 return ERR_INVALID_ADDRESS_STATE;
589 }
590
588 const auto permissions_type = static_cast<MemoryPermission>(permissions); 591 const auto permissions_type = static_cast<MemoryPermission>(permissions);
589 if (permissions_type != MemoryPermission::Read && 592 if (permissions_type != MemoryPermission::Read &&
590 permissions_type != MemoryPermission::ReadWrite) { 593 permissions_type != MemoryPermission::ReadWrite) {
@@ -598,26 +601,46 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
598 return ERR_INVALID_HANDLE; 601 return ERR_INVALID_HANDLE;
599 } 602 }
600 603
601 return shared_memory->Map(Core::CurrentProcess(), addr, permissions_type, 604 auto* const current_process = Core::CurrentProcess();
602 MemoryPermission::DontCare); 605 const auto& vm_manager = current_process->VMManager();
606
607 if (!vm_manager.IsWithinASLRRegion(addr, size)) {
608 return ERR_INVALID_MEMORY_RANGE;
609 }
610
611 return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare);
603} 612}
604 613
605static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { 614static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
606 LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", 615 LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}",
607 shared_memory_handle, addr, size); 616 shared_memory_handle, addr, size);
608 617
609 if (!Is4KBAligned(addr)) { 618 if (!Common::Is4KBAligned(addr)) {
610 return ERR_INVALID_ADDRESS; 619 return ERR_INVALID_ADDRESS;
611 } 620 }
612 621
613 if (size == 0 || !Is4KBAligned(size)) { 622 if (size == 0 || !Common::Is4KBAligned(size)) {
614 return ERR_INVALID_SIZE; 623 return ERR_INVALID_SIZE;
615 } 624 }
616 625
626 if (!IsValidAddressRange(addr, size)) {
627 return ERR_INVALID_ADDRESS_STATE;
628 }
629
617 auto& kernel = Core::System::GetInstance().Kernel(); 630 auto& kernel = Core::System::GetInstance().Kernel();
618 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 631 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
632 if (!shared_memory) {
633 return ERR_INVALID_HANDLE;
634 }
635
636 auto* const current_process = Core::CurrentProcess();
637 const auto& vm_manager = current_process->VMManager();
619 638
620 return shared_memory->Unmap(Core::CurrentProcess(), addr); 639 if (!vm_manager.IsWithinASLRRegion(addr, size)) {
640 return ERR_INVALID_MEMORY_RANGE;
641 }
642
643 return shared_memory->Unmap(current_process, addr);
621} 644}
622 645
623/// Query process memory 646/// Query process memory
@@ -803,7 +826,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
803 std::vector<SharedPtr<Thread>>& waiting_threads, 826 std::vector<SharedPtr<Thread>>& waiting_threads,
804 VAddr condvar_addr) { 827 VAddr condvar_addr) {
805 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 828 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
806 const auto& thread_list = scheduler->GetThreadList(); 829 const auto& thread_list = scheduler.GetThreadList();
807 830
808 for (const auto& thread : thread_list) { 831 for (const auto& thread : thread_list) {
809 if (thread->GetCondVarWaitAddress() == condvar_addr) 832 if (thread->GetCondVarWaitAddress() == condvar_addr)
@@ -1092,6 +1115,29 @@ static ResultCode ClearEvent(Handle handle) {
1092 return RESULT_SUCCESS; 1115 return RESULT_SUCCESS;
1093} 1116}
1094 1117
1118static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
1119 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
1120
1121 // This function currently only allows retrieving a process' status.
1122 enum class InfoType {
1123 Status,
1124 };
1125
1126 const auto& kernel = Core::System::GetInstance().Kernel();
1127 const auto process = kernel.HandleTable().Get<Process>(process_handle);
1128 if (!process) {
1129 return ERR_INVALID_HANDLE;
1130 }
1131
1132 const auto info_type = static_cast<InfoType>(type);
1133 if (info_type != InfoType::Status) {
1134 return ERR_INVALID_ENUM_VALUE;
1135 }
1136
1137 *out = static_cast<u64>(process->GetStatus());
1138 return RESULT_SUCCESS;
1139}
1140
1095namespace { 1141namespace {
1096struct FunctionDef { 1142struct FunctionDef {
1097 using Func = void(); 1143 using Func = void();
@@ -1227,7 +1273,7 @@ static const FunctionDef SVC_Table[] = {
1227 {0x79, nullptr, "CreateProcess"}, 1273 {0x79, nullptr, "CreateProcess"},
1228 {0x7A, nullptr, "StartProcess"}, 1274 {0x7A, nullptr, "StartProcess"},
1229 {0x7B, nullptr, "TerminateProcess"}, 1275 {0x7B, nullptr, "TerminateProcess"},
1230 {0x7C, nullptr, "GetProcessInfo"}, 1276 {0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"},
1231 {0x7D, nullptr, "CreateResourceLimit"}, 1277 {0x7D, nullptr, "CreateResourceLimit"},
1232 {0x7E, nullptr, "SetResourceLimitLimitValue"}, 1278 {0x7E, nullptr, "SetResourceLimitLimitValue"},
1233 {0x7F, nullptr, "CallSecureMonitor"}, 1279 {0x7F, nullptr, "CallSecureMonitor"},
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index 70148c4fe..554a5e328 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -41,8 +41,8 @@ enum class GetInfoType : u64 {
41 RandomEntropy = 11, 41 RandomEntropy = 11,
42 PerformanceCounter = 0xF0000002, 42 PerformanceCounter = 0xF0000002,
43 // 2.0.0+ 43 // 2.0.0+
44 AddressSpaceBaseAddr = 12, 44 ASLRRegionBaseAddr = 12,
45 AddressSpaceSize = 13, 45 ASLRRegionSize = 13,
46 NewMapRegionBaseAddr = 14, 46 NewMapRegionBaseAddr = 14,
47 NewMapRegionSize = 15, 47 NewMapRegionSize = 15,
48 // 3.0.0+ 48 // 3.0.0+
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index cbb80c3c4..b09753c80 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -77,6 +77,14 @@ void SvcWrap() {
77 FuncReturn(retval); 77 FuncReturn(retval);
78} 78}
79 79
80template <ResultCode func(u64*, u32, u32)>
81void SvcWrap() {
82 u64 param_1 = 0;
83 u32 retval = func(&param_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw;
84 Core::CurrentArmInterface().SetReg(1, param_1);
85 FuncReturn(retval);
86}
87
80template <ResultCode func(u32, u64)> 88template <ResultCode func(u32, u64)>
81void SvcWrap() { 89void SvcWrap() {
82 FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw); 90 FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 352ce1725..35ec98c1a 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -97,7 +97,7 @@ void Thread::CancelWakeupTimer() {
97static boost::optional<s32> GetNextProcessorId(u64 mask) { 97static boost::optional<s32> GetNextProcessorId(u64 mask) {
98 for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) { 98 for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) {
99 if (mask & (1ULL << index)) { 99 if (mask & (1ULL << index)) {
100 if (!Core::System::GetInstance().Scheduler(index)->GetCurrentThread()) { 100 if (!Core::System::GetInstance().Scheduler(index).GetCurrentThread()) {
101 // Core is enabled and not running any threads, use this one 101 // Core is enabled and not running any threads, use this one
102 return index; 102 return index;
103 } 103 }
@@ -147,14 +147,14 @@ void Thread::ResumeFromWait() {
147 new_processor_id = processor_id; 147 new_processor_id = processor_id;
148 } 148 }
149 if (ideal_core != -1 && 149 if (ideal_core != -1 &&
150 Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { 150 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
151 new_processor_id = ideal_core; 151 new_processor_id = ideal_core;
152 } 152 }
153 153
154 ASSERT(*new_processor_id < 4); 154 ASSERT(*new_processor_id < 4);
155 155
156 // Add thread to new core's scheduler 156 // Add thread to new core's scheduler
157 auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id); 157 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
158 158
159 if (*new_processor_id != processor_id) { 159 if (*new_processor_id != processor_id) {
160 // Remove thread from previous core's scheduler 160 // Remove thread from previous core's scheduler
@@ -169,7 +169,7 @@ void Thread::ResumeFromWait() {
169 next_scheduler->ScheduleThread(this, current_priority); 169 next_scheduler->ScheduleThread(this, current_priority);
170 170
171 // Change thread's scheduler 171 // Change thread's scheduler
172 scheduler = next_scheduler.get(); 172 scheduler = next_scheduler;
173 173
174 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); 174 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
175} 175}
@@ -230,7 +230,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
230 thread->name = std::move(name); 230 thread->name = std::move(name);
231 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); 231 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
232 thread->owner_process = &owner_process; 232 thread->owner_process = &owner_process;
233 thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get(); 233 thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id);
234 thread->scheduler->AddThread(thread, priority); 234 thread->scheduler->AddThread(thread, priority);
235 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); 235 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
236 236
@@ -375,14 +375,14 @@ void Thread::ChangeCore(u32 core, u64 mask) {
375 new_processor_id = processor_id; 375 new_processor_id = processor_id;
376 } 376 }
377 if (ideal_core != -1 && 377 if (ideal_core != -1 &&
378 Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { 378 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
379 new_processor_id = ideal_core; 379 new_processor_id = ideal_core;
380 } 380 }
381 381
382 ASSERT(*new_processor_id < 4); 382 ASSERT(*new_processor_id < 4);
383 383
384 // Add thread to new core's scheduler 384 // Add thread to new core's scheduler
385 auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id); 385 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
386 386
387 if (*new_processor_id != processor_id) { 387 if (*new_processor_id != processor_id) {
388 // Remove thread from previous core's scheduler 388 // Remove thread from previous core's scheduler
@@ -397,7 +397,7 @@ void Thread::ChangeCore(u32 core, u64 mask) {
397 next_scheduler->ScheduleThread(this, current_priority); 397 next_scheduler->ScheduleThread(this, current_priority);
398 398
399 // Change thread's scheduler 399 // Change thread's scheduler
400 scheduler = next_scheduler.get(); 400 scheduler = next_scheduler;
401 401
402 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); 402 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
403} 403}
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index e412309fd..e1a34eef1 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -393,30 +393,35 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
393 393
394 switch (type) { 394 switch (type) {
395 case FileSys::ProgramAddressSpaceType::Is32Bit: 395 case FileSys::ProgramAddressSpaceType::Is32Bit:
396 case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
396 address_space_width = 32; 397 address_space_width = 32;
397 code_region_base = 0x200000; 398 code_region_base = 0x200000;
398 code_region_end = code_region_base + 0x3FE00000; 399 code_region_end = code_region_base + 0x3FE00000;
399 map_region_size = 0x40000000; 400 aslr_region_base = 0x200000;
400 heap_region_size = 0x40000000; 401 aslr_region_end = aslr_region_base + 0xFFE00000;
402 if (type == FileSys::ProgramAddressSpaceType::Is32Bit) {
403 map_region_size = 0x40000000;
404 heap_region_size = 0x40000000;
405 } else {
406 map_region_size = 0;
407 heap_region_size = 0x80000000;
408 }
401 break; 409 break;
402 case FileSys::ProgramAddressSpaceType::Is36Bit: 410 case FileSys::ProgramAddressSpaceType::Is36Bit:
403 address_space_width = 36; 411 address_space_width = 36;
404 code_region_base = 0x8000000; 412 code_region_base = 0x8000000;
405 code_region_end = code_region_base + 0x78000000; 413 code_region_end = code_region_base + 0x78000000;
414 aslr_region_base = 0x8000000;
415 aslr_region_end = aslr_region_base + 0xFF8000000;
406 map_region_size = 0x180000000; 416 map_region_size = 0x180000000;
407 heap_region_size = 0x180000000; 417 heap_region_size = 0x180000000;
408 break; 418 break;
409 case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
410 address_space_width = 32;
411 code_region_base = 0x200000;
412 code_region_end = code_region_base + 0x3FE00000;
413 map_region_size = 0;
414 heap_region_size = 0x80000000;
415 break;
416 case FileSys::ProgramAddressSpaceType::Is39Bit: 419 case FileSys::ProgramAddressSpaceType::Is39Bit:
417 address_space_width = 39; 420 address_space_width = 39;
418 code_region_base = 0x8000000; 421 code_region_base = 0x8000000;
419 code_region_end = code_region_base + 0x80000000; 422 code_region_end = code_region_base + 0x80000000;
423 aslr_region_base = 0x8000000;
424 aslr_region_end = aslr_region_base + 0x7FF8000000;
420 map_region_size = 0x1000000000; 425 map_region_size = 0x1000000000;
421 heap_region_size = 0x180000000; 426 heap_region_size = 0x180000000;
422 new_map_region_size = 0x80000000; 427 new_map_region_size = 0x80000000;
@@ -490,6 +495,38 @@ u64 VMManager::GetAddressSpaceWidth() const {
490 return address_space_width; 495 return address_space_width;
491} 496}
492 497
498VAddr VMManager::GetASLRRegionBaseAddress() const {
499 return aslr_region_base;
500}
501
502VAddr VMManager::GetASLRRegionEndAddress() const {
503 return aslr_region_end;
504}
505
506u64 VMManager::GetASLRRegionSize() const {
507 return aslr_region_end - aslr_region_base;
508}
509
510bool VMManager::IsWithinASLRRegion(VAddr begin, u64 size) const {
511 const VAddr range_end = begin + size;
512 const VAddr aslr_start = GetASLRRegionBaseAddress();
513 const VAddr aslr_end = GetASLRRegionEndAddress();
514
515 if (aslr_start > begin || begin > range_end || range_end - 1 > aslr_end - 1) {
516 return false;
517 }
518
519 if (range_end > heap_region_base && heap_region_end > begin) {
520 return false;
521 }
522
523 if (range_end > map_region_base && map_region_end > begin) {
524 return false;
525 }
526
527 return true;
528}
529
493VAddr VMManager::GetCodeRegionBaseAddress() const { 530VAddr VMManager::GetCodeRegionBaseAddress() const {
494 return code_region_base; 531 return code_region_base;
495} 532}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 015559a64..84c890224 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -205,6 +205,18 @@ public:
205 /// Gets the address space width in bits. 205 /// Gets the address space width in bits.
206 u64 GetAddressSpaceWidth() const; 206 u64 GetAddressSpaceWidth() const;
207 207
208 /// Gets the base address of the ASLR region.
209 VAddr GetASLRRegionBaseAddress() const;
210
211 /// Gets the end address of the ASLR region.
212 VAddr GetASLRRegionEndAddress() const;
213
214 /// Determines whether or not the specified address range is within the ASLR region.
215 bool IsWithinASLRRegion(VAddr address, u64 size) const;
216
217 /// Gets the size of the ASLR region
218 u64 GetASLRRegionSize() const;
219
208 /// Gets the base address of the code region. 220 /// Gets the base address of the code region.
209 VAddr GetCodeRegionBaseAddress() const; 221 VAddr GetCodeRegionBaseAddress() const;
210 222
@@ -306,6 +318,9 @@ private:
306 VAddr address_space_base = 0; 318 VAddr address_space_base = 0;
307 VAddr address_space_end = 0; 319 VAddr address_space_end = 0;
308 320
321 VAddr aslr_region_base = 0;
322 VAddr aslr_region_end = 0;
323
309 VAddr code_region_base = 0; 324 VAddr code_region_base = 0;
310 VAddr code_region_end = 0; 325 VAddr code_region_end = 0;
311 326
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 69bfce1c1..4d1f83170 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -638,10 +638,12 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
638 {24, nullptr, "GetLaunchStorageInfoForDebug"}, 638 {24, nullptr, "GetLaunchStorageInfoForDebug"},
639 {25, nullptr, "ExtendSaveData"}, 639 {25, nullptr, "ExtendSaveData"},
640 {26, nullptr, "GetSaveDataSize"}, 640 {26, nullptr, "GetSaveDataSize"},
641 {30, nullptr, "BeginBlockingHomeButtonShortAndLongPressed"}, 641 {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed,
642 {31, nullptr, "EndBlockingHomeButtonShortAndLongPressed"}, 642 "BeginBlockingHomeButtonShortAndLongPressed"},
643 {32, nullptr, "BeginBlockingHomeButton"}, 643 {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed,
644 {33, nullptr, "EndBlockingHomeButton"}, 644 "EndBlockingHomeButtonShortAndLongPressed"},
645 {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
646 {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
645 {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, 647 {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
646 {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, 648 {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
647 {60, nullptr, "SetMediaPlaybackStateForApplication"}, 649 {60, nullptr, "SetMediaPlaybackStateForApplication"},
@@ -669,6 +671,32 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
669 671
670IApplicationFunctions::~IApplicationFunctions() = default; 672IApplicationFunctions::~IApplicationFunctions() = default;
671 673
674void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
675 Kernel::HLERequestContext& ctx) {
676 IPC::ResponseBuilder rb{ctx, 2};
677 rb.Push(RESULT_SUCCESS);
678 LOG_WARNING(Service_AM, "(STUBBED) called");
679}
680
681void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(
682 Kernel::HLERequestContext& ctx) {
683 IPC::ResponseBuilder rb{ctx, 2};
684 rb.Push(RESULT_SUCCESS);
685 LOG_WARNING(Service_AM, "(STUBBED) called");
686}
687
688void IApplicationFunctions::BeginBlockingHomeButton(Kernel::HLERequestContext& ctx) {
689 IPC::ResponseBuilder rb{ctx, 2};
690 rb.Push(RESULT_SUCCESS);
691 LOG_WARNING(Service_AM, "(STUBBED) called");
692}
693
694void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx) {
695 IPC::ResponseBuilder rb{ctx, 2};
696 rb.Push(RESULT_SUCCESS);
697 LOG_WARNING(Service_AM, "(STUBBED) called");
698}
699
672void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { 700void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
673 constexpr std::array<u8, 0x88> data{{ 701 constexpr std::array<u8, 0x88> data{{
674 0xca, 0x97, 0x94, 0xc7, // Magic 702 0xca, 0x97, 0x94, 0xc7, // Magic
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index b39b0d838..095f94851 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -154,6 +154,10 @@ private:
154 void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx); 154 void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
155 void NotifyRunning(Kernel::HLERequestContext& ctx); 155 void NotifyRunning(Kernel::HLERequestContext& ctx);
156 void GetPseudoDeviceId(Kernel::HLERequestContext& ctx); 156 void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
157 void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
158 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
159 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
160 void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
157}; 161};
158 162
159class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { 163class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp
index 1c37f849f..6ab3fb906 100644
--- a/src/core/hle/service/am/omm.cpp
+++ b/src/core/hle/service/am/omm.cpp
@@ -17,22 +17,24 @@ OMM::OMM() : ServiceFramework{"omm"} {
17 {5, nullptr, "GetCradleStatus"}, 17 {5, nullptr, "GetCradleStatus"},
18 {6, nullptr, "FadeInDisplay"}, 18 {6, nullptr, "FadeInDisplay"},
19 {7, nullptr, "FadeOutDisplay"}, 19 {7, nullptr, "FadeOutDisplay"},
20 {8, nullptr, "Unknown1"}, 20 {8, nullptr, "GetCradleFwVersion"},
21 {9, nullptr, "Unknown2"}, 21 {9, nullptr, "NotifyCecSettingsChanged"},
22 {10, nullptr, "Unknown3"}, 22 {10, nullptr, "SetOperationModePolicy"},
23 {11, nullptr, "Unknown4"}, 23 {11, nullptr, "GetDefaultDisplayResolution"},
24 {12, nullptr, "Unknown5"}, 24 {12, nullptr, "GetDefaultDisplayResolutionChangeEvent"},
25 {13, nullptr, "Unknown6"}, 25 {13, nullptr, "UpdateDefaultDisplayResolution"},
26 {14, nullptr, "Unknown7"}, 26 {14, nullptr, "ShouldSleepOnBoot"},
27 {15, nullptr, "Unknown8"}, 27 {15, nullptr, "NotifyHdcpApplicationExecutionStarted"},
28 {16, nullptr, "Unknown9"}, 28 {16, nullptr, "NotifyHdcpApplicationExecutionFinished"},
29 {17, nullptr, "Unknown10"}, 29 {17, nullptr, "NotifyHdcpApplicationDrawingStarted"},
30 {18, nullptr, "Unknown11"}, 30 {18, nullptr, "NotifyHdcpApplicationDrawingFinished"},
31 {19, nullptr, "Unknown12"}, 31 {19, nullptr, "GetHdcpAuthenticationFailedEvent"},
32 {20, nullptr, "Unknown13"}, 32 {20, nullptr, "GetHdcpAuthenticationFailedEmulationEnabled"},
33 {21, nullptr, "Unknown14"}, 33 {21, nullptr, "SetHdcpAuthenticationFailedEmulation"},
34 {22, nullptr, "Unknown15"}, 34 {22, nullptr, "GetHdcpStateChangeEvent"},
35 {23, nullptr, "Unknown16"}, 35 {23, nullptr, "GetHdcpState"},
36 {24, nullptr, "ShowCardUpdateProcessing"},
37 {25, nullptr, "SetApplicationCecSettingsAndNotifyChanged"},
36 }; 38 };
37 // clang-format on 39 // clang-format on
38 40
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 0ecfb5af1..428069df2 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -7,10 +7,13 @@
7#include <vector> 7#include <vector>
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/file_sys/content_archive.h" 9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h"
10#include "core/file_sys/nca_metadata.h" 11#include "core/file_sys/nca_metadata.h"
11#include "core/file_sys/partition_filesystem.h" 12#include "core/file_sys/partition_filesystem.h"
13#include "core/file_sys/patch_manager.h"
12#include "core/file_sys/registered_cache.h" 14#include "core/file_sys/registered_cache.h"
13#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
16#include "core/hle/kernel/event.h"
14#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
15#include "core/hle/service/aoc/aoc_u.h" 18#include "core/hle/service/aoc/aoc_u.h"
16#include "core/hle/service/filesystem/filesystem.h" 19#include "core/hle/service/filesystem/filesystem.h"
@@ -19,7 +22,7 @@
19namespace Service::AOC { 22namespace Service::AOC {
20 23
21constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; 24constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
22constexpr u64 DLC_BASE_TO_AOC_ID_MASK = 0x1000; 25constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000;
23 26
24static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { 27static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) {
25 return (aoc & DLC_BASE_TITLE_ID_MASK) == base; 28 return (aoc & DLC_BASE_TITLE_ID_MASK) == base;
@@ -53,9 +56,13 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
53 {5, &AOC_U::GetAddOnContentBaseId, "GetAddOnContentBaseId"}, 56 {5, &AOC_U::GetAddOnContentBaseId, "GetAddOnContentBaseId"},
54 {6, nullptr, "PrepareAddOnContentByApplicationId"}, 57 {6, nullptr, "PrepareAddOnContentByApplicationId"},
55 {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, 58 {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"},
56 {8, nullptr, "GetAddOnContentListChangedEvent"}, 59 {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"},
57 }; 60 };
58 RegisterHandlers(functions); 61 RegisterHandlers(functions);
62
63 auto& kernel = Core::System::GetInstance().Kernel();
64 aoc_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky,
65 "GetAddOnContentListChanged:Event");
59} 66}
60 67
61AOC_U::~AOC_U() = default; 68AOC_U::~AOC_U() = default;
@@ -97,14 +104,24 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
97 104
98 ctx.WriteBuffer(out); 105 ctx.WriteBuffer(out);
99 106
100 IPC::ResponseBuilder rb{ctx, 2}; 107 IPC::ResponseBuilder rb{ctx, 3};
101 rb.Push(RESULT_SUCCESS); 108 rb.Push(RESULT_SUCCESS);
109 rb.Push(count);
102} 110}
103 111
104void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { 112void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
105 IPC::ResponseBuilder rb{ctx, 4}; 113 IPC::ResponseBuilder rb{ctx, 4};
106 rb.Push(RESULT_SUCCESS); 114 rb.Push(RESULT_SUCCESS);
107 rb.Push(Core::System::GetInstance().CurrentProcess()->GetTitleID() | DLC_BASE_TO_AOC_ID_MASK); 115 const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
116 FileSys::PatchManager pm{title_id};
117
118 const auto res = pm.GetControlMetadata();
119 if (res.first == nullptr) {
120 rb.Push(title_id + DLC_BASE_TO_AOC_ID);
121 return;
122 }
123
124 rb.Push(res.first->GetDLCBaseTitleId());
108} 125}
109 126
110void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) { 127void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
@@ -118,6 +135,14 @@ void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
118 rb.Push(RESULT_SUCCESS); 135 rb.Push(RESULT_SUCCESS);
119} 136}
120 137
138void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) {
139 LOG_WARNING(Service_AOC, "(STUBBED) called");
140
141 IPC::ResponseBuilder rb{ctx, 2, 1};
142 rb.Push(RESULT_SUCCESS);
143 rb.PushCopyObjects(aoc_change_event);
144}
145
121void InstallInterfaces(SM::ServiceManager& service_manager) { 146void InstallInterfaces(SM::ServiceManager& service_manager) {
122 std::make_shared<AOC_U>()->InstallAsService(service_manager); 147 std::make_shared<AOC_U>()->InstallAsService(service_manager);
123} 148}
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h
index b3c7cab7a..68d94fdaa 100644
--- a/src/core/hle/service/aoc/aoc_u.h
+++ b/src/core/hle/service/aoc/aoc_u.h
@@ -18,8 +18,10 @@ private:
18 void ListAddOnContent(Kernel::HLERequestContext& ctx); 18 void ListAddOnContent(Kernel::HLERequestContext& ctx);
19 void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx); 19 void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx);
20 void PrepareAddOnContent(Kernel::HLERequestContext& ctx); 20 void PrepareAddOnContent(Kernel::HLERequestContext& ctx);
21 void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx);
21 22
22 std::vector<u64> add_on_content; 23 std::vector<u64> add_on_content;
24 Kernel::SharedPtr<Kernel::Event> aoc_change_event;
23}; 25};
24 26
25/// Registers all AOC services with the specified service manager. 27/// Registers all AOC services with the specified service manager.
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 6073f4ecd..fac6785a5 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -22,20 +22,22 @@ class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
22public: 22public:
23 explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params) 23 explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
24 : ServiceFramework("IAudioRenderer") { 24 : ServiceFramework("IAudioRenderer") {
25 // clang-format off
25 static const FunctionInfo functions[] = { 26 static const FunctionInfo functions[] = {
26 {0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"}, 27 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
27 {1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"}, 28 {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
28 {2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"}, 29 {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
29 {3, &IAudioRenderer::GetAudioRendererState, "GetAudioRendererState"}, 30 {3, &IAudioRenderer::GetState, "GetState"},
30 {4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"}, 31 {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
31 {5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"}, 32 {5, &IAudioRenderer::Start, "Start"},
32 {6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"}, 33 {6, &IAudioRenderer::Stop, "Stop"},
33 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, 34 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
34 {8, nullptr, "SetAudioRendererRenderingTimeLimit"}, 35 {8, nullptr, "SetRenderingTimeLimit"},
35 {9, nullptr, "GetAudioRendererRenderingTimeLimit"}, 36 {9, nullptr, "GetRenderingTimeLimit"},
36 {10, nullptr, "RequestUpdateAudioRendererAuto"}, 37 {10, nullptr, "RequestUpdateAuto"},
37 {11, nullptr, "ExecuteAudioRendererRendering"}, 38 {11, nullptr, "ExecuteAudioRendererRendering"},
38 }; 39 };
40 // clang-format on
39 RegisterHandlers(functions); 41 RegisterHandlers(functions);
40 42
41 auto& kernel = Core::System::GetInstance().Kernel(); 43 auto& kernel = Core::System::GetInstance().Kernel();
@@ -49,42 +51,42 @@ private:
49 system_event->Signal(); 51 system_event->Signal();
50 } 52 }
51 53
52 void GetAudioRendererSampleRate(Kernel::HLERequestContext& ctx) { 54 void GetSampleRate(Kernel::HLERequestContext& ctx) {
53 IPC::ResponseBuilder rb{ctx, 3}; 55 IPC::ResponseBuilder rb{ctx, 3};
54 rb.Push(RESULT_SUCCESS); 56 rb.Push(RESULT_SUCCESS);
55 rb.Push<u32>(renderer->GetSampleRate()); 57 rb.Push<u32>(renderer->GetSampleRate());
56 LOG_DEBUG(Service_Audio, "called"); 58 LOG_DEBUG(Service_Audio, "called");
57 } 59 }
58 60
59 void GetAudioRendererSampleCount(Kernel::HLERequestContext& ctx) { 61 void GetSampleCount(Kernel::HLERequestContext& ctx) {
60 IPC::ResponseBuilder rb{ctx, 3}; 62 IPC::ResponseBuilder rb{ctx, 3};
61 rb.Push(RESULT_SUCCESS); 63 rb.Push(RESULT_SUCCESS);
62 rb.Push<u32>(renderer->GetSampleCount()); 64 rb.Push<u32>(renderer->GetSampleCount());
63 LOG_DEBUG(Service_Audio, "called"); 65 LOG_DEBUG(Service_Audio, "called");
64 } 66 }
65 67
66 void GetAudioRendererState(Kernel::HLERequestContext& ctx) { 68 void GetState(Kernel::HLERequestContext& ctx) {
67 IPC::ResponseBuilder rb{ctx, 3}; 69 IPC::ResponseBuilder rb{ctx, 3};
68 rb.Push(RESULT_SUCCESS); 70 rb.Push(RESULT_SUCCESS);
69 rb.Push<u32>(static_cast<u32>(renderer->GetStreamState())); 71 rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
70 LOG_DEBUG(Service_Audio, "called"); 72 LOG_DEBUG(Service_Audio, "called");
71 } 73 }
72 74
73 void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) { 75 void GetMixBufferCount(Kernel::HLERequestContext& ctx) {
74 IPC::ResponseBuilder rb{ctx, 3}; 76 IPC::ResponseBuilder rb{ctx, 3};
75 rb.Push(RESULT_SUCCESS); 77 rb.Push(RESULT_SUCCESS);
76 rb.Push<u32>(renderer->GetMixBufferCount()); 78 rb.Push<u32>(renderer->GetMixBufferCount());
77 LOG_DEBUG(Service_Audio, "called"); 79 LOG_DEBUG(Service_Audio, "called");
78 } 80 }
79 81
80 void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { 82 void RequestUpdate(Kernel::HLERequestContext& ctx) {
81 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); 83 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
82 IPC::ResponseBuilder rb{ctx, 2}; 84 IPC::ResponseBuilder rb{ctx, 2};
83 rb.Push(RESULT_SUCCESS); 85 rb.Push(RESULT_SUCCESS);
84 LOG_WARNING(Service_Audio, "(STUBBED) called"); 86 LOG_WARNING(Service_Audio, "(STUBBED) called");
85 } 87 }
86 88
87 void StartAudioRenderer(Kernel::HLERequestContext& ctx) { 89 void Start(Kernel::HLERequestContext& ctx) {
88 IPC::ResponseBuilder rb{ctx, 2}; 90 IPC::ResponseBuilder rb{ctx, 2};
89 91
90 rb.Push(RESULT_SUCCESS); 92 rb.Push(RESULT_SUCCESS);
@@ -92,7 +94,7 @@ private:
92 LOG_WARNING(Service_Audio, "(STUBBED) called"); 94 LOG_WARNING(Service_Audio, "(STUBBED) called");
93 } 95 }
94 96
95 void StopAudioRenderer(Kernel::HLERequestContext& ctx) { 97 void Stop(Kernel::HLERequestContext& ctx) {
96 IPC::ResponseBuilder rb{ctx, 2}; 98 IPC::ResponseBuilder rb{ctx, 2};
97 99
98 rb.Push(RESULT_SUCCESS); 100 rb.Push(RESULT_SUCCESS);
@@ -129,6 +131,7 @@ public:
129 {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, 131 {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
130 {11, nullptr, "QueryAudioDeviceInputEvent"}, 132 {11, nullptr, "QueryAudioDeviceInputEvent"},
131 {12, nullptr, "QueryAudioDeviceOutputEvent"}, 133 {12, nullptr, "QueryAudioDeviceOutputEvent"},
134 {13, nullptr, "GetAudioSystemMasterVolumeSetting"},
132 }; 135 };
133 RegisterHandlers(functions); 136 RegisterHandlers(functions);
134 137
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index d40f18565..6701cb913 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -9,6 +9,7 @@ namespace Service::ES {
9class ETicket final : public ServiceFramework<ETicket> { 9class ETicket final : public ServiceFramework<ETicket> {
10public: 10public:
11 explicit ETicket() : ServiceFramework{"es"} { 11 explicit ETicket() : ServiceFramework{"es"} {
12 // clang-format off
12 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
13 {1, nullptr, "ImportTicket"}, 14 {1, nullptr, "ImportTicket"},
14 {2, nullptr, "ImportTicketCertificateSet"}, 15 {2, nullptr, "ImportTicketCertificateSet"},
@@ -37,15 +38,18 @@ public:
37 {25, nullptr, "DeletePrepurchaseRecord"}, 38 {25, nullptr, "DeletePrepurchaseRecord"},
38 {26, nullptr, "DeleteAllPrepurchaseRecord"}, 39 {26, nullptr, "DeleteAllPrepurchaseRecord"},
39 {27, nullptr, "CountPrepurchaseRecord"}, 40 {27, nullptr, "CountPrepurchaseRecord"},
40 {28, nullptr, "ListPrepurchaseRecord"}, 41 {28, nullptr, "ListPrepurchaseRecordRightsIds"},
41 {29, nullptr, "ListPrepurchaseRecordInfo"}, 42 {29, nullptr, "ListPrepurchaseRecordInfo"},
42 {30, nullptr, "Unknown1"}, 43 {30, nullptr, "CountTicket"},
43 {31, nullptr, "Unknown2"}, 44 {31, nullptr, "ListTicketRightsIds"},
44 {32, nullptr, "Unknown3"}, 45 {32, nullptr, "CountPrepurchaseRecordEx"},
45 {33, nullptr, "Unknown4"}, 46 {33, nullptr, "ListPrepurchaseRecordRightsIdsEx"},
46 {34, nullptr, "Unknown5"}, 47 {34, nullptr, "GetEncryptedTicketSize"},
47 {35, nullptr, "Unknown6"}, 48 {35, nullptr, "GetEncryptedTicketData"},
49 {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"},
50 {503, nullptr, "GetTitleKey"},
48 }; 51 };
52 // clang-format on
49 RegisterHandlers(functions); 53 RegisterHandlers(functions);
50 } 54 }
51}; 55};
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 439e62d27..e32a7c48e 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -319,13 +319,12 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
319 return sdmc_factory->Open(); 319 return sdmc_factory->Open();
320} 320}
321 321
322std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() { 322std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
323 return std::make_shared<FileSys::RegisteredCacheUnion>( 323 return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
324 std::vector<std::shared_ptr<FileSys::RegisteredCache>>{ 324 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
325 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
326} 325}
327 326
328std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() { 327FileSys::RegisteredCache* GetSystemNANDContents() {
329 LOG_TRACE(Service_FS, "Opening System NAND Contents"); 328 LOG_TRACE(Service_FS, "Opening System NAND Contents");
330 329
331 if (bis_factory == nullptr) 330 if (bis_factory == nullptr)
@@ -334,7 +333,7 @@ std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() {
334 return bis_factory->GetSystemNANDContents(); 333 return bis_factory->GetSystemNANDContents();
335} 334}
336 335
337std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents() { 336FileSys::RegisteredCache* GetUserNANDContents() {
338 LOG_TRACE(Service_FS, "Opening User NAND Contents"); 337 LOG_TRACE(Service_FS, "Opening User NAND Contents");
339 338
340 if (bis_factory == nullptr) 339 if (bis_factory == nullptr)
@@ -343,7 +342,7 @@ std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents() {
343 return bis_factory->GetUserNANDContents(); 342 return bis_factory->GetUserNANDContents();
344} 343}
345 344
346std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents() { 345FileSys::RegisteredCache* GetSDMCContents() {
347 LOG_TRACE(Service_FS, "Opening SDMC Contents"); 346 LOG_TRACE(Service_FS, "Opening SDMC Contents");
348 347
349 if (sdmc_factory == nullptr) 348 if (sdmc_factory == nullptr)
@@ -361,19 +360,19 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
361 return bis_factory->GetModificationLoadRoot(title_id); 360 return bis_factory->GetModificationLoadRoot(title_id);
362} 361}
363 362
364void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) { 363void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
365 if (overwrite) { 364 if (overwrite) {
366 bis_factory = nullptr; 365 bis_factory = nullptr;
367 save_data_factory = nullptr; 366 save_data_factory = nullptr;
368 sdmc_factory = nullptr; 367 sdmc_factory = nullptr;
369 } 368 }
370 369
371 auto nand_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), 370 auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
372 FileSys::Mode::ReadWrite); 371 FileSys::Mode::ReadWrite);
373 auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), 372 auto sd_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
374 FileSys::Mode::ReadWrite); 373 FileSys::Mode::ReadWrite);
375 auto load_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), 374 auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
376 FileSys::Mode::ReadWrite); 375 FileSys::Mode::ReadWrite);
377 376
378 if (bis_factory == nullptr) 377 if (bis_factory == nullptr)
379 bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory); 378 bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory);
@@ -383,7 +382,7 @@ void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
383 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); 382 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
384} 383}
385 384
386void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs) { 385void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {
387 romfs_factory = nullptr; 386 romfs_factory = nullptr;
388 CreateFactories(vfs, false); 387 CreateFactories(vfs, false);
389 std::make_shared<FSP_LDR>()->InstallAsService(service_manager); 388 std::make_shared<FSP_LDR>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 53b01bb01..6ca5c5636 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -47,19 +47,19 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
47 FileSys::SaveDataDescriptor save_struct); 47 FileSys::SaveDataDescriptor save_struct);
48ResultVal<FileSys::VirtualDir> OpenSDMC(); 48ResultVal<FileSys::VirtualDir> OpenSDMC();
49 49
50std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); 50std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
51 51
52std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents(); 52FileSys::RegisteredCache* GetSystemNANDContents();
53std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents(); 53FileSys::RegisteredCache* GetUserNANDContents();
54std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents(); 54FileSys::RegisteredCache* GetSDMCContents();
55 55
56FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); 56FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
57 57
58// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function 58// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
59// above is called. 59// above is called.
60void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true); 60void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
61 61
62void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs); 62void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs);
63 63
64// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of 64// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
65// pointers and booleans. This makes using a VfsDirectory with switch services much easier and 65// pointers and booleans. This makes using a VfsDirectory with switch services much easier and
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
new file mode 100644
index 000000000..0993a7815
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -0,0 +1,30 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/hid/controllers/controller_base.h"
6
7namespace Service::HID {
8
9ControllerBase::ControllerBase() = default;
10ControllerBase::~ControllerBase() = default;
11
12void ControllerBase::ActivateController() {
13 if (is_activated) {
14 OnRelease();
15 }
16 is_activated = true;
17 OnInit();
18}
19
20void ControllerBase::DeactivateController() {
21 if (is_activated) {
22 OnRelease();
23 }
24 is_activated = false;
25}
26
27bool ControllerBase::IsControllerActivated() const {
28 return is_activated;
29}
30} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
new file mode 100644
index 000000000..f0e092b1b
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -0,0 +1,45 @@
1// Copyright 2018 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 "common/common_types.h"
8#include "common/swap.h"
9
10namespace Service::HID {
11class ControllerBase {
12public:
13 ControllerBase();
14 virtual ~ControllerBase();
15
16 // Called when the controller is initialized
17 virtual void OnInit() = 0;
18
19 // When the controller is released
20 virtual void OnRelease() = 0;
21
22 // When the controller is requesting an update for the shared memory
23 virtual void OnUpdate(u8* data, std::size_t size) = 0;
24
25 // Called when input devices should be loaded
26 virtual void OnLoadInputDevices() = 0;
27
28 void ActivateController();
29
30 void DeactivateController();
31
32 bool IsControllerActivated() const;
33
34protected:
35 bool is_activated{false};
36
37 struct CommonHeader {
38 s64_le timestamp;
39 s64_le total_entry_count;
40 s64_le last_entry_index;
41 s64_le entry_count;
42 };
43 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
44};
45} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
new file mode 100644
index 000000000..3d100763f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -0,0 +1,42 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/debug_pad.h"
9
10namespace Service::HID {
11
12Controller_DebugPad::Controller_DebugPad() = default;
13Controller_DebugPad::~Controller_DebugPad() = default;
14
15void Controller_DebugPad::OnInit() {}
16
17void Controller_DebugPad::OnRelease() {}
18
19void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
20 shared_memory.header.timestamp = CoreTiming::GetTicks();
21 shared_memory.header.total_entry_count = 17;
22
23 if (!IsControllerActivated()) {
24 shared_memory.header.entry_count = 0;
25 shared_memory.header.last_entry_index = 0;
26 return;
27 }
28 shared_memory.header.entry_count = 16;
29
30 const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
31 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
32 auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
33
34 cur_entry.sampling_number = last_entry.sampling_number + 1;
35 cur_entry.sampling_number2 = cur_entry.sampling_number;
36 // TODO(ogniK): Update debug pad states
37
38 std::memcpy(data, &shared_memory, sizeof(SharedMemory));
39}
40
41void Controller_DebugPad::OnLoadInputDevices() {}
42} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
new file mode 100644
index 000000000..62b4f2682
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -0,0 +1,56 @@
1// Copyright 2018 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 "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/hid/controllers/controller_base.h"
12
13namespace Service::HID {
14class Controller_DebugPad final : public ControllerBase {
15public:
16 Controller_DebugPad();
17 ~Controller_DebugPad() override;
18
19 // Called when the controller is initialized
20 void OnInit() override;
21
22 // When the controller is released
23 void OnRelease() override;
24
25 // When the controller is requesting an update for the shared memory
26 void OnUpdate(u8* data, std::size_t size) override;
27
28 // Called when input devices should be loaded
29 void OnLoadInputDevices() override;
30
31private:
32 struct AnalogStick {
33 s32_le x;
34 s32_le y;
35 };
36 static_assert(sizeof(AnalogStick) == 0x8);
37
38 struct PadStates {
39 s64_le sampling_number;
40 s64_le sampling_number2;
41 u32_le attribute;
42 u32_le button_state;
43 AnalogStick r_stick;
44 AnalogStick l_stick;
45 };
46 static_assert(sizeof(PadStates) == 0x28, "PadStates is an invalid state");
47
48 struct SharedMemory {
49 CommonHeader header;
50 std::array<PadStates, 17> pad_states;
51 INSERT_PADDING_BYTES(0x138);
52 };
53 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
54 SharedMemory shared_memory{};
55};
56} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
new file mode 100644
index 000000000..898572277
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -0,0 +1,43 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/gesture.h"
9
10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
12
13Controller_Gesture::Controller_Gesture() = default;
14Controller_Gesture::~Controller_Gesture() = default;
15
16void Controller_Gesture::OnInit() {}
17
18void Controller_Gesture::OnRelease() {}
19
20void Controller_Gesture::OnUpdate(u8* data, std::size_t size) {
21 shared_memory.header.timestamp = CoreTiming::GetTicks();
22 shared_memory.header.total_entry_count = 17;
23
24 if (!IsControllerActivated()) {
25 shared_memory.header.entry_count = 0;
26 shared_memory.header.last_entry_index = 0;
27 return;
28 }
29 shared_memory.header.entry_count = 16;
30
31 const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
32 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
33 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
34
35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update gesture states
38
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40}
41
42void Controller_Gesture::OnLoadInputDevices() {}
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
new file mode 100644
index 000000000..1056ffbcd
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -0,0 +1,63 @@
1// Copyright 2018 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 "common/common_types.h"
9#include "common/swap.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11
12namespace Service::HID {
13class Controller_Gesture final : public ControllerBase {
14public:
15 Controller_Gesture();
16 ~Controller_Gesture() override;
17
18 // Called when the controller is initialized
19 void OnInit() override;
20
21 // When the controller is released
22 void OnRelease() override;
23
24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, size_t size) override;
26
27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override;
29
30private:
31 struct Locations {
32 s32_le x;
33 s32_le y;
34 };
35
36 struct GestureState {
37 s64_le sampling_number;
38 s64_le sampling_number2;
39
40 s64_le detection_count;
41 s32_le type;
42 s32_le dir;
43 s32_le x;
44 s32_le y;
45 s32_le delta_x;
46 s32_le delta_y;
47 f32 vel_x;
48 f32 vel_y;
49 s32_le attributes;
50 f32 scale;
51 f32 rotation;
52 s32_le location_count;
53 std::array<Locations, 4> locations;
54 };
55 static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
56
57 struct SharedMemory {
58 CommonHeader header;
59 std::array<GestureState, 17> gesture_states;
60 };
61 SharedMemory shared_memory{};
62};
63} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
new file mode 100644
index 000000000..ccfbce9ac
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -0,0 +1,43 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/keyboard.h"
9
10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
12
13Controller_Keyboard::Controller_Keyboard() = default;
14Controller_Keyboard::~Controller_Keyboard() = default;
15
16void Controller_Keyboard::OnInit() {}
17
18void Controller_Keyboard::OnRelease() {}
19
20void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
21 shared_memory.header.timestamp = CoreTiming::GetTicks();
22 shared_memory.header.total_entry_count = 17;
23
24 if (!IsControllerActivated()) {
25 shared_memory.header.entry_count = 0;
26 shared_memory.header.last_entry_index = 0;
27 return;
28 }
29 shared_memory.header.entry_count = 16;
30
31 const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
32 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
33 auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
34
35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update keyboard states
38
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40}
41
42void Controller_Keyboard::OnLoadInputDevices() {}
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
new file mode 100644
index 000000000..493e68fce
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -0,0 +1,50 @@
1// Copyright 2018 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 "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/hid/controllers/controller_base.h"
12
13namespace Service::HID {
14class Controller_Keyboard final : public ControllerBase {
15public:
16 Controller_Keyboard();
17 ~Controller_Keyboard() override;
18
19 // Called when the controller is initialized
20 void OnInit() override;
21
22 // When the controller is released
23 void OnRelease() override;
24
25 // When the controller is requesting an update for the shared memory
26 void OnUpdate(u8* data, std::size_t size) override;
27
28 // Called when input devices should be loaded
29 void OnLoadInputDevices() override;
30
31private:
32 struct KeyboardState {
33 s64_le sampling_number;
34 s64_le sampling_number2;
35
36 s32_le modifier;
37 s32_le attribute;
38 std::array<u8, 32> key;
39 };
40 static_assert(sizeof(KeyboardState) == 0x38, "KeyboardState is an invalid size");
41
42 struct SharedMemory {
43 CommonHeader header;
44 std::array<KeyboardState, 17> pad_states;
45 INSERT_PADDING_BYTES(0x28);
46 };
47 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
48 SharedMemory shared_memory{};
49};
50} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
new file mode 100644
index 000000000..4e246a57d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -0,0 +1,43 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/mouse.h"
9
10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
12
13Controller_Mouse::Controller_Mouse() = default;
14Controller_Mouse::~Controller_Mouse() = default;
15
16void Controller_Mouse::OnInit() {}
17
18void Controller_Mouse::OnRelease() {}
19
20void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
21 shared_memory.header.timestamp = CoreTiming::GetTicks();
22 shared_memory.header.total_entry_count = 17;
23
24 if (!IsControllerActivated()) {
25 shared_memory.header.entry_count = 0;
26 shared_memory.header.last_entry_index = 0;
27 return;
28 }
29 shared_memory.header.entry_count = 16;
30
31 auto& last_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
32 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
33 auto& cur_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
34
35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update mouse states
38
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40}
41
42void Controller_Mouse::OnLoadInputDevices() {}
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
new file mode 100644
index 000000000..543b0b71f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -0,0 +1,50 @@
1// Copyright 2018 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 "common/common_types.h"
9#include "common/swap.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11
12namespace Service::HID {
13class Controller_Mouse final : public ControllerBase {
14public:
15 Controller_Mouse();
16 ~Controller_Mouse() override;
17
18 // Called when the controller is initialized
19 void OnInit() override;
20
21 // When the controller is released
22 void OnRelease() override;
23
24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, std::size_t size) override;
26
27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override;
29
30private:
31 struct MouseState {
32 s64_le sampling_number;
33 s64_le sampling_number2;
34 s32_le x;
35 s32_le y;
36 s32_le delta_x;
37 s32_le delta_y;
38 s32_le mouse_wheel;
39 s32_le button;
40 s32_le attribute;
41 };
42 static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size");
43
44 struct SharedMemory {
45 CommonHeader header;
46 std::array<MouseState, 17> mouse_states;
47 };
48 SharedMemory shared_memory{};
49};
50} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
new file mode 100644
index 000000000..b26593b4f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -0,0 +1,436 @@
1// Copyright 2018 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 <array>
7#include <cstring>
8#include "common/assert.h"
9#include "common/bit_field.h"
10#include "common/common_types.h"
11#include "common/logging/log.h"
12#include "core/core.h"
13#include "core/core_timing.h"
14#include "core/frontend/input.h"
15#include "core/hle/kernel/event.h"
16#include "core/hle/service/hid/controllers/npad.h"
17#include "core/settings.h"
18
19namespace Service::HID {
20
21constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
22constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
23constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
24constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
25constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
26constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
27constexpr std::size_t NPAD_OFFSET = 0x9A00;
28constexpr u32 BATTERY_FULL = 2;
29
30constexpr std::array<u32, 10> npad_id_list{
31 0, 1, 2, 3, 4, 5, 6, 7, 32, 16,
32};
33
34enum class JoystickId : std::size_t {
35 Joystick_Left,
36 Joystick_Right,
37};
38
39Controller_NPad::Controller_NPad() = default;
40Controller_NPad::~Controller_NPad() = default;
41
42void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
43 const auto controller_type = connected_controllers[controller_idx].type;
44 auto& controller = shared_memory_entries[controller_idx];
45 if (controller_type == NPadControllerType::None) {
46 return;
47 }
48 controller.joy_styles.raw = 0; // Zero out
49 controller.device_type.raw = 0;
50 switch (controller_type) {
51 case NPadControllerType::Handheld:
52 controller.joy_styles.handheld.Assign(1);
53 controller.device_type.handheld.Assign(1);
54 controller.pad_assignment = NPadAssignments::Dual;
55 break;
56 case NPadControllerType::JoyDual:
57 controller.joy_styles.joycon_dual.Assign(1);
58 controller.device_type.joycon_left.Assign(1);
59 controller.device_type.joycon_right.Assign(1);
60 controller.pad_assignment = NPadAssignments::Dual;
61 break;
62 case NPadControllerType::JoyLeft:
63 controller.joy_styles.joycon_left.Assign(1);
64 controller.device_type.joycon_left.Assign(1);
65 controller.pad_assignment = NPadAssignments::Dual;
66 break;
67 case NPadControllerType::JoyRight:
68 controller.joy_styles.joycon_right.Assign(1);
69 controller.device_type.joycon_right.Assign(1);
70 controller.pad_assignment = NPadAssignments::Dual;
71 break;
72 case NPadControllerType::Pokeball:
73 controller.joy_styles.pokeball.Assign(1);
74 controller.device_type.pokeball.Assign(1);
75 controller.pad_assignment = NPadAssignments::Single;
76 break;
77 case NPadControllerType::ProController:
78 controller.joy_styles.pro_controller.Assign(1);
79 controller.device_type.pro_controller.Assign(1);
80 controller.pad_assignment = NPadAssignments::Single;
81 break;
82 }
83
84 controller.single_color_error = ColorReadError::ReadOk;
85 controller.single_color.body_color = 0;
86 controller.single_color.button_color = 0;
87
88 controller.dual_color_error = ColorReadError::ReadOk;
89 controller.left_color.body_color = JOYCON_BODY_NEON_BLUE;
90 controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE;
91 controller.right_color.body_color = JOYCON_BODY_NEON_RED;
92 controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED;
93
94 controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations
95 controller.properties.use_plus.Assign(1);
96 controller.properties.use_minus.Assign(1);
97 controller.battery_level[0] = BATTERY_FULL;
98 controller.battery_level[1] = BATTERY_FULL;
99 controller.battery_level[2] = BATTERY_FULL;
100}
101
102void Controller_NPad::OnInit() {
103 auto& kernel = Core::System::GetInstance().Kernel();
104 styleset_changed_event =
105 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
106
107 if (!IsControllerActivated())
108 return;
109 std::size_t controller{};
110 if (style.raw == 0) {
111 // We want to support all controllers
112 style.handheld.Assign(1);
113 style.joycon_left.Assign(1);
114 style.joycon_right.Assign(1);
115 style.joycon_dual.Assign(1);
116 style.pro_controller.Assign(1);
117 style.pokeball.Assign(1);
118 }
119 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
120 [](const ControllerHolder& controller) { return controller.is_connected; })) {
121 supported_npad_id_types.resize(npad_id_list.size());
122 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
123 npad_id_list.size() * sizeof(u32));
124 AddNewController(NPadControllerType::JoyDual);
125 }
126}
127
128void Controller_NPad::OnLoadInputDevices() {
129 std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
130 Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
131 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
132 std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
133 Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
134 sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
135}
136
137void Controller_NPad::OnRelease() {}
138
139void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
140 if (!IsControllerActivated())
141 return;
142 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
143 auto& npad = shared_memory_entries[i];
144 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
145 &npad.handheld_states,
146 &npad.dual_states,
147 &npad.left_joy_states,
148 &npad.right_joy_states,
149 &npad.pokeball_states,
150 &npad.libnx};
151
152 for (auto* main_controller : controller_npads) {
153 main_controller->common.entry_count = 16;
154 main_controller->common.total_entry_count = 17;
155
156 const auto& last_entry =
157 main_controller->npad[main_controller->common.last_entry_index];
158
159 main_controller->common.timestamp = CoreTiming::GetTicks();
160 main_controller->common.last_entry_index =
161 (main_controller->common.last_entry_index + 1) % 17;
162
163 auto& cur_entry = main_controller->npad[main_controller->common.last_entry_index];
164
165 cur_entry.timestamp = last_entry.timestamp + 1;
166 cur_entry.timestamp2 = cur_entry.timestamp;
167 }
168
169 const auto& controller_type = connected_controllers[i].type;
170
171 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
172 continue;
173 }
174
175 // Pad states
176 ControllerPadState pad_state{};
177 using namespace Settings::NativeButton;
178 pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
179 pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
180 pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
181 pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
182 pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
183 pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
184 pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
185 pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
186 pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
187 pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
188 pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
189 pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
190
191 pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
192 pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
193 pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
194 pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
195
196 pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
197 pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
198 pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
199 pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
200
201 pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
202 pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
203 pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
204 pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
205
206 pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
207 pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
208
209 AnalogPosition lstick_entry{};
210 AnalogPosition rstick_entry{};
211
212 const auto [stick_l_x_f, stick_l_y_f] =
213 sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
214 const auto [stick_r_x_f, stick_r_y_f] =
215 sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
216 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
217 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
218 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
219 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
220
221 auto& main_controller =
222 npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
223 auto& handheld_entry =
224 npad.handheld_states.npad[npad.handheld_states.common.last_entry_index];
225 auto& dual_entry = npad.dual_states.npad[npad.dual_states.common.last_entry_index];
226 auto& left_entry = npad.left_joy_states.npad[npad.left_joy_states.common.last_entry_index];
227 auto& right_entry =
228 npad.right_joy_states.npad[npad.right_joy_states.common.last_entry_index];
229 auto& pokeball_entry =
230 npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
231 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
232
233 if (hold_type == NpadHoldType::Horizontal) {
234 // TODO(ogniK): Remap buttons for different orientations
235 }
236 libnx_entry.connection_status.raw = 0;
237
238 switch (controller_type) {
239 case NPadControllerType::Handheld:
240 handheld_entry.connection_status.raw = 0;
241 handheld_entry.connection_status.IsConnected.Assign(1);
242 if (!Settings::values.use_docked_mode) {
243 handheld_entry.connection_status.IsWired.Assign(1);
244 }
245 handheld_entry.pad_states.raw = pad_state.raw;
246 handheld_entry.l_stick = lstick_entry;
247 handheld_entry.r_stick = rstick_entry;
248 break;
249 case NPadControllerType::JoyDual:
250 dual_entry.connection_status.raw = 0;
251
252 dual_entry.connection_status.IsLeftJoyConnected.Assign(1);
253 dual_entry.connection_status.IsRightJoyConnected.Assign(1);
254 dual_entry.connection_status.IsConnected.Assign(1);
255
256 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
257 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
258 libnx_entry.connection_status.IsConnected.Assign(1);
259
260 dual_entry.pad_states.raw = pad_state.raw;
261 dual_entry.l_stick = lstick_entry;
262 dual_entry.r_stick = rstick_entry;
263 case NPadControllerType::JoyLeft:
264 left_entry.connection_status.raw = 0;
265
266 left_entry.connection_status.IsConnected.Assign(1);
267 left_entry.pad_states.raw = pad_state.raw;
268 left_entry.l_stick = lstick_entry;
269 left_entry.r_stick = rstick_entry;
270 break;
271 case NPadControllerType::JoyRight:
272 right_entry.connection_status.raw = 0;
273
274 right_entry.connection_status.IsConnected.Assign(1);
275 right_entry.pad_states.raw = pad_state.raw;
276 right_entry.l_stick = lstick_entry;
277 right_entry.r_stick = rstick_entry;
278 break;
279 case NPadControllerType::Pokeball:
280 pokeball_entry.connection_status.raw = 0;
281
282 pokeball_entry.connection_status.IsConnected.Assign(1);
283 pokeball_entry.connection_status.IsWired.Assign(1);
284
285 pokeball_entry.pad_states.raw = pad_state.raw;
286 pokeball_entry.l_stick = lstick_entry;
287 pokeball_entry.r_stick = rstick_entry;
288 break;
289 case NPadControllerType::ProController:
290 main_controller.connection_status.raw = 0;
291
292 main_controller.connection_status.IsConnected.Assign(1);
293 main_controller.connection_status.IsWired.Assign(1);
294 main_controller.pad_states.raw = pad_state.raw;
295 main_controller.l_stick = lstick_entry;
296 main_controller.r_stick = rstick_entry;
297 break;
298 }
299
300 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
301 // any controllers.
302 libnx_entry.pad_states.raw = pad_state.raw;
303 libnx_entry.l_stick = lstick_entry;
304 libnx_entry.r_stick = rstick_entry;
305 }
306 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
307 shared_memory_entries.size() * sizeof(NPadEntry));
308} // namespace Service::HID
309
310void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
311 style.raw = style_set.raw;
312}
313
314Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const {
315 return style;
316}
317
318void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
319 ASSERT(length > 0 && (length % sizeof(u32)) == 0);
320 supported_npad_id_types.clear();
321 supported_npad_id_types.resize(length / sizeof(u32));
322 std::memcpy(supported_npad_id_types.data(), data, length);
323}
324
325void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
326 ASSERT(max_length < supported_npad_id_types.size());
327 std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size());
328}
329
330std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
331 return supported_npad_id_types.size();
332}
333
334void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
335 hold_type = joy_hold_type;
336}
337Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
338 return hold_type;
339}
340
341void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
342 ASSERT(npad_id < shared_memory_entries.size());
343 shared_memory_entries[npad_id].pad_assignment = assignment_mode;
344}
345
346void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
347 const std::vector<Vibration>& vibrations) {
348 if (!can_controllers_vibrate) {
349 return;
350 }
351 for (std::size_t i = 0; i < controller_ids.size(); i++) {
352 std::size_t controller_pos = i;
353 // Handheld controller conversion
354 if (controller_pos == 32) {
355 controller_pos = 8;
356 }
357 // Unknown controller conversion
358 if (controller_pos == 16) {
359 controller_pos = 9;
360 }
361 if (connected_controllers[controller_pos].is_connected) {
362 // TODO(ogniK): Vibrate the physical controller
363 }
364 }
365 LOG_WARNING(Service_HID, "(STUBBED) called");
366 last_processed_vibration = vibrations.back();
367}
368
369Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const {
370 return styleset_changed_event;
371}
372
373Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
374 return last_processed_vibration;
375}
376void Controller_NPad::AddNewController(NPadControllerType controller) {
377 if (controller == NPadControllerType::Handheld) {
378 connected_controllers[8] = {controller, true};
379 InitNewlyAddedControler(8);
380 return;
381 }
382 const auto pos =
383 std::find_if(connected_controllers.begin(), connected_controllers.end() - 2,
384 [](const ControllerHolder& holder) { return !holder.is_connected; });
385 if (pos == connected_controllers.end() - 2) {
386 LOG_ERROR(Service_HID, "Cannot connect any more controllers!");
387 return;
388 }
389 const auto controller_id = std::distance(connected_controllers.begin(), pos);
390 connected_controllers[controller_id] = {controller, true};
391 InitNewlyAddedControler(controller_id);
392}
393
394void Controller_NPad::ConnectNPad(u32 npad_id) {
395 if (npad_id >= connected_controllers.size())
396 return;
397 connected_controllers[npad_id].is_connected = true;
398}
399
400void Controller_NPad::DisconnectNPad(u32 npad_id) {
401 if (npad_id >= connected_controllers.size())
402 return;
403 connected_controllers[npad_id].is_connected = false;
404}
405
406Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
407 if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
408 // These are controllers without led patterns
409 return LedPattern{0, 0, 0, 0};
410 }
411 switch (npad_id) {
412 case 0:
413 return LedPattern{1, 0, 0, 0};
414 case 1:
415 return LedPattern{0, 1, 0, 0};
416 case 2:
417 return LedPattern{0, 0, 1, 0};
418 case 3:
419 return LedPattern{0, 0, 0, 1};
420 case 4:
421 return LedPattern{1, 0, 0, 1};
422 case 5:
423 return LedPattern{1, 0, 1, 0};
424 case 6:
425 return LedPattern{1, 0, 1, 1};
426 case 7:
427 return LedPattern{0, 1, 1, 0};
428 default:
429 UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
430 return LedPattern{0, 0, 0, 0};
431 };
432}
433void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
434 can_controllers_vibrate = can_vibrate;
435}
436} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
new file mode 100644
index 000000000..7c0f93acf
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -0,0 +1,287 @@
1// Copyright 2018 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 "common/common_types.h"
9#include "core/frontend/input.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11#include "core/settings.h"
12
13namespace Service::HID {
14
15class Controller_NPad final : public ControllerBase {
16public:
17 Controller_NPad();
18 ~Controller_NPad() override;
19
20 // Called when the controller is initialized
21 void OnInit() override;
22
23 // When the controller is released
24 void OnRelease() override;
25
26 // When the controller is requesting an update for the shared memory
27 void OnUpdate(u8* data, std::size_t size) override;
28
29 // Called when input devices should be loaded
30 void OnLoadInputDevices() override;
31
32 struct NPadType {
33 union {
34 u32_le raw{};
35
36 BitField<0, 1, u32_le> pro_controller;
37 BitField<1, 1, u32_le> handheld;
38 BitField<2, 1, u32_le> joycon_dual;
39 BitField<3, 1, u32_le> joycon_left;
40 BitField<4, 1, u32_le> joycon_right;
41
42 BitField<6, 1, u32_le> pokeball; // TODO(ogniK): Confirm when possible
43 };
44 };
45 static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
46
47 struct Vibration {
48 f32 amp_low;
49 f32 freq_low;
50 f32 amp_high;
51 f32 freq_high;
52 };
53 static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size");
54
55 enum class NpadHoldType : u64 {
56 Vertical = 0,
57 Horizontal = 1,
58 };
59
60 enum class NPadAssignments : u32_le {
61 Dual = 0,
62 Single = 1,
63 };
64
65 enum class NPadControllerType {
66 None,
67 ProController,
68 Handheld,
69 JoyDual,
70 JoyLeft,
71 JoyRight,
72 Pokeball,
73 };
74
75 struct LedPattern {
76 explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
77 position1.Assign(light1);
78 position1.Assign(light2);
79 position1.Assign(light3);
80 position1.Assign(light4);
81 }
82 union {
83 u64 raw{};
84 BitField<0, 1, u64> position1;
85 BitField<1, 1, u64> position2;
86 BitField<2, 1, u64> position3;
87 BitField<3, 1, u64> position4;
88 };
89 };
90
91 void SetSupportedStyleSet(NPadType style_set);
92 NPadType GetSupportedStyleSet() const;
93
94 void SetSupportedNPadIdTypes(u8* data, std::size_t length);
95 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
96 std::size_t GetSupportedNPadIdTypesSize() const;
97
98 void SetHoldType(NpadHoldType joy_hold_type);
99 NpadHoldType GetHoldType() const;
100
101 void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode);
102
103 void VibrateController(const std::vector<u32>& controller_ids,
104 const std::vector<Vibration>& vibrations);
105
106 Kernel::SharedPtr<Kernel::Event> GetStyleSetChangedEvent() const;
107 Vibration GetLastVibration() const;
108
109 void AddNewController(NPadControllerType controller);
110
111 void ConnectNPad(u32 npad_id);
112 void DisconnectNPad(u32 npad_id);
113 LedPattern GetLedPattern(u32 npad_id);
114 void SetVibrationEnabled(bool can_vibrate);
115
116private:
117 struct CommonHeader {
118 s64_le timestamp;
119 s64_le total_entry_count;
120 s64_le last_entry_index;
121 s64_le entry_count;
122 };
123 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
124
125 struct ControllerColor {
126 u32_le body_color;
127 u32_le button_color;
128 };
129 static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size");
130
131 struct ControllerPadState {
132 union {
133 u64_le raw{};
134 // Button states
135 BitField<0, 1, u64_le> a;
136 BitField<1, 1, u64_le> b;
137 BitField<2, 1, u64_le> x;
138 BitField<3, 1, u64_le> y;
139 BitField<4, 1, u64_le> l_stick;
140 BitField<5, 1, u64_le> r_stick;
141 BitField<6, 1, u64_le> l;
142 BitField<7, 1, u64_le> r;
143 BitField<8, 1, u64_le> zl;
144 BitField<9, 1, u64_le> zr;
145 BitField<10, 1, u64_le> plus;
146 BitField<11, 1, u64_le> minus;
147
148 // D-Pad
149 BitField<12, 1, u64_le> d_left;
150 BitField<13, 1, u64_le> d_up;
151 BitField<14, 1, u64_le> d_right;
152 BitField<15, 1, u64_le> d_down;
153
154 // Left JoyStick
155 BitField<16, 1, u64_le> l_stick_left;
156 BitField<17, 1, u64_le> l_stick_up;
157 BitField<18, 1, u64_le> l_stick_right;
158 BitField<19, 1, u64_le> l_stick_down;
159
160 // Right JoyStick
161 BitField<20, 1, u64_le> r_stick_left;
162 BitField<21, 1, u64_le> r_stick_up;
163 BitField<22, 1, u64_le> r_stick_right;
164 BitField<23, 1, u64_le> r_stick_down;
165
166 // Not always active?
167 BitField<24, 1, u64_le> sl;
168 BitField<25, 1, u64_le> sr;
169 };
170 };
171 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
172
173 struct AnalogPosition {
174 s32_le x;
175 s32_le y;
176 };
177 static_assert(sizeof(AnalogPosition) == 8, "AnalogPosition is an invalid size");
178
179 struct ConnectionState {
180 union {
181 u32_le raw{};
182 BitField<0, 1, u32_le> IsConnected;
183 BitField<1, 1, u32_le> IsWired;
184 BitField<2, 1, u32_le> IsLeftJoyConnected;
185 BitField<3, 1, u32_le> IsLeftJoyWired;
186 BitField<4, 1, u32_le> IsRightJoyConnected;
187 BitField<5, 1, u32_le> IsRightJoyWired;
188 };
189 };
190 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
191
192 struct GenericStates {
193 s64_le timestamp;
194 s64_le timestamp2;
195 ControllerPadState pad_states;
196 AnalogPosition l_stick;
197 AnalogPosition r_stick;
198 ConnectionState connection_status;
199 };
200 static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
201
202 struct NPadGeneric {
203 CommonHeader common;
204 std::array<GenericStates, 17> npad;
205 };
206 static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
207
208 enum class ColorReadError : u32_le {
209 ReadOk = 0,
210 ColorDoesntExist = 1,
211 NoController = 2,
212 };
213
214 struct NPadProperties {
215 union {
216 s64_le raw{};
217 BitField<11, 1, s64_le> is_vertical;
218 BitField<12, 1, s64_le> is_horizontal;
219 BitField<13, 1, s64_le> use_plus;
220 BitField<14, 1, s64_le> use_minus;
221 };
222 };
223
224 struct NPadDevice {
225 union {
226 u32_le raw{};
227 BitField<0, 1, s32_le> pro_controller;
228 BitField<1, 1, s32_le> handheld;
229 BitField<2, 1, s32_le> handheld_left;
230 BitField<3, 1, s32_le> handheld_right;
231 BitField<4, 1, s32_le> joycon_left;
232 BitField<5, 1, s32_le> joycon_right;
233 BitField<6, 1, s32_le> pokeball;
234 };
235 };
236
237 struct NPadEntry {
238 NPadType joy_styles;
239 NPadAssignments pad_assignment;
240
241 ColorReadError single_color_error;
242 ControllerColor single_color;
243
244 ColorReadError dual_color_error;
245 ControllerColor left_color;
246 ControllerColor right_color;
247
248 NPadGeneric main_controller_states;
249 NPadGeneric handheld_states;
250 NPadGeneric dual_states;
251 NPadGeneric left_joy_states;
252 NPadGeneric right_joy_states;
253 NPadGeneric pokeball_states;
254 NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be
255 // relying on this for the time being
256 INSERT_PADDING_BYTES(
257 0x708 *
258 6); // TODO(ogniK): SixAxis states, require more information before implementation
259 NPadDevice device_type;
260 NPadProperties properties;
261 INSERT_PADDING_WORDS(1);
262 std::array<u32, 3> battery_level;
263 INSERT_PADDING_BYTES(0x5c);
264 INSERT_PADDING_BYTES(0xdf8);
265 };
266 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
267
268 struct ControllerHolder {
269 Controller_NPad::NPadControllerType type;
270 bool is_connected;
271 };
272
273 NPadType style{};
274 std::array<NPadEntry, 10> shared_memory_entries{};
275 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
276 buttons;
277 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
278 std::vector<u32> supported_npad_id_types{};
279 NpadHoldType hold_type{NpadHoldType::Vertical};
280 Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
281 Vibration last_processed_vibration{};
282 std::array<ControllerHolder, 10> connected_controllers{};
283 bool can_controllers_vibrate{true};
284
285 void InitNewlyAddedControler(std::size_t controller_idx);
286};
287} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
new file mode 100644
index 000000000..02fcfadd9
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -0,0 +1,39 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/stubbed.h"
9
10namespace Service::HID {
11
12Controller_Stubbed::Controller_Stubbed() = default;
13Controller_Stubbed::~Controller_Stubbed() = default;
14
15void Controller_Stubbed::OnInit() {}
16
17void Controller_Stubbed::OnRelease() {}
18
19void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) {
20 if (!smart_update) {
21 return;
22 }
23
24 CommonHeader header{};
25 header.timestamp = CoreTiming::GetTicks();
26 header.total_entry_count = 17;
27 header.entry_count = 0;
28 header.last_entry_index = 0;
29
30 std::memcpy(data + common_offset, &header, sizeof(CommonHeader));
31}
32
33void Controller_Stubbed::OnLoadInputDevices() {}
34
35void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
36 common_offset = off;
37 smart_update = true;
38}
39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
new file mode 100644
index 000000000..4a21c643e
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -0,0 +1,34 @@
1// Copyright 2018 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 "common/common_types.h"
8#include "core/hle/service/hid/controllers/controller_base.h"
9
10namespace Service::HID {
11class Controller_Stubbed final : public ControllerBase {
12public:
13 Controller_Stubbed();
14 ~Controller_Stubbed() override;
15
16 // Called when the controller is initialized
17 void OnInit() override;
18
19 // When the controller is released
20 void OnRelease() override;
21
22 // When the controller is requesting an update for the shared memory
23 void OnUpdate(u8* data, std::size_t size) override;
24
25 // Called when input devices should be loaded
26 void OnLoadInputDevices() override;
27
28 void SetCommonHeaderOffset(std::size_t off);
29
30private:
31 bool smart_update{};
32 std::size_t common_offset{};
33};
34} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
new file mode 100644
index 000000000..43efef803
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -0,0 +1,65 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/frontend/emu_window.h"
9#include "core/frontend/input.h"
10#include "core/hle/service/hid/controllers/touchscreen.h"
11#include "core/settings.h"
12
13namespace Service::HID {
14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
15
16Controller_Touchscreen::Controller_Touchscreen() = default;
17Controller_Touchscreen::~Controller_Touchscreen() = default;
18
19void Controller_Touchscreen::OnInit() {}
20
21void Controller_Touchscreen::OnRelease() {}
22
23void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
24 shared_memory.header.timestamp = CoreTiming::GetTicks();
25 shared_memory.header.total_entry_count = 17;
26
27 if (!IsControllerActivated()) {
28 shared_memory.header.entry_count = 0;
29 shared_memory.header.last_entry_index = 0;
30 return;
31 }
32 shared_memory.header.entry_count = 16;
33
34 const auto& last_entry =
35 shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
36 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
37 auto& cur_entry = shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
38
39 cur_entry.sampling_number = last_entry.sampling_number + 1;
40 cur_entry.sampling_number2 = cur_entry.sampling_number;
41
42 const auto [x, y, pressed] = touch_device->GetStatus();
43 auto& touch_entry = cur_entry.states[0];
44 if (pressed) {
45 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
46 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
47 touch_entry.diameter_x = 15;
48 touch_entry.diameter_y = 15;
49 touch_entry.rotation_angle = 0;
50 const u64 tick = CoreTiming::GetTicks();
51 touch_entry.delta_time = tick - last_touch;
52 last_touch = tick;
53 touch_entry.finger = 0;
54 cur_entry.entry_count = 1;
55 } else {
56 cur_entry.entry_count = 0;
57 }
58
59 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
60}
61
62void Controller_Touchscreen::OnLoadInputDevices() {
63 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
64}
65} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
new file mode 100644
index 000000000..e5db6e6ba
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -0,0 +1,63 @@
1// Copyright 2018 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 "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/frontend/input.h"
11#include "core/hle/service/hid/controllers/controller_base.h"
12
13namespace Service::HID {
14class Controller_Touchscreen final : public ControllerBase {
15public:
16 Controller_Touchscreen();
17 ~Controller_Touchscreen() override;
18
19 // Called when the controller is initialized
20 void OnInit() override;
21
22 // When the controller is released
23 void OnRelease() override;
24
25 // When the controller is requesting an update for the shared memory
26 void OnUpdate(u8* data, std::size_t size) override;
27
28 // Called when input devices should be loaded
29 void OnLoadInputDevices() override;
30
31private:
32 struct TouchState {
33 u64_le delta_time;
34 u32_le attribute;
35 u32_le finger;
36 u32_le x;
37 u32_le y;
38 u32_le diameter_x;
39 u32_le diameter_y;
40 u32_le rotation_angle;
41 };
42 static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
43
44 struct TouchScreenEntry {
45 s64_le sampling_number;
46 s64_le sampling_number2;
47 s32_le entry_count;
48 std::array<TouchState, 16> states;
49 };
50 static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
51
52 struct TouchScreenSharedMemory {
53 CommonHeader header;
54 std::array<TouchScreenEntry, 17> shared_memory_entries{};
55 INSERT_PADDING_BYTES(0x3c8);
56 };
57 static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
58 "TouchScreenSharedMemory is an invalid size");
59 TouchScreenSharedMemory shared_memory{};
60 std::unique_ptr<Input::TouchDevice> touch_device;
61 s64_le last_touch{};
62};
63} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
new file mode 100644
index 000000000..cd397c70b
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -0,0 +1,45 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/xpad.h"
9
10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
12
13Controller_XPad::Controller_XPad() = default;
14Controller_XPad::~Controller_XPad() = default;
15
16void Controller_XPad::OnInit() {}
17
18void Controller_XPad::OnRelease() {}
19
20void Controller_XPad::OnUpdate(u8* data, std::size_t size) {
21 for (auto& xpad_entry : shared_memory.shared_memory_entries) {
22 xpad_entry.header.timestamp = CoreTiming::GetTicks();
23 xpad_entry.header.total_entry_count = 17;
24
25 if (!IsControllerActivated()) {
26 xpad_entry.header.entry_count = 0;
27 xpad_entry.header.last_entry_index = 0;
28 return;
29 }
30 xpad_entry.header.entry_count = 16;
31
32 const auto& last_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
33 xpad_entry.header.last_entry_index = (xpad_entry.header.last_entry_index + 1) % 17;
34 auto& cur_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
35
36 cur_entry.sampling_number = last_entry.sampling_number + 1;
37 cur_entry.sampling_number2 = cur_entry.sampling_number;
38 }
39 // TODO(ogniK): Update xpad states
40
41 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
42}
43
44void Controller_XPad::OnLoadInputDevices() {}
45} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
new file mode 100644
index 000000000..ff836989f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -0,0 +1,60 @@
1// Copyright 2018 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 "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11
12namespace Service::HID {
13class Controller_XPad final : public ControllerBase {
14public:
15 Controller_XPad();
16 ~Controller_XPad() override;
17
18 // Called when the controller is initialized
19 void OnInit() override;
20
21 // When the controller is released
22 void OnRelease() override;
23
24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, std::size_t size) override;
26
27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override;
29
30private:
31 struct AnalogStick {
32 s32_le x;
33 s32_le y;
34 };
35 static_assert(sizeof(AnalogStick) == 0x8, "AnalogStick is an invalid size");
36
37 struct XPadState {
38 s64_le sampling_number;
39 s64_le sampling_number2;
40 s32_le attributes;
41 u32_le pad_states;
42 AnalogStick x_stick;
43 AnalogStick y_stick;
44 };
45 static_assert(sizeof(XPadState) == 0x28, "XPadState is an invalid size");
46
47 struct XPadEntry {
48 CommonHeader header;
49 std::array<XPadState, 17> pad_states{};
50 INSERT_PADDING_BYTES(0x138);
51 };
52 static_assert(sizeof(XPadEntry) == 0x400, "XPadEntry is an invalid size");
53
54 struct SharedMemory {
55 std::array<XPadEntry, 4> shared_memory_entries{};
56 };
57 static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
58 SharedMemory shared_memory{};
59};
60} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 7c6b0a4e6..beb89218a 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.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 <array>
6#include "common/common_types.h"
5#include "common/logging/log.h" 7#include "common/logging/log.h"
6#include "core/core.h" 8#include "core/core.h"
7#include "core/core_timing.h" 9#include "core/core_timing.h"
@@ -19,6 +21,16 @@
19#include "core/hle/service/service.h" 21#include "core/hle/service/service.h"
20#include "core/settings.h" 22#include "core/settings.h"
21 23
24#include "core/hle/service/hid/controllers/controller_base.h"
25#include "core/hle/service/hid/controllers/debug_pad.h"
26#include "core/hle/service/hid/controllers/gesture.h"
27#include "core/hle/service/hid/controllers/keyboard.h"
28#include "core/hle/service/hid/controllers/mouse.h"
29#include "core/hle/service/hid/controllers/npad.h"
30#include "core/hle/service/hid/controllers/stubbed.h"
31#include "core/hle/service/hid/controllers/touchscreen.h"
32#include "core/hle/service/hid/controllers/xpad.h"
33
22namespace Service::HID { 34namespace Service::HID {
23 35
24// Updating period for each HID device. 36// Updating period for each HID device.
@@ -26,6 +38,22 @@ namespace Service::HID {
26constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 38constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
27constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 39constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
28constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 40constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
42enum class HidController : std::size_t {
43 DebugPad,
44 Touchscreen,
45 Mouse,
46 Keyboard,
47 XPad,
48 Unknown1,
49 Unknown2,
50 Unknown3,
51 SixAxisSensor,
52 NPad,
53 Gesture,
54
55 MaxControllers,
56};
29 57
30class IAppletResource final : public ServiceFramework<IAppletResource> { 58class IAppletResource final : public ServiceFramework<IAppletResource> {
31public: 59public:
@@ -37,19 +65,57 @@ public:
37 65
38 auto& kernel = Core::System::GetInstance().Kernel(); 66 auto& kernel = Core::System::GetInstance().Kernel();
39 shared_mem = Kernel::SharedMemory::Create( 67 shared_mem = Kernel::SharedMemory::Create(
40 kernel, nullptr, 0x40000, Kernel::MemoryPermission::ReadWrite, 68 kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
41 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); 69 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
42 70
71 MakeController<Controller_DebugPad>(HidController::DebugPad);
72 MakeController<Controller_Touchscreen>(HidController::Touchscreen);
73 MakeController<Controller_Mouse>(HidController::Mouse);
74 MakeController<Controller_Keyboard>(HidController::Keyboard);
75 MakeController<Controller_XPad>(HidController::XPad);
76 MakeController<Controller_Stubbed>(HidController::Unknown1);
77 MakeController<Controller_Stubbed>(HidController::Unknown2);
78 MakeController<Controller_Stubbed>(HidController::Unknown3);
79 MakeController<Controller_Stubbed>(HidController::SixAxisSensor);
80 MakeController<Controller_NPad>(HidController::NPad);
81 MakeController<Controller_Gesture>(HidController::Gesture);
82
83 // Homebrew doesn't try to activate some controllers, so we activate them by default
84 GetController<Controller_NPad>(HidController::NPad).ActivateController();
85 GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
86
87 GetController<Controller_Stubbed>(HidController::Unknown1).SetCommonHeaderOffset(0x4c00);
88 GetController<Controller_Stubbed>(HidController::Unknown2).SetCommonHeaderOffset(0x4e00);
89 GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
90
43 // Register update callbacks 91 // Register update callbacks
44 pad_update_event = CoreTiming::RegisterEvent( 92 pad_update_event = CoreTiming::RegisterEvent(
45 "HID::UpdatePadCallback", 93 "HID::UpdatePadCallback",
46 [this](u64 userdata, int cycles_late) { UpdatePadCallback(userdata, cycles_late); }); 94 [this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); });
47 95
48 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) 96 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
49 97
50 CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); 98 CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
51 } 99 }
52 100
101 void ActivateController(HidController controller) {
102 controllers[static_cast<size_t>(controller)]->ActivateController();
103 }
104
105 void DeactivateController(HidController controller) {
106 controllers[static_cast<size_t>(controller)]->DeactivateController();
107 }
108
109 template <typename T>
110 void MakeController(HidController controller) {
111 controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>();
112 }
113
114 template <typename T>
115 T& GetController(HidController controller) {
116 return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
117 }
118
53 ~IAppletResource() { 119 ~IAppletResource() {
54 CoreTiming::UnscheduleEvent(pad_update_event, 0); 120 CoreTiming::UnscheduleEvent(pad_update_event, 0);
55 } 121 }
@@ -62,200 +128,15 @@ private:
62 LOG_DEBUG(Service_HID, "called"); 128 LOG_DEBUG(Service_HID, "called");
63 } 129 }
64 130
65 void LoadInputDevices() { 131 void UpdateControllers(u64 userdata, int cycles_late) {
66 std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, 132 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
67 Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, 133 for (const auto& controller : controllers) {
68 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); 134 if (should_reload) {
69 std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 135 controller->OnLoadInputDevices();
70 Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
71 sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
72 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
73 // TODO(shinyquagsire23): gyro, mouse, keyboard
74 }
75
76 void UpdatePadCallback(u64 userdata, int cycles_late) {
77 SharedMemory mem{};
78 std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory));
79
80 if (Settings::values.is_device_reload_pending.exchange(false))
81 LoadInputDevices();
82
83 // Set up controllers as neon red+blue Joy-Con attached to console
84 ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header;
85 controller_header.type = ControllerType_Handheld;
86 controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent;
87 controller_header.right_color_body = JOYCON_BODY_NEON_RED;
88 controller_header.right_color_buttons = JOYCON_BUTTONS_NEON_RED;
89 controller_header.left_color_body = JOYCON_BODY_NEON_BLUE;
90 controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE;
91
92 for (std::size_t controller = 0; controller < mem.controllers.size(); controller++) {
93 for (auto& layout : mem.controllers[controller].layouts) {
94 layout.header.num_entries = HID_NUM_ENTRIES;
95 layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
96
97 // HID shared memory stores the state of the past 17 samples in a circlular buffer,
98 // each with a timestamp in number of samples since boot.
99 const ControllerInputEntry& last_entry = layout.entries[layout.header.latest_entry];
100
101 layout.header.timestamp_ticks = CoreTiming::GetTicks();
102 layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES;
103
104 ControllerInputEntry& entry = layout.entries[layout.header.latest_entry];
105 entry.timestamp = last_entry.timestamp + 1;
106 // TODO(shinyquagsire23): Is this always identical to timestamp?
107 entry.timestamp_2 = entry.timestamp;
108
109 // TODO(shinyquagsire23): More than just handheld input
110 if (controller != Controller_Handheld)
111 continue;
112
113 entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
114
115 // TODO(shinyquagsire23): Set up some LUTs for each layout mapping in the future?
116 // For now everything is just the default handheld layout, but split Joy-Con will
117 // rotate the face buttons and directions for certain layouts.
118 ControllerPadState& state = entry.buttons;
119 using namespace Settings::NativeButton;
120 state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
121 state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
122 state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
123 state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
124 state.lstick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
125 state.rstick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
126 state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
127 state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
128 state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
129 state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
130 state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
131 state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
132
133 state.dleft.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
134 state.dup.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
135 state.dright.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
136 state.ddown.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
137
138 state.lstick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
139 state.lstick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
140 state.lstick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
141 state.lstick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
142
143 state.rstick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
144 state.rstick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
145 state.rstick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
146 state.rstick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
147
148 state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
149 state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
150
151 const auto [stick_l_x_f, stick_l_y_f] = sticks[Joystick_Left]->GetStatus();
152 const auto [stick_r_x_f, stick_r_y_f] = sticks[Joystick_Right]->GetStatus();
153 entry.joystick_left_x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
154 entry.joystick_left_y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
155 entry.joystick_right_x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
156 entry.joystick_right_y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
157 } 136 }
137 controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
158 } 138 }
159 139
160 TouchScreen& touchscreen = mem.touchscreen;
161 const u64 last_entry = touchscreen.header.latest_entry;
162 const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size();
163 const u64 timestamp = CoreTiming::GetTicks();
164 const u64 sample_counter = touchscreen.entries[last_entry].header.timestamp + 1;
165 touchscreen.header.timestamp_ticks = timestamp;
166 touchscreen.header.num_entries = touchscreen.entries.size();
167 touchscreen.header.latest_entry = curr_entry;
168 touchscreen.header.max_entry_index = touchscreen.entries.size();
169 touchscreen.header.timestamp = timestamp;
170 touchscreen.entries[curr_entry].header.timestamp = sample_counter;
171
172 TouchScreenEntryTouch touch_entry{};
173 auto [x, y, pressed] = touch_device->GetStatus();
174 touch_entry.timestamp = timestamp;
175 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
176 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
177 touch_entry.touch_index = 0;
178
179 // TODO(DarkLordZach): Maybe try to derive these from EmuWindow?
180 touch_entry.diameter_x = 15;
181 touch_entry.diameter_y = 15;
182 touch_entry.angle = 0;
183
184 // TODO(DarkLordZach): Implement multi-touch support
185 if (pressed) {
186 touchscreen.entries[curr_entry].header.num_touches = 1;
187 touchscreen.entries[curr_entry].touches[0] = touch_entry;
188 } else {
189 touchscreen.entries[curr_entry].header.num_touches = 0;
190 }
191
192 // TODO(shinyquagsire23): Properly implement mouse
193 Mouse& mouse = mem.mouse;
194 const u64 last_mouse_entry = mouse.header.latest_entry;
195 const u64 curr_mouse_entry = (mouse.header.latest_entry + 1) % mouse.entries.size();
196 const u64 mouse_sample_counter = mouse.entries[last_mouse_entry].timestamp + 1;
197 mouse.header.timestamp_ticks = timestamp;
198 mouse.header.num_entries = mouse.entries.size();
199 mouse.header.max_entry_index = mouse.entries.size();
200 mouse.header.latest_entry = curr_mouse_entry;
201
202 mouse.entries[curr_mouse_entry].timestamp = mouse_sample_counter;
203 mouse.entries[curr_mouse_entry].timestamp_2 = mouse_sample_counter;
204
205 // TODO(shinyquagsire23): Properly implement keyboard
206 Keyboard& keyboard = mem.keyboard;
207 const u64 last_keyboard_entry = keyboard.header.latest_entry;
208 const u64 curr_keyboard_entry =
209 (keyboard.header.latest_entry + 1) % keyboard.entries.size();
210 const u64 keyboard_sample_counter = keyboard.entries[last_keyboard_entry].timestamp + 1;
211 keyboard.header.timestamp_ticks = timestamp;
212 keyboard.header.num_entries = keyboard.entries.size();
213 keyboard.header.latest_entry = last_keyboard_entry;
214 keyboard.header.max_entry_index = keyboard.entries.size();
215
216 keyboard.entries[curr_keyboard_entry].timestamp = keyboard_sample_counter;
217 keyboard.entries[curr_keyboard_entry].timestamp_2 = keyboard_sample_counter;
218
219 // TODO(shinyquagsire23): Figure out what any of these are
220 for (auto& input : mem.unk_input_1) {
221 const u64 last_input_entry = input.header.latest_entry;
222 const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size();
223 const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1;
224
225 input.header.timestamp_ticks = timestamp;
226 input.header.num_entries = input.entries.size();
227 input.header.latest_entry = last_input_entry;
228 input.header.max_entry_index = input.entries.size();
229
230 input.entries[curr_input_entry].timestamp = input_sample_counter;
231 input.entries[curr_input_entry].timestamp_2 = input_sample_counter;
232 }
233
234 for (auto& input : mem.unk_input_2) {
235 input.header.timestamp_ticks = timestamp;
236 input.header.num_entries = 17;
237 input.header.latest_entry = 0;
238 input.header.max_entry_index = 0;
239 }
240
241 UnkInput3& input = mem.unk_input_3;
242 const u64 last_input_entry = input.header.latest_entry;
243 const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size();
244 const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1;
245
246 input.header.timestamp_ticks = timestamp;
247 input.header.num_entries = input.entries.size();
248 input.header.latest_entry = last_input_entry;
249 input.header.max_entry_index = input.entries.size();
250
251 input.entries[curr_input_entry].timestamp = input_sample_counter;
252 input.entries[curr_input_entry].timestamp_2 = input_sample_counter;
253
254 // TODO(shinyquagsire23): Signal events
255
256 std::memcpy(shared_mem->GetPointer(), &mem, sizeof(SharedMemory));
257
258 // Reschedule recurrent event
259 CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); 140 CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
260 } 141 }
261 142
@@ -265,11 +146,8 @@ private:
265 // CoreTiming update events 146 // CoreTiming update events
266 CoreTiming::EventType* pad_update_event; 147 CoreTiming::EventType* pad_update_event;
267 148
268 // Stored input state info 149 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
269 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 150 controllers{};
270 buttons;
271 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
272 std::unique_ptr<Input::TouchDevice> touch_device;
273}; 151};
274 152
275class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { 153class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
@@ -299,9 +177,10 @@ public:
299 {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, 177 {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
300 {21, &Hid::ActivateMouse, "ActivateMouse"}, 178 {21, &Hid::ActivateMouse, "ActivateMouse"},
301 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, 179 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
180 {32, nullptr, "SendKeyboardLockKeyEvent"},
302 {40, nullptr, "AcquireXpadIdEventHandle"}, 181 {40, nullptr, "AcquireXpadIdEventHandle"},
303 {41, nullptr, "ReleaseXpadIdEventHandle"}, 182 {41, nullptr, "ReleaseXpadIdEventHandle"},
304 {51, nullptr, "ActivateXpad"}, 183 {51, &Hid::ActivateXpad, "ActivateXpad"},
305 {55, nullptr, "GetXpadIds"}, 184 {55, nullptr, "GetXpadIds"},
306 {56, nullptr, "ActivateJoyXpad"}, 185 {56, nullptr, "ActivateJoyXpad"},
307 {58, nullptr, "GetJoyXpadLifoHandle"}, 186 {58, nullptr, "GetJoyXpadLifoHandle"},
@@ -329,6 +208,7 @@ public:
329 {80, nullptr, "GetGyroscopeZeroDriftMode"}, 208 {80, nullptr, "GetGyroscopeZeroDriftMode"},
330 {81, nullptr, "ResetGyroscopeZeroDriftMode"}, 209 {81, nullptr, "ResetGyroscopeZeroDriftMode"},
331 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, 210 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
211 {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"},
332 {91, &Hid::ActivateGesture, "ActivateGesture"}, 212 {91, &Hid::ActivateGesture, "ActivateGesture"},
333 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, 213 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
334 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, 214 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
@@ -362,8 +242,8 @@ public:
362 {206, &Hid::SendVibrationValues, "SendVibrationValues"}, 242 {206, &Hid::SendVibrationValues, "SendVibrationValues"},
363 {207, nullptr, "SendVibrationGcErmCommand"}, 243 {207, nullptr, "SendVibrationGcErmCommand"},
364 {208, nullptr, "GetActualVibrationGcErmCommand"}, 244 {208, nullptr, "GetActualVibrationGcErmCommand"},
365 {209, nullptr, "BeginPermitVibrationSession"}, 245 {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
366 {210, nullptr, "EndPermitVibrationSession"}, 246 {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
367 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, 247 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
368 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, 248 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
369 {302, nullptr, "StopConsoleSixAxisSensor"}, 249 {302, nullptr, "StopConsoleSixAxisSensor"},
@@ -374,6 +254,7 @@ public:
374 {307, nullptr, "FinalizeSevenSixAxisSensor"}, 254 {307, nullptr, "FinalizeSevenSixAxisSensor"},
375 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, 255 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
376 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, 256 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
257 {310, nullptr, "ResetSevenSixAxisSensorTimestamp"},
377 {400, nullptr, "IsUsbFullKeyControllerEnabled"}, 258 {400, nullptr, "IsUsbFullKeyControllerEnabled"},
378 {401, nullptr, "EnableUsbFullKeyController"}, 259 {401, nullptr, "EnableUsbFullKeyController"},
379 {402, nullptr, "IsUsbFullKeyControllerConnected"}, 260 {402, nullptr, "IsUsbFullKeyControllerConnected"},
@@ -389,28 +270,35 @@ public:
389 {505, nullptr, "SetPalmaFrModeType"}, 270 {505, nullptr, "SetPalmaFrModeType"},
390 {506, nullptr, "ReadPalmaStep"}, 271 {506, nullptr, "ReadPalmaStep"},
391 {507, nullptr, "EnablePalmaStep"}, 272 {507, nullptr, "EnablePalmaStep"},
392 {508, nullptr, "SuspendPalmaStep"}, 273 {508, nullptr, "ResetPalmaStep"},
393 {509, nullptr, "ResetPalmaStep"}, 274 {509, nullptr, "ReadPalmaApplicationSection"},
394 {510, nullptr, "ReadPalmaApplicationSection"}, 275 {510, nullptr, "WritePalmaApplicationSection"},
395 {511, nullptr, "WritePalmaApplicationSection"}, 276 {511, nullptr, "ReadPalmaUniqueCode"},
396 {512, nullptr, "ReadPalmaUniqueCode"}, 277 {512, nullptr, "SetPalmaUniqueCodeInvalid"},
397 {513, nullptr, "SetPalmaUniqueCodeInvalid"}, 278 {513, nullptr, "WritePalmaActivityEntry"},
279 {514, nullptr, "WritePalmaRgbLedPatternEntry"},
280 {515, nullptr, "WritePalmaWaveEntry"},
281 {516, nullptr, "SetPalmaDataBaseIdentificationVersion"},
282 {517, nullptr, "GetPalmaDataBaseIdentificationVersion"},
283 {518, nullptr, "SuspendPalmaFeature"},
284 {519, nullptr, "GetPalmaOperationResult"},
285 {520, nullptr, "ReadPalmaPlayLog"},
286 {521, nullptr, "ResetPalmaPlayLog"},
287 {522, nullptr, "SetIsPalmaAllConnectable"},
288 {523, nullptr, "SetIsPalmaPairedConnectable"},
289 {524, nullptr, "PairPalma"},
290 {525, nullptr, "SetPalmaBoostMode"},
398 {1000, nullptr, "SetNpadCommunicationMode"}, 291 {1000, nullptr, "SetNpadCommunicationMode"},
399 {1001, nullptr, "GetNpadCommunicationMode"}, 292 {1001, nullptr, "GetNpadCommunicationMode"},
400 }; 293 };
401 // clang-format on 294 // clang-format on
402 295
403 RegisterHandlers(functions); 296 RegisterHandlers(functions);
404
405 auto& kernel = Core::System::GetInstance().Kernel();
406 event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "hid:EventHandle");
407 } 297 }
408 ~Hid() = default; 298 ~Hid() = default;
409 299
410private: 300private:
411 std::shared_ptr<IAppletResource> applet_resource; 301 std::shared_ptr<IAppletResource> applet_resource;
412 u32 joy_hold_type{0};
413 Kernel::SharedPtr<Kernel::Event> event;
414 302
415 void CreateAppletResource(Kernel::HLERequestContext& ctx) { 303 void CreateAppletResource(Kernel::HLERequestContext& ctx) {
416 if (applet_resource == nullptr) { 304 if (applet_resource == nullptr) {
@@ -423,31 +311,59 @@ private:
423 LOG_DEBUG(Service_HID, "called"); 311 LOG_DEBUG(Service_HID, "called");
424 } 312 }
425 313
314 void ActivateXpad(Kernel::HLERequestContext& ctx) {
315 applet_resource->ActivateController(HidController::XPad);
316 IPC::ResponseBuilder rb{ctx, 2};
317 rb.Push(RESULT_SUCCESS);
318 LOG_DEBUG(Service_HID, "called");
319 }
320
426 void ActivateDebugPad(Kernel::HLERequestContext& ctx) { 321 void ActivateDebugPad(Kernel::HLERequestContext& ctx) {
322 applet_resource->ActivateController(HidController::DebugPad);
427 IPC::ResponseBuilder rb{ctx, 2}; 323 IPC::ResponseBuilder rb{ctx, 2};
428 rb.Push(RESULT_SUCCESS); 324 rb.Push(RESULT_SUCCESS);
429 LOG_WARNING(Service_HID, "(STUBBED) called"); 325 LOG_DEBUG(Service_HID, "called");
430 } 326 }
431 327
432 void ActivateTouchScreen(Kernel::HLERequestContext& ctx) { 328 void ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
329 applet_resource->ActivateController(HidController::Touchscreen);
433 IPC::ResponseBuilder rb{ctx, 2}; 330 IPC::ResponseBuilder rb{ctx, 2};
434 rb.Push(RESULT_SUCCESS); 331 rb.Push(RESULT_SUCCESS);
435 LOG_WARNING(Service_HID, "(STUBBED) called"); 332 LOG_DEBUG(Service_HID, "called");
436 } 333 }
437 334
438 void ActivateMouse(Kernel::HLERequestContext& ctx) { 335 void ActivateMouse(Kernel::HLERequestContext& ctx) {
336 applet_resource->ActivateController(HidController::Mouse);
439 IPC::ResponseBuilder rb{ctx, 2}; 337 IPC::ResponseBuilder rb{ctx, 2};
440 rb.Push(RESULT_SUCCESS); 338 rb.Push(RESULT_SUCCESS);
441 LOG_WARNING(Service_HID, "(STUBBED) called"); 339 LOG_DEBUG(Service_HID, "called");
442 } 340 }
443 341
444 void ActivateKeyboard(Kernel::HLERequestContext& ctx) { 342 void ActivateKeyboard(Kernel::HLERequestContext& ctx) {
343 applet_resource->ActivateController(HidController::Keyboard);
445 IPC::ResponseBuilder rb{ctx, 2}; 344 IPC::ResponseBuilder rb{ctx, 2};
446 rb.Push(RESULT_SUCCESS); 345 rb.Push(RESULT_SUCCESS);
447 LOG_WARNING(Service_HID, "(STUBBED) called"); 346 LOG_DEBUG(Service_HID, "called");
347 }
348
349 void ActivateGesture(Kernel::HLERequestContext& ctx) {
350 applet_resource->ActivateController(HidController::Gesture);
351 IPC::ResponseBuilder rb{ctx, 2};
352 rb.Push(RESULT_SUCCESS);
353 LOG_DEBUG(Service_HID, "called");
354 }
355
356 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
357 // Should have no effect with how our npad sets up the data
358 applet_resource->ActivateController(HidController::NPad);
359 IPC::ResponseBuilder rb{ctx, 2};
360 rb.Push(RESULT_SUCCESS);
361 LOG_DEBUG(Service_HID, "called");
448 } 362 }
449 363
450 void StartSixAxisSensor(Kernel::HLERequestContext& ctx) { 364 void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
365 IPC::RequestParser rp{ctx};
366 auto handle = rp.PopRaw<u32>();
451 IPC::ResponseBuilder rb{ctx, 2}; 367 IPC::ResponseBuilder rb{ctx, 2};
452 rb.Push(RESULT_SUCCESS); 368 rb.Push(RESULT_SUCCESS);
453 LOG_WARNING(Service_HID, "(STUBBED) called"); 369 LOG_WARNING(Service_HID, "(STUBBED) called");
@@ -468,84 +384,168 @@ private:
468 } 384 }
469 385
470 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 386 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
387 IPC::RequestParser rp{ctx};
388 auto supported_styleset = rp.PopRaw<u32>();
389 applet_resource->GetController<Controller_NPad>(HidController::NPad)
390 .SetSupportedStyleSet({supported_styleset});
391
471 IPC::ResponseBuilder rb{ctx, 2}; 392 IPC::ResponseBuilder rb{ctx, 2};
472 rb.Push(RESULT_SUCCESS); 393 rb.Push(RESULT_SUCCESS);
473 LOG_WARNING(Service_HID, "(STUBBED) called"); 394
395 LOG_DEBUG(Service_HID, "called");
474 } 396 }
475 397
476 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 398 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
399 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
400
477 IPC::ResponseBuilder rb{ctx, 3}; 401 IPC::ResponseBuilder rb{ctx, 3};
478 rb.Push(RESULT_SUCCESS); 402 rb.Push(RESULT_SUCCESS);
479 rb.Push<u32>(0); 403 rb.Push<u32>(controller.GetSupportedStyleSet().raw);
480 LOG_WARNING(Service_HID, "(STUBBED) called"); 404 LOG_DEBUG(Service_HID, "called");
481 } 405 }
482 406
483 void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { 407 void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
408 applet_resource->GetController<Controller_NPad>(HidController::NPad)
409 .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
484 IPC::ResponseBuilder rb{ctx, 2}; 410 IPC::ResponseBuilder rb{ctx, 2};
485 rb.Push(RESULT_SUCCESS); 411 rb.Push(RESULT_SUCCESS);
486 LOG_WARNING(Service_HID, "(STUBBED) called"); 412 LOG_DEBUG(Service_HID, "called");
487 } 413 }
488 414
489 void ActivateNpad(Kernel::HLERequestContext& ctx) { 415 void ActivateNpad(Kernel::HLERequestContext& ctx) {
490 IPC::ResponseBuilder rb{ctx, 2}; 416 IPC::ResponseBuilder rb{ctx, 2};
491 rb.Push(RESULT_SUCCESS); 417 rb.Push(RESULT_SUCCESS);
492 LOG_WARNING(Service_HID, "(STUBBED) called"); 418 applet_resource->ActivateController(HidController::NPad);
419 LOG_DEBUG(Service_HID, "called");
493 } 420 }
494 421
495 void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { 422 void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
423 IPC::RequestParser rp{ctx};
424 auto npad_id = rp.PopRaw<u32>();
496 IPC::ResponseBuilder rb{ctx, 2, 1}; 425 IPC::ResponseBuilder rb{ctx, 2, 1};
497 rb.Push(RESULT_SUCCESS); 426 rb.Push(RESULT_SUCCESS);
498 rb.PushCopyObjects(event); 427 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
499 LOG_WARNING(Service_HID, "(STUBBED) called"); 428 .GetStyleSetChangedEvent());
429 LOG_DEBUG(Service_HID, "called");
500 } 430 }
501 431
502 void DisconnectNpad(Kernel::HLERequestContext& ctx) { 432 void DisconnectNpad(Kernel::HLERequestContext& ctx) {
433 IPC::RequestParser rp{ctx};
434 auto npad_id = rp.PopRaw<u32>();
435 applet_resource->GetController<Controller_NPad>(HidController::NPad)
436 .DisconnectNPad(npad_id);
503 IPC::ResponseBuilder rb{ctx, 2}; 437 IPC::ResponseBuilder rb{ctx, 2};
504 rb.Push(RESULT_SUCCESS); 438 rb.Push(RESULT_SUCCESS);
505 LOG_WARNING(Service_HID, "(STUBBED) called"); 439 LOG_DEBUG(Service_HID, "called");
506 } 440 }
507 441
508 void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { 442 void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
509 IPC::ResponseBuilder rb{ctx, 2}; 443 IPC::RequestParser rp{ctx};
444 auto npad_id = rp.PopRaw<u32>();
445 IPC::ResponseBuilder rb{ctx, 4};
510 rb.Push(RESULT_SUCCESS); 446 rb.Push(RESULT_SUCCESS);
511 LOG_WARNING(Service_HID, "(STUBBED) called"); 447 rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
448 .GetLedPattern(npad_id)
449 .raw);
450 LOG_DEBUG(Service_HID, "called");
512 } 451 }
513 452
514 void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { 453 void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
454 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
455 IPC::RequestParser rp{ctx};
456 const auto hold_type = rp.PopRaw<u64>();
457 controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
458
515 IPC::ResponseBuilder rb{ctx, 2}; 459 IPC::ResponseBuilder rb{ctx, 2};
516 rb.Push(RESULT_SUCCESS); 460 rb.Push(RESULT_SUCCESS);
517 LOG_WARNING(Service_HID, "(STUBBED) called"); 461 LOG_DEBUG(Service_HID, "called");
518 } 462 }
519 463
520 void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { 464 void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
521 IPC::ResponseBuilder rb{ctx, 3}; 465 const auto& controller =
466 applet_resource->GetController<Controller_NPad>(HidController::NPad);
467 IPC::ResponseBuilder rb{ctx, 4};
522 rb.Push(RESULT_SUCCESS); 468 rb.Push(RESULT_SUCCESS);
523 rb.Push(joy_hold_type); 469 rb.Push<u64>(static_cast<u64>(controller.GetHoldType()));
524 LOG_WARNING(Service_HID, "(STUBBED) called"); 470 LOG_DEBUG(Service_HID, "called");
525 } 471 }
526 472
527 void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { 473 void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
474 IPC::RequestParser rp{ctx};
475 auto npad_id = rp.PopRaw<u32>();
528 IPC::ResponseBuilder rb{ctx, 2}; 476 IPC::ResponseBuilder rb{ctx, 2};
529 rb.Push(RESULT_SUCCESS); 477 rb.Push(RESULT_SUCCESS);
530 LOG_WARNING(Service_HID, "(STUBBED) called"); 478 LOG_WARNING(Service_HID, "(STUBBED) called");
531 } 479 }
532 480
481 void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
482 applet_resource->GetController<Controller_NPad>(HidController::NPad)
483 .SetVibrationEnabled(true);
484 IPC::ResponseBuilder rb{ctx, 2};
485 rb.Push(RESULT_SUCCESS);
486 LOG_DEBUG(Service_HID, "called");
487 }
488
489 void EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
490 applet_resource->GetController<Controller_NPad>(HidController::NPad)
491 .SetVibrationEnabled(false);
492 IPC::ResponseBuilder rb{ctx, 2};
493 rb.Push(RESULT_SUCCESS);
494 LOG_DEBUG(Service_HID, "called");
495 }
496
533 void SendVibrationValue(Kernel::HLERequestContext& ctx) { 497 void SendVibrationValue(Kernel::HLERequestContext& ctx) {
498 IPC::RequestParser rp{ctx};
499 const auto controller_id = rp.PopRaw<u32>();
500 const auto vibration_values = rp.PopRaw<Controller_NPad::Vibration>();
501
534 IPC::ResponseBuilder rb{ctx, 2}; 502 IPC::ResponseBuilder rb{ctx, 2};
535 rb.Push(RESULT_SUCCESS); 503 rb.Push(RESULT_SUCCESS);
536 LOG_WARNING(Service_HID, "(STUBBED) called"); 504
505 applet_resource->GetController<Controller_NPad>(HidController::NPad)
506 .VibrateController({controller_id}, {vibration_values});
507 LOG_DEBUG(Service_HID, "called");
537 } 508 }
538 509
539 void GetActualVibrationValue(Kernel::HLERequestContext& ctx) { 510 void SendVibrationValues(Kernel::HLERequestContext& ctx) {
511 const auto controllers = ctx.ReadBuffer(0);
512 const auto vibrations = ctx.ReadBuffer(1);
513
514 std::vector<u32> controller_list(controllers.size() / sizeof(u32));
515 std::vector<Controller_NPad::Vibration> vibration_list(vibrations.size() /
516 sizeof(Controller_NPad::Vibration));
517
518 std::memcpy(controller_list.data(), controllers.data(), controllers.size());
519 std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size());
520 std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(),
521 [](u32 controller_id) { return controller_id - 3; });
522
523 applet_resource->GetController<Controller_NPad>(HidController::NPad)
524 .VibrateController(controller_list, vibration_list);
525
540 IPC::ResponseBuilder rb{ctx, 2}; 526 IPC::ResponseBuilder rb{ctx, 2};
541 rb.Push(RESULT_SUCCESS); 527 rb.Push(RESULT_SUCCESS);
542 LOG_WARNING(Service_HID, "(STUBBED) called"); 528 LOG_DEBUG(Service_HID, "called");
529 }
530
531 void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
532 IPC::ResponseBuilder rb{ctx, 6};
533 rb.Push(RESULT_SUCCESS);
534 rb.PushRaw<Controller_NPad::Vibration>(
535 applet_resource->GetController<Controller_NPad>(HidController::NPad)
536 .GetLastVibration());
537 LOG_DEBUG(Service_HID, "called");
543 } 538 }
544 539
545 void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { 540 void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
541 IPC::RequestParser rp{ctx};
542 const auto npad_id = rp.PopRaw<u32>();
543 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
544 controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
545
546 IPC::ResponseBuilder rb{ctx, 2}; 546 IPC::ResponseBuilder rb{ctx, 2};
547 rb.Push(RESULT_SUCCESS); 547 rb.Push(RESULT_SUCCESS);
548 LOG_WARNING(Service_HID, "(STUBBED) called"); 548 LOG_DEBUG(Service_HID, "called");
549 } 549 }
550 550
551 void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { 551 void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
@@ -555,6 +555,8 @@ private:
555 } 555 }
556 556
557 void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { 557 void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
558 IPC::RequestParser rp{ctx};
559 auto mode = rp.PopRaw<u32>();
558 IPC::ResponseBuilder rb{ctx, 2}; 560 IPC::ResponseBuilder rb{ctx, 2};
559 rb.Push(RESULT_SUCCESS); 561 rb.Push(RESULT_SUCCESS);
560 LOG_WARNING(Service_HID, "(STUBBED) called"); 562 LOG_WARNING(Service_HID, "(STUBBED) called");
@@ -563,8 +565,9 @@ private:
563 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { 565 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
564 IPC::ResponseBuilder rb{ctx, 4}; 566 IPC::ResponseBuilder rb{ctx, 4};
565 rb.Push(RESULT_SUCCESS); 567 rb.Push(RESULT_SUCCESS);
566 rb.Push<u64>(0); 568 rb.Push<u32>(1);
567 LOG_WARNING(Service_HID, "(STUBBED) called"); 569 rb.Push<u32>(0);
570 LOG_DEBUG(Service_HID, "called");
568 } 571 }
569 572
570 void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { 573 void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
@@ -574,12 +577,6 @@ private:
574 LOG_DEBUG(Service_HID, "called"); 577 LOG_DEBUG(Service_HID, "called");
575 } 578 }
576 579
577 void SendVibrationValues(Kernel::HLERequestContext& ctx) {
578 IPC::ResponseBuilder rb{ctx, 2};
579 rb.Push(RESULT_SUCCESS);
580 LOG_WARNING(Service_HID, "(STUBBED) called");
581 }
582
583 void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 580 void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
584 IPC::ResponseBuilder rb{ctx, 2}; 581 IPC::ResponseBuilder rb{ctx, 2};
585 rb.Push(RESULT_SUCCESS); 582 rb.Push(RESULT_SUCCESS);
@@ -597,18 +594,6 @@ private:
597 rb.Push(RESULT_SUCCESS); 594 rb.Push(RESULT_SUCCESS);
598 LOG_WARNING(Service_HID, "(STUBBED) called"); 595 LOG_WARNING(Service_HID, "(STUBBED) called");
599 } 596 }
600
601 void ActivateGesture(Kernel::HLERequestContext& ctx) {
602 IPC::ResponseBuilder rb{ctx, 2};
603 rb.Push(RESULT_SUCCESS);
604 LOG_WARNING(Service_HID, "(STUBBED) called");
605 }
606
607 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
608 IPC::ResponseBuilder rb{ctx, 2};
609 rb.Push(RESULT_SUCCESS);
610 LOG_WARNING(Service_HID, "(STUBBED) called");
611 }
612}; 597};
613 598
614class HidDbg final : public ServiceFramework<HidDbg> { 599class HidDbg final : public ServiceFramework<HidDbg> {
@@ -650,6 +635,7 @@ public:
650 {140, nullptr, "DeactivateConsoleSixAxisSensor"}, 635 {140, nullptr, "DeactivateConsoleSixAxisSensor"},
651 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, 636 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
652 {142, nullptr, "DeactivateSevenSixAxisSensor"}, 637 {142, nullptr, "DeactivateSevenSixAxisSensor"},
638 {143, nullptr, "GetConsoleSixAxisSensorCountStates"},
653 {201, nullptr, "ActivateFirmwareUpdate"}, 639 {201, nullptr, "ActivateFirmwareUpdate"},
654 {202, nullptr, "DeactivateFirmwareUpdate"}, 640 {202, nullptr, "DeactivateFirmwareUpdate"},
655 {203, nullptr, "StartFirmwareUpdate"}, 641 {203, nullptr, "StartFirmwareUpdate"},
@@ -660,12 +646,23 @@ public:
660 {208, nullptr, "StartFirmwareUpdateForRevert"}, 646 {208, nullptr, "StartFirmwareUpdateForRevert"},
661 {209, nullptr, "GetAvailableFirmwareVersionForRevert"}, 647 {209, nullptr, "GetAvailableFirmwareVersionForRevert"},
662 {210, nullptr, "IsFirmwareUpdatingDevice"}, 648 {210, nullptr, "IsFirmwareUpdatingDevice"},
649 {211, nullptr, "StartFirmwareUpdateIndividual"},
650 {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
651 {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
663 {221, nullptr, "UpdateControllerColor"}, 652 {221, nullptr, "UpdateControllerColor"},
664 {222, nullptr, "ConnectUsbPadsAsync"}, 653 {222, nullptr, "ConnectUsbPadsAsync"},
665 {223, nullptr, "DisconnectUsbPadsAsync"}, 654 {223, nullptr, "DisconnectUsbPadsAsync"},
666 {224, nullptr, "UpdateDesignInfo"}, 655 {224, nullptr, "UpdateDesignInfo"},
667 {225, nullptr, "GetUniquePadDriverState"}, 656 {225, nullptr, "GetUniquePadDriverState"},
668 {226, nullptr, "GetSixAxisSensorDriverStates"}, 657 {226, nullptr, "GetSixAxisSensorDriverStates"},
658 {227, nullptr, "GetRxPacketHistory"},
659 {228, nullptr, "AcquireOperationEventHandle"},
660 {229, nullptr, "ReadSerialFlash"},
661 {230, nullptr, "WriteSerialFlash"},
662 {231, nullptr, "GetOperationResult"},
663 {232, nullptr, "EnableShipmentMode"},
664 {233, nullptr, "ClearPairingInfo"},
665 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
669 {301, nullptr, "GetAbstractedPadHandles"}, 666 {301, nullptr, "GetAbstractedPadHandles"},
670 {302, nullptr, "GetAbstractedPadState"}, 667 {302, nullptr, "GetAbstractedPadState"},
671 {303, nullptr, "GetAbstractedPadsState"}, 668 {303, nullptr, "GetAbstractedPadsState"},
@@ -673,6 +670,8 @@ public:
673 {322, nullptr, "UnsetAutoPilotVirtualPadState"}, 670 {322, nullptr, "UnsetAutoPilotVirtualPadState"},
674 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, 671 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
675 {350, nullptr, "AddRegisteredDevice"}, 672 {350, nullptr, "AddRegisteredDevice"},
673 {400, nullptr, "DisableExternalMcuOnNxDevice"},
674 {401, nullptr, "DisableRailDeviceFiltering"},
676 }; 675 };
677 // clang-format on 676 // clang-format on
678 677
@@ -708,7 +707,9 @@ public:
708 {307, nullptr, "GetNpadSystemExtStyle"}, 707 {307, nullptr, "GetNpadSystemExtStyle"},
709 {308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, 708 {308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
710 {309, nullptr, "GetNpadFullKeyGripColor"}, 709 {309, nullptr, "GetNpadFullKeyGripColor"},
710 {310, nullptr, "GetMaskedSupportedNpadStyleSet"},
711 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, 711 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
712 {312, nullptr, "SetSupportedNpadStyleSetAll"},
712 {321, nullptr, "GetUniquePadsFromNpad"}, 713 {321, nullptr, "GetUniquePadsFromNpad"},
713 {322, nullptr, "GetIrSensorState"}, 714 {322, nullptr, "GetIrSensorState"},
714 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, 715 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
@@ -733,6 +734,7 @@ public:
733 {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"}, 734 {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"},
734 {547, nullptr, "GetAllowedBluetoothLinksCount"}, 735 {547, nullptr, "GetAllowedBluetoothLinksCount"},
735 {548, nullptr, "GetRegisteredDevices"}, 736 {548, nullptr, "GetRegisteredDevices"},
737 {549, nullptr, "GetConnectableRegisteredDevices"},
736 {700, nullptr, "ActivateUniquePad"}, 738 {700, nullptr, "ActivateUniquePad"},
737 {702, nullptr, "AcquireUniquePadConnectionEventHandle"}, 739 {702, nullptr, "AcquireUniquePadConnectionEventHandle"},
738 {703, nullptr, "GetUniquePadIds"}, 740 {703, nullptr, "GetUniquePadIds"},
@@ -761,6 +763,7 @@ public:
761 {850, nullptr, "IsUsbFullKeyControllerEnabled"}, 763 {850, nullptr, "IsUsbFullKeyControllerEnabled"},
762 {851, nullptr, "EnableUsbFullKeyController"}, 764 {851, nullptr, "EnableUsbFullKeyController"},
763 {852, nullptr, "IsUsbConnected"}, 765 {852, nullptr, "IsUsbConnected"},
766 {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
764 {900, nullptr, "ActivateInputDetector"}, 767 {900, nullptr, "ActivateInputDetector"},
765 {901, nullptr, "NotifyInputDetector"}, 768 {901, nullptr, "NotifyInputDetector"},
766 {1000, nullptr, "InitializeFirmwareUpdate"}, 769 {1000, nullptr, "InitializeFirmwareUpdate"},
@@ -780,6 +783,12 @@ public:
780 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, 783 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
781 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, 784 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
782 {1100, nullptr, "GetHidbusSystemServiceObject"}, 785 {1100, nullptr, "GetHidbusSystemServiceObject"},
786 {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
787 {1130, nullptr, "InitializeUsbFirmwareUpdate"},
788 {1131, nullptr, "FinalizeUsbFirmwareUpdate"},
789 {1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
790 {1133, nullptr, "StartUsbFirmwareUpdate"},
791 {1134, nullptr, "GetUsbFirmwareUpdateState"},
783 }; 792 };
784 // clang-format on 793 // clang-format on
785 794
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 88d926808..773035460 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -4,408 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7namespace SM {
8#include "common/bit_field.h" 8class ServiceManager;
9#include "common/common_types.h" 9}
10#include "core/hle/service/service.h"
11 10
12namespace Service::HID { 11namespace Service::HID {
13 12
14// Begin enums and output structs
15
16constexpr u32 HID_NUM_ENTRIES = 17;
17constexpr u32 HID_NUM_LAYOUTS = 7;
18constexpr s32 HID_JOYSTICK_MAX = 0x8000;
19constexpr s32 HID_JOYSTICK_MIN = -0x8000;
20
21constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
22constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
23constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
24constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
25
26enum ControllerType : u32 {
27 ControllerType_ProController = 1 << 0,
28 ControllerType_Handheld = 1 << 1,
29 ControllerType_JoyconPair = 1 << 2,
30 ControllerType_JoyconLeft = 1 << 3,
31 ControllerType_JoyconRight = 1 << 4,
32};
33
34enum ControllerLayoutType : u32 {
35 Layout_ProController = 0, // Pro Controller or HID gamepad
36 Layout_Handheld = 1, // Two Joy-Con docked to rails
37 Layout_Single = 2, // Horizontal single Joy-Con or pair of Joy-Con, adjusted for orientation
38 Layout_Left = 3, // Only raw left Joy-Con state, no orientation adjustment
39 Layout_Right = 4, // Only raw right Joy-Con state, no orientation adjustment
40 Layout_DefaultDigital = 5, // Same as next, but sticks have 8-direction values only
41 Layout_Default = 6, // Safe default, single Joy-Con have buttons/sticks rotated for orientation
42};
43
44enum ControllerColorDescription {
45 ColorDesc_ColorsNonexistent = 1 << 1,
46};
47
48enum ControllerConnectionState {
49 ConnectionState_Connected = 1 << 0,
50 ConnectionState_Wired = 1 << 1,
51};
52
53enum ControllerJoystick {
54 Joystick_Left = 0,
55 Joystick_Right = 1,
56};
57
58enum ControllerID {
59 Controller_Player1 = 0,
60 Controller_Player2 = 1,
61 Controller_Player3 = 2,
62 Controller_Player4 = 3,
63 Controller_Player5 = 4,
64 Controller_Player6 = 5,
65 Controller_Player7 = 6,
66 Controller_Player8 = 7,
67 Controller_Handheld = 8,
68 Controller_Unknown = 9,
69};
70
71// End enums and output structs
72
73// Begin UnkInput3
74
75struct UnkInput3Header {
76 u64 timestamp_ticks;
77 u64 num_entries;
78 u64 latest_entry;
79 u64 max_entry_index;
80};
81static_assert(sizeof(UnkInput3Header) == 0x20, "HID UnkInput3 header structure has incorrect size");
82
83struct UnkInput3Entry {
84 u64 timestamp;
85 u64 timestamp_2;
86 u64 unk_8;
87 u64 unk_10;
88 u64 unk_18;
89};
90static_assert(sizeof(UnkInput3Entry) == 0x28, "HID UnkInput3 entry structure has incorrect size");
91
92struct UnkInput3 {
93 UnkInput3Header header;
94 std::array<UnkInput3Entry, 17> entries;
95 std::array<u8, 0x138> padding;
96};
97static_assert(sizeof(UnkInput3) == 0x400, "HID UnkInput3 structure has incorrect size");
98
99// End UnkInput3
100
101// Begin TouchScreen
102
103struct TouchScreenHeader {
104 u64 timestamp_ticks;
105 u64 num_entries;
106 u64 latest_entry;
107 u64 max_entry_index;
108 u64 timestamp;
109};
110static_assert(sizeof(TouchScreenHeader) == 0x28,
111 "HID touch screen header structure has incorrect size");
112
113struct TouchScreenEntryHeader {
114 u64 timestamp;
115 u64 num_touches;
116};
117static_assert(sizeof(TouchScreenEntryHeader) == 0x10,
118 "HID touch screen entry header structure has incorrect size");
119
120struct TouchScreenEntryTouch {
121 u64 timestamp;
122 u32 padding;
123 u32 touch_index;
124 u32 x;
125 u32 y;
126 u32 diameter_x;
127 u32 diameter_y;
128 u32 angle;
129 u32 padding_2;
130};
131static_assert(sizeof(TouchScreenEntryTouch) == 0x28,
132 "HID touch screen touch structure has incorrect size");
133
134struct TouchScreenEntry {
135 TouchScreenEntryHeader header;
136 std::array<TouchScreenEntryTouch, 16> touches;
137 u64 unk;
138};
139static_assert(sizeof(TouchScreenEntry) == 0x298,
140 "HID touch screen entry structure has incorrect size");
141
142struct TouchScreen {
143 TouchScreenHeader header;
144 std::array<TouchScreenEntry, 17> entries;
145 std::array<u8, 0x3c0> padding;
146};
147static_assert(sizeof(TouchScreen) == 0x3000, "HID touch screen structure has incorrect size");
148
149// End TouchScreen
150
151// Begin Mouse
152
153struct MouseHeader {
154 u64 timestamp_ticks;
155 u64 num_entries;
156 u64 latest_entry;
157 u64 max_entry_index;
158};
159static_assert(sizeof(MouseHeader) == 0x20, "HID mouse header structure has incorrect size");
160
161struct MouseButtonState {
162 union {
163 u64 hex{};
164
165 // Buttons
166 BitField<0, 1, u64> left;
167 BitField<1, 1, u64> right;
168 BitField<2, 1, u64> middle;
169 BitField<3, 1, u64> forward;
170 BitField<4, 1, u64> back;
171 };
172};
173
174struct MouseEntry {
175 u64 timestamp;
176 u64 timestamp_2;
177 u32 x;
178 u32 y;
179 u32 velocity_x;
180 u32 velocity_y;
181 u32 scroll_velocity_x;
182 u32 scroll_velocity_y;
183 MouseButtonState buttons;
184};
185static_assert(sizeof(MouseEntry) == 0x30, "HID mouse entry structure has incorrect size");
186
187struct Mouse {
188 MouseHeader header;
189 std::array<MouseEntry, 17> entries;
190 std::array<u8, 0xB0> padding;
191};
192static_assert(sizeof(Mouse) == 0x400, "HID mouse structure has incorrect size");
193
194// End Mouse
195
196// Begin Keyboard
197
198struct KeyboardHeader {
199 u64 timestamp_ticks;
200 u64 num_entries;
201 u64 latest_entry;
202 u64 max_entry_index;
203};
204static_assert(sizeof(KeyboardHeader) == 0x20, "HID keyboard header structure has incorrect size");
205
206struct KeyboardModifierKeyState {
207 union {
208 u64 hex{};
209
210 // Buttons
211 BitField<0, 1, u64> lctrl;
212 BitField<1, 1, u64> lshift;
213 BitField<2, 1, u64> lalt;
214 BitField<3, 1, u64> lmeta;
215 BitField<4, 1, u64> rctrl;
216 BitField<5, 1, u64> rshift;
217 BitField<6, 1, u64> ralt;
218 BitField<7, 1, u64> rmeta;
219 BitField<8, 1, u64> capslock;
220 BitField<9, 1, u64> scrolllock;
221 BitField<10, 1, u64> numlock;
222 };
223};
224
225struct KeyboardEntry {
226 u64 timestamp;
227 u64 timestamp_2;
228 KeyboardModifierKeyState modifier;
229 u32 keys[8];
230};
231static_assert(sizeof(KeyboardEntry) == 0x38, "HID keyboard entry structure has incorrect size");
232
233struct Keyboard {
234 KeyboardHeader header;
235 std::array<KeyboardEntry, 17> entries;
236 std::array<u8, 0x28> padding;
237};
238static_assert(sizeof(Keyboard) == 0x400, "HID keyboard structure has incorrect size");
239
240// End Keyboard
241
242// Begin UnkInput1
243
244struct UnkInput1Header {
245 u64 timestamp_ticks;
246 u64 num_entries;
247 u64 latest_entry;
248 u64 max_entry_index;
249};
250static_assert(sizeof(UnkInput1Header) == 0x20, "HID UnkInput1 header structure has incorrect size");
251
252struct UnkInput1Entry {
253 u64 timestamp;
254 u64 timestamp_2;
255 u64 unk_8;
256 u64 unk_10;
257 u64 unk_18;
258};
259static_assert(sizeof(UnkInput1Entry) == 0x28, "HID UnkInput1 entry structure has incorrect size");
260
261struct UnkInput1 {
262 UnkInput1Header header;
263 std::array<UnkInput1Entry, 17> entries;
264 std::array<u8, 0x138> padding;
265};
266static_assert(sizeof(UnkInput1) == 0x400, "HID UnkInput1 structure has incorrect size");
267
268// End UnkInput1
269
270// Begin UnkInput2
271
272struct UnkInput2Header {
273 u64 timestamp_ticks;
274 u64 num_entries;
275 u64 latest_entry;
276 u64 max_entry_index;
277};
278static_assert(sizeof(UnkInput2Header) == 0x20, "HID UnkInput2 header structure has incorrect size");
279
280struct UnkInput2 {
281 UnkInput2Header header;
282 std::array<u8, 0x1E0> padding;
283};
284static_assert(sizeof(UnkInput2) == 0x200, "HID UnkInput2 structure has incorrect size");
285
286// End UnkInput2
287
288// Begin Controller
289
290struct ControllerMAC {
291 u64 timestamp;
292 std::array<u8, 0x8> mac;
293 u64 unk;
294 u64 timestamp_2;
295};
296static_assert(sizeof(ControllerMAC) == 0x20, "HID controller MAC structure has incorrect size");
297
298struct ControllerHeader {
299 u32 type;
300 u32 is_half;
301 u32 single_colors_descriptor;
302 u32 single_color_body;
303 u32 single_color_buttons;
304 u32 split_colors_descriptor;
305 u32 left_color_body;
306 u32 left_color_buttons;
307 u32 right_color_body;
308 u32 right_color_buttons;
309};
310static_assert(sizeof(ControllerHeader) == 0x28,
311 "HID controller header structure has incorrect size");
312
313struct ControllerLayoutHeader {
314 u64 timestamp_ticks;
315 u64 num_entries;
316 u64 latest_entry;
317 u64 max_entry_index;
318};
319static_assert(sizeof(ControllerLayoutHeader) == 0x20,
320 "HID controller layout header structure has incorrect size");
321
322struct ControllerPadState {
323 union {
324 u64 hex{};
325
326 // Buttons
327 BitField<0, 1, u64> a;
328 BitField<1, 1, u64> b;
329 BitField<2, 1, u64> x;
330 BitField<3, 1, u64> y;
331 BitField<4, 1, u64> lstick;
332 BitField<5, 1, u64> rstick;
333 BitField<6, 1, u64> l;
334 BitField<7, 1, u64> r;
335 BitField<8, 1, u64> zl;
336 BitField<9, 1, u64> zr;
337 BitField<10, 1, u64> plus;
338 BitField<11, 1, u64> minus;
339
340 // D-pad buttons
341 BitField<12, 1, u64> dleft;
342 BitField<13, 1, u64> dup;
343 BitField<14, 1, u64> dright;
344 BitField<15, 1, u64> ddown;
345
346 // Left stick directions
347 BitField<16, 1, u64> lstick_left;
348 BitField<17, 1, u64> lstick_up;
349 BitField<18, 1, u64> lstick_right;
350 BitField<19, 1, u64> lstick_down;
351
352 // Right stick directions
353 BitField<20, 1, u64> rstick_left;
354 BitField<21, 1, u64> rstick_up;
355 BitField<22, 1, u64> rstick_right;
356 BitField<23, 1, u64> rstick_down;
357
358 BitField<24, 1, u64> sl;
359 BitField<25, 1, u64> sr;
360 };
361};
362
363struct ControllerInputEntry {
364 u64 timestamp;
365 u64 timestamp_2;
366 ControllerPadState buttons;
367 s32 joystick_left_x;
368 s32 joystick_left_y;
369 s32 joystick_right_x;
370 s32 joystick_right_y;
371 u64 connection_state;
372};
373static_assert(sizeof(ControllerInputEntry) == 0x30,
374 "HID controller input entry structure has incorrect size");
375
376struct ControllerLayout {
377 ControllerLayoutHeader header;
378 std::array<ControllerInputEntry, 17> entries;
379};
380static_assert(sizeof(ControllerLayout) == 0x350,
381 "HID controller layout structure has incorrect size");
382
383struct Controller {
384 ControllerHeader header;
385 std::array<ControllerLayout, HID_NUM_LAYOUTS> layouts;
386 std::array<u8, 0x2a70> unk_1;
387 ControllerMAC mac_left;
388 ControllerMAC mac_right;
389 std::array<u8, 0xdf8> unk_2;
390};
391static_assert(sizeof(Controller) == 0x5000, "HID controller structure has incorrect size");
392
393// End Controller
394
395struct SharedMemory {
396 UnkInput3 unk_input_3;
397 TouchScreen touchscreen;
398 Mouse mouse;
399 Keyboard keyboard;
400 std::array<UnkInput1, 4> unk_input_1;
401 std::array<UnkInput2, 3> unk_input_2;
402 std::array<u8, 0x800> unk_section_8;
403 std::array<u8, 0x4000> controller_serials;
404 std::array<Controller, 10> controllers;
405 std::array<u8, 0x4600> unk_section_9;
406};
407static_assert(sizeof(SharedMemory) == 0x40000, "HID Shared Memory structure has incorrect size");
408
409/// Reload input devices. Used when input configuration changed 13/// Reload input devices. Used when input configuration changed
410void ReloadInputDevices(); 14void ReloadInputDevices();
411 15
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp
index 7b91bb258..e1f17a926 100644
--- a/src/core/hle/service/mm/mm_u.cpp
+++ b/src/core/hle/service/mm/mm_u.cpp
@@ -14,14 +14,14 @@ public:
14 explicit MM_U() : ServiceFramework{"mm:u"} { 14 explicit MM_U() : ServiceFramework{"mm:u"} {
15 // clang-format off 15 // clang-format off
16 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
17 {0, &MM_U::Initialize, "InitializeOld"}, 17 {0, &MM_U::Initialize, "Initialize"},
18 {1, &MM_U::Finalize, "FinalizeOld"}, 18 {1, &MM_U::Finalize, "Finalize"},
19 {2, &MM_U::SetAndWait, "SetAndWaitOld"}, 19 {2, &MM_U::SetAndWait, "SetAndWait"},
20 {3, &MM_U::Get, "GetOld"}, 20 {3, &MM_U::Get, "Get"},
21 {4, &MM_U::Initialize, "Initialize"}, 21 {4, &MM_U::InitializeWithId, "InitializeWithId"},
22 {5, &MM_U::Finalize, "Finalize"}, 22 {5, &MM_U::FinalizeWithId, "FinalizeWithId"},
23 {6, &MM_U::SetAndWait, "SetAndWait"}, 23 {6, &MM_U::SetAndWaitWithId, "SetAndWaitWithId"},
24 {7, &MM_U::Get, "Get"}, 24 {7, &MM_U::GetWithId, "GetWithId"},
25 }; 25 };
26 // clang-format on 26 // clang-format on
27 27
@@ -59,9 +59,43 @@ private:
59 rb.Push(current); 59 rb.Push(current);
60 } 60 }
61 61
62 void InitializeWithId(Kernel::HLERequestContext& ctx) {
63 LOG_WARNING(Service_MM, "(STUBBED) called");
64 IPC::ResponseBuilder rb{ctx, 3};
65 rb.Push(RESULT_SUCCESS);
66 rb.Push<u32>(id); // Any non zero value
67 }
68
69 void FinalizeWithId(Kernel::HLERequestContext& ctx) {
70 LOG_WARNING(Service_MM, "(STUBBED) called");
71 IPC::ResponseBuilder rb{ctx, 2};
72 rb.Push(RESULT_SUCCESS);
73 }
74
75 void SetAndWaitWithId(Kernel::HLERequestContext& ctx) {
76 IPC::RequestParser rp{ctx};
77 u32 input_id = rp.Pop<u32>();
78 min = rp.Pop<u32>();
79 max = rp.Pop<u32>();
80 current = min;
81
82 LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}",
83 input_id, min, max);
84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(RESULT_SUCCESS);
86 }
87
88 void GetWithId(Kernel::HLERequestContext& ctx) {
89 LOG_WARNING(Service_MM, "(STUBBED) called");
90 IPC::ResponseBuilder rb{ctx, 3};
91 rb.Push(RESULT_SUCCESS);
92 rb.Push(current);
93 }
94
62 u32 min{0}; 95 u32 min{0};
63 u32 max{0}; 96 u32 max{0};
64 u32 current{0}; 97 u32 current{0};
98 u32 id{1};
65}; 99};
66 100
67void InstallInterfaces(SM::ServiceManager& service_manager) { 101void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 8c07a05c2..39c0c1e63 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -144,7 +144,7 @@ private:
144 } 144 }
145 145
146 const u64 device_handle{0xDEAD}; 146 const u64 device_handle{0xDEAD};
147 const HID::ControllerID npad_id{HID::Controller_Player1}; 147 const u32 npad_id{0}; // This is the first player controller id
148 State state{State::NonInitialized}; 148 State state{State::NonInitialized};
149 DeviceState device_state{DeviceState::Initialized}; 149 DeviceState device_state{DeviceState::Initialized};
150 Kernel::SharedPtr<Kernel::Event> activate_event; 150 Kernel::SharedPtr<Kernel::Event> activate_event;
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 10611ed6a..75dcd94a3 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -219,6 +219,7 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
219 {35, nullptr, "GetScanData"}, 219 {35, nullptr, "GetScanData"},
220 {36, nullptr, "GetCurrentAccessPoint"}, 220 {36, nullptr, "GetCurrentAccessPoint"},
221 {37, nullptr, "Shutdown"}, 221 {37, nullptr, "Shutdown"},
222 {38, nullptr, "GetAllowedChannels"},
222 }; 223 };
223 RegisterHandlers(functions); 224 RegisterHandlers(functions);
224} 225}
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index 261ad539c..18091c9bb 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -71,6 +71,22 @@ public:
71 } 71 }
72}; 72};
73 73
74class NIM_ECA final : public ServiceFramework<NIM_ECA> {
75public:
76 explicit NIM_ECA() : ServiceFramework{"nim:eca"} {
77 // clang-format off
78 static const FunctionInfo functions[] = {
79 {0, nullptr, "CreateServerInterface"},
80 {1, nullptr, "RefreshDebugAvailability"},
81 {2, nullptr, "ClearDebugResponse"},
82 {3, nullptr, "RegisterDebugResponse"},
83 };
84 // clang-format on
85
86 RegisterHandlers(functions);
87 }
88};
89
74class NIM_SHP final : public ServiceFramework<NIM_SHP> { 90class NIM_SHP final : public ServiceFramework<NIM_SHP> {
75public: 91public:
76 explicit NIM_SHP() : ServiceFramework{"nim:shp"} { 92 explicit NIM_SHP() : ServiceFramework{"nim:shp"} {
@@ -214,6 +230,7 @@ private:
214 230
215void InstallInterfaces(SM::ServiceManager& sm) { 231void InstallInterfaces(SM::ServiceManager& sm) {
216 std::make_shared<NIM>()->InstallAsService(sm); 232 std::make_shared<NIM>()->InstallAsService(sm);
233 std::make_shared<NIM_ECA>()->InstallAsService(sm);
217 std::make_shared<NIM_SHP>()->InstallAsService(sm); 234 std::make_shared<NIM_SHP>()->InstallAsService(sm);
218 std::make_shared<NTC>()->InstallAsService(sm); 235 std::make_shared<NTC>()->InstallAsService(sm);
219} 236}
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 98017267c..07c1381fe 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -93,13 +93,23 @@ public:
93 {86, nullptr, "EnableApplicationCrashReport"}, 93 {86, nullptr, "EnableApplicationCrashReport"},
94 {87, nullptr, "IsApplicationCrashReportEnabled"}, 94 {87, nullptr, "IsApplicationCrashReportEnabled"},
95 {90, nullptr, "BoostSystemMemoryResourceLimit"}, 95 {90, nullptr, "BoostSystemMemoryResourceLimit"},
96 {91, nullptr, "Unknown1"},
97 {92, nullptr, "Unknown2"},
98 {93, nullptr, "GetMainApplicationProgramIndex"},
99 {94, nullptr, "LaunchApplication2"},
100 {95, nullptr, "GetApplicationLaunchInfo"},
101 {96, nullptr, "AcquireApplicationLaunchInfo"},
102 {97, nullptr, "GetMainApplicationProgramIndex2"},
103 {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
96 {100, nullptr, "ResetToFactorySettings"}, 104 {100, nullptr, "ResetToFactorySettings"},
97 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, 105 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
98 {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, 106 {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
99 {200, nullptr, "CalculateUserSaveDataStatistics"}, 107 {200, nullptr, "CalculateUserSaveDataStatistics"},
100 {201, nullptr, "DeleteUserSaveDataAll"}, 108 {201, nullptr, "DeleteUserSaveDataAll"},
101 {210, nullptr, "DeleteUserSystemSaveData"}, 109 {210, nullptr, "DeleteUserSystemSaveData"},
110 {211, nullptr, "DeleteSaveData"},
102 {220, nullptr, "UnregisterNetworkServiceAccount"}, 111 {220, nullptr, "UnregisterNetworkServiceAccount"},
112 {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
103 {300, nullptr, "GetApplicationShellEvent"}, 113 {300, nullptr, "GetApplicationShellEvent"},
104 {301, nullptr, "PopApplicationShellEventInfo"}, 114 {301, nullptr, "PopApplicationShellEventInfo"},
105 {302, nullptr, "LaunchLibraryApplet"}, 115 {302, nullptr, "LaunchLibraryApplet"},
@@ -114,6 +124,7 @@ public:
114 {403, nullptr, "GetMaxApplicationControlCacheCount"}, 124 {403, nullptr, "GetMaxApplicationControlCacheCount"},
115 {404, nullptr, "InvalidateApplicationControlCache"}, 125 {404, nullptr, "InvalidateApplicationControlCache"},
116 {405, nullptr, "ListApplicationControlCacheEntryInfo"}, 126 {405, nullptr, "ListApplicationControlCacheEntryInfo"},
127 {406, nullptr, "GetApplicationControlProperty"},
117 {502, nullptr, "RequestCheckGameCardRegistration"}, 128 {502, nullptr, "RequestCheckGameCardRegistration"},
118 {503, nullptr, "RequestGameCardRegistrationGoldPoint"}, 129 {503, nullptr, "RequestGameCardRegistrationGoldPoint"},
119 {504, nullptr, "RequestRegisterGameCard"}, 130 {504, nullptr, "RequestRegisterGameCard"},
@@ -129,6 +140,7 @@ public:
129 {604, nullptr, "RegisterContentsExternalKey"}, 140 {604, nullptr, "RegisterContentsExternalKey"},
130 {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, 141 {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
131 {606, nullptr, "GetContentMetaStorage"}, 142 {606, nullptr, "GetContentMetaStorage"},
143 {607, nullptr, "ListAvailableAddOnContent"},
132 {700, nullptr, "PushDownloadTaskList"}, 144 {700, nullptr, "PushDownloadTaskList"},
133 {701, nullptr, "ClearTaskStatusList"}, 145 {701, nullptr, "ClearTaskStatusList"},
134 {702, nullptr, "RequestDownloadTaskList"}, 146 {702, nullptr, "RequestDownloadTaskList"},
@@ -148,6 +160,9 @@ public:
148 {907, nullptr, "WithdrawApplicationUpdateRequest"}, 160 {907, nullptr, "WithdrawApplicationUpdateRequest"},
149 {908, nullptr, "ListApplicationRecordInstalledContentMeta"}, 161 {908, nullptr, "ListApplicationRecordInstalledContentMeta"},
150 {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"}, 162 {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
163 {910, nullptr, "Unknown3"},
164 {911, nullptr, "SetPreInstalledApplication"},
165 {912, nullptr, "ClearPreInstalledApplicationFlag"},
151 {1000, nullptr, "RequestVerifyApplicationDeprecated"}, 166 {1000, nullptr, "RequestVerifyApplicationDeprecated"},
152 {1001, nullptr, "CorruptApplicationForDebug"}, 167 {1001, nullptr, "CorruptApplicationForDebug"},
153 {1002, nullptr, "RequestVerifyAddOnContentsRights"}, 168 {1002, nullptr, "RequestVerifyAddOnContentsRights"},
@@ -162,6 +177,8 @@ public:
162 {1305, nullptr, "TryDeleteRunningApplicationEntity"}, 177 {1305, nullptr, "TryDeleteRunningApplicationEntity"},
163 {1306, nullptr, "TryDeleteRunningApplicationCompletely"}, 178 {1306, nullptr, "TryDeleteRunningApplicationCompletely"},
164 {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, 179 {1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
180 {1308, nullptr, "DeleteApplicationCompletelyForDebug"},
181 {1309, nullptr, "CleanupUnavailableAddOnContents"},
165 {1400, nullptr, "PrepareShutdown"}, 182 {1400, nullptr, "PrepareShutdown"},
166 {1500, nullptr, "FormatSdCard"}, 183 {1500, nullptr, "FormatSdCard"},
167 {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, 184 {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
@@ -199,6 +216,28 @@ public:
199 {2015, nullptr, "CompareSystemDeliveryInfo"}, 216 {2015, nullptr, "CompareSystemDeliveryInfo"},
200 {2016, nullptr, "ListNotCommittedContentMeta"}, 217 {2016, nullptr, "ListNotCommittedContentMeta"},
201 {2017, nullptr, "CreateDownloadTask"}, 218 {2017, nullptr, "CreateDownloadTask"},
219 {2018, nullptr, "Unknown4"},
220 {2050, nullptr, "Unknown5"},
221 {2100, nullptr, "Unknown6"},
222 {2101, nullptr, "Unknown7"},
223 {2150, nullptr, "CreateRightsEnvironment"},
224 {2151, nullptr, "DestroyRightsEnvironment"},
225 {2152, nullptr, "ActivateRightsEnvironment"},
226 {2153, nullptr, "DeactivateRightsEnvironment"},
227 {2154, nullptr, "ForceActivateRightsContextForExit"},
228 {2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
229 {2161, nullptr, "SetUsersToRightsEnvironment"},
230 {2170, nullptr, "GetRightsEnvironmentStatus"},
231 {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
232 {2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
233 {2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
234 {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
235 {2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
236 {2199, nullptr, "GetRightsEnvironmentCountForDebug"},
237 {2200, nullptr, "Unknown8"},
238 {2201, nullptr, "Unknown9"},
239 {2250, nullptr, "Unknown10"},
240 {2300, nullptr, "Unknown11"},
202 }; 241 };
203 // clang-format on 242 // clang-format on
204 243
@@ -348,12 +387,15 @@ public:
348 {0, nullptr, "LaunchProgram"}, 387 {0, nullptr, "LaunchProgram"},
349 {1, nullptr, "TerminateProcess"}, 388 {1, nullptr, "TerminateProcess"},
350 {2, nullptr, "TerminateProgram"}, 389 {2, nullptr, "TerminateProgram"},
351 {3, nullptr, "GetShellEventHandle"}, 390 {4, nullptr, "GetShellEventHandle"},
352 {4, nullptr, "GetShellEventInfo"}, 391 {5, nullptr, "GetShellEventInfo"},
353 {5, nullptr, "TerminateApplication"}, 392 {6, nullptr, "TerminateApplication"},
354 {6, nullptr, "PrepareLaunchProgramFromHost"}, 393 {7, nullptr, "PrepareLaunchProgramFromHost"},
355 {7, nullptr, "LaunchApplication"}, 394 {8, nullptr, "LaunchApplication"},
356 {8, nullptr, "LaunchApplicationWithStorageId"}, 395 {9, nullptr, "LaunchApplicationWithStorageId"},
396 {10, nullptr, "TerminateApplication2"},
397 {11, nullptr, "GetRunningApplicationProcessId"},
398 {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"},
357 }; 399 };
358 // clang-format on 400 // clang-format on
359 401
@@ -388,6 +430,7 @@ public:
388 {19, nullptr, "GetReceivedEulaDataSize"}, 430 {19, nullptr, "GetReceivedEulaDataSize"},
389 {20, nullptr, "GetReceivedEulaData"}, 431 {20, nullptr, "GetReceivedEulaData"},
390 {21, nullptr, "SetupToReceiveSystemUpdate"}, 432 {21, nullptr, "SetupToReceiveSystemUpdate"},
433 {22, nullptr, "RequestCheckLatestUpdateIncludesRebootlessUpdate"},
391 }; 434 };
392 // clang-format on 435 // clang-format on
393 436
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 4b2f758a8..44accecb7 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -161,7 +161,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
161 }; 161 };
162 RegisterHandlers(functions); 162 RegisterHandlers(functions);
163 // Attempt to load shared font data from disk 163 // Attempt to load shared font data from disk
164 const auto nand = FileSystem::GetSystemNANDContents(); 164 const auto* nand = FileSystem::GetSystemNANDContents();
165 std::size_t offset = 0; 165 std::size_t offset = 0;
166 // Rebuild shared fonts from data ncas 166 // Rebuild shared fonts from data ncas
167 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), 167 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 62f049660..a225cb4cb 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -197,7 +197,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
197// Module interface 197// Module interface
198 198
199/// Initialize ServiceManager 199/// Initialize ServiceManager
200void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) { 200void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) {
201 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 201 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
202 // here and pass it into the respective InstallInterfaces functions. 202 // here and pass it into the respective InstallInterfaces functions.
203 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); 203 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>();
@@ -220,7 +220,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesys
220 EUPLD::InstallInterfaces(*sm); 220 EUPLD::InstallInterfaces(*sm);
221 Fatal::InstallInterfaces(*sm); 221 Fatal::InstallInterfaces(*sm);
222 FGM::InstallInterfaces(*sm); 222 FGM::InstallInterfaces(*sm);
223 FileSystem::InstallInterfaces(*sm, rfs); 223 FileSystem::InstallInterfaces(*sm, vfs);
224 Friend::InstallInterfaces(*sm); 224 Friend::InstallInterfaces(*sm);
225 GRC::InstallInterfaces(*sm); 225 GRC::InstallInterfaces(*sm);
226 HID::InstallInterfaces(*sm); 226 HID::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 2fc57a82e..98483ecf1 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -180,8 +180,7 @@ private:
180}; 180};
181 181
182/// Initialize ServiceManager 182/// Initialize ServiceManager
183void Init(std::shared_ptr<SM::ServiceManager>& sm, 183void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs);
184 const std::shared_ptr<FileSys::VfsFilesystem>& vfs);
185 184
186/// Shutdown ServiceManager 185/// Shutdown ServiceManager
187void Shutdown(); 186void Shutdown();
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp
index 5af356d10..34654bb07 100644
--- a/src/core/hle/service/set/set_cal.cpp
+++ b/src/core/hle/service/set/set_cal.cpp
@@ -39,7 +39,8 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") {
39 {29, nullptr, "GetAmiiboEcqvBlsKey"}, 39 {29, nullptr, "GetAmiiboEcqvBlsKey"},
40 {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, 40 {30, nullptr, "GetAmiiboEcqvBlsCertificate"},
41 {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, 41 {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"},
42 {32, nullptr, "GetUnknownId"}, 42 {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"},
43 {33, nullptr, "GetBatteryVersion"},
43 }; 44 };
44 RegisterHandlers(functions); 45 RegisterHandlers(functions);
45} 46}
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index bbc02abcc..184537daa 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -968,6 +968,54 @@ private:
968 rb.PushCopyObjects(vsync_event); 968 rb.PushCopyObjects(vsync_event);
969 } 969 }
970 970
971 enum class ConvertedScaleMode : u64 {
972 None = 0, // VI seems to name this as "Unknown" but lots of games pass it, assume it's no
973 // scaling/default
974 Freeze = 1,
975 ScaleToWindow = 2,
976 Crop = 3,
977 NoCrop = 4,
978 };
979
980 // This struct is different, currently it's 1:1 but this might change in the future.
981 enum class NintendoScaleMode : u32 {
982 None = 0,
983 Freeze = 1,
984 ScaleToWindow = 2,
985 Crop = 3,
986 NoCrop = 4,
987 };
988
989 void ConvertScalingMode(Kernel::HLERequestContext& ctx) {
990 IPC::RequestParser rp{ctx};
991 auto mode = rp.PopEnum<NintendoScaleMode>();
992 LOG_DEBUG(Service_VI, "called mode={}", static_cast<u32>(mode));
993
994 IPC::ResponseBuilder rb{ctx, 4};
995 rb.Push(RESULT_SUCCESS);
996 switch (mode) {
997 case NintendoScaleMode::None:
998 rb.PushEnum(ConvertedScaleMode::None);
999 break;
1000 case NintendoScaleMode::Freeze:
1001 rb.PushEnum(ConvertedScaleMode::Freeze);
1002 break;
1003 case NintendoScaleMode::ScaleToWindow:
1004 rb.PushEnum(ConvertedScaleMode::ScaleToWindow);
1005 break;
1006 case NintendoScaleMode::Crop:
1007 rb.PushEnum(ConvertedScaleMode::Crop);
1008 break;
1009 case NintendoScaleMode::NoCrop:
1010 rb.PushEnum(ConvertedScaleMode::NoCrop);
1011 break;
1012 default:
1013 UNIMPLEMENTED_MSG("Unknown scaling mode {}", static_cast<u32>(mode));
1014 rb.PushEnum(ConvertedScaleMode::None);
1015 break;
1016 }
1017 }
1018
971 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 1019 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
972}; 1020};
973 1021
@@ -991,7 +1039,7 @@ IApplicationDisplayService::IApplicationDisplayService(
991 {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"}, 1039 {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
992 {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"}, 1040 {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
993 {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, 1041 {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
994 {2102, nullptr, "ConvertScalingMode"}, 1042 {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
995 {2450, nullptr, "GetIndirectLayerImageMap"}, 1043 {2450, nullptr, "GetIndirectLayerImageMap"},
996 {2451, nullptr, "GetIndirectLayerImageCropMap"}, 1044 {2451, nullptr, "GetIndirectLayerImageCropMap"},
997 {2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"}, 1045 {2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"},
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 951fd8257..8518dddcb 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -139,14 +139,22 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
139 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", 139 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
140 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { 140 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
141 const FileSys::VirtualFile module_file = dir->GetFile(module); 141 const FileSys::VirtualFile module_file = dir->GetFile(module);
142 if (module_file != nullptr) { 142 if (module_file == nullptr) {
143 const VAddr load_addr = next_load_addr; 143 continue;
144 next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr,
145 std::strcmp(module, "rtld") == 0, pm);
146 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
147 // Register module with GDBStub
148 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
149 } 144 }
145
146 const VAddr load_addr = next_load_addr;
147 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
148 const auto tentative_next_load_addr =
149 AppLoader_NSO::LoadModule(*module_file, load_addr, should_pass_arguments, pm);
150 if (!tentative_next_load_addr) {
151 return ResultStatus::ErrorLoadingNSO;
152 }
153
154 next_load_addr = *tentative_next_load_addr;
155 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
156 // Register module with GDBStub
157 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
150 } 158 }
151 159
152 process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()); 160 process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 91659ec17..9cd0b0ccd 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
93 return "unknown"; 93 return "unknown";
94} 94}
95 95
96constexpr std::array<const char*, 59> RESULT_MESSAGES{ 96constexpr std::array<const char*, 60> RESULT_MESSAGES{
97 "The operation completed successfully.", 97 "The operation completed successfully.",
98 "The loader requested to load is already loaded.", 98 "The loader requested to load is already loaded.",
99 "The operation is not implemented.", 99 "The operation is not implemented.",
@@ -128,6 +128,7 @@ constexpr std::array<const char*, 59> RESULT_MESSAGES{
128 "The RomFS could not be found.", 128 "The RomFS could not be found.",
129 "The ELF file has incorrect size as determined by the header.", 129 "The ELF file has incorrect size as determined by the header.",
130 "There was a general error loading the NRO into emulated memory.", 130 "There was a general error loading the NRO into emulated memory.",
131 "There was a general error loading the NSO into emulated memory.",
131 "There is no icon available.", 132 "There is no icon available.",
132 "There is no control data available.", 133 "There is no control data available.",
133 "The NAX file has a bad header.", 134 "The NAX file has a bad header.",
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 0e0333db5..e562b3a04 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -90,6 +90,7 @@ enum class ResultStatus : u16 {
90 ErrorNoRomFS, 90 ErrorNoRomFS,
91 ErrorIncorrectELFFileSize, 91 ErrorIncorrectELFFileSize,
92 ErrorLoadingNRO, 92 ErrorLoadingNRO,
93 ErrorLoadingNSO,
93 ErrorNoIcon, 94 ErrorNoIcon,
94 ErrorNoControl, 95 ErrorNoControl,
95 ErrorBadNAXHeader, 96 ErrorBadNAXHeader,
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 576fe692a..243b499f2 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -127,10 +127,10 @@ static constexpr u32 PageAlignSize(u32 size) {
127 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 127 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
128} 128}
129 129
130bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { 130bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
131 // Read NSO header 131 // Read NSO header
132 NroHeader nro_header{}; 132 NroHeader nro_header{};
133 if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { 133 if (sizeof(NroHeader) != file.ReadObject(&nro_header)) {
134 return {}; 134 return {};
135 } 135 }
136 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { 136 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
@@ -138,7 +138,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
138 } 138 }
139 139
140 // Build program image 140 // Build program image
141 std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size)); 141 std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size));
142 if (program_image.size() != PageAlignSize(nro_header.file_size)) { 142 if (program_image.size() != PageAlignSize(nro_header.file_size)) {
143 return {}; 143 return {};
144 } 144 }
@@ -182,7 +182,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
182 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); 182 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
183 183
184 // Register module with GDBStub 184 // Register module with GDBStub
185 GDBStub::RegisterModule(file->GetName(), load_base, load_base); 185 GDBStub::RegisterModule(file.GetName(), load_base, load_base);
186 186
187 return true; 187 return true;
188} 188}
@@ -195,7 +195,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
195 // Load NRO 195 // Load NRO
196 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 196 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
197 197
198 if (!LoadNro(file, base_address)) { 198 if (!LoadNro(*file, base_address)) {
199 return ResultStatus::ErrorLoadingNRO; 199 return ResultStatus::ErrorLoadingNRO;
200 } 200 }
201 201
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 04b46119a..50ee5a78a 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -41,7 +41,7 @@ public:
41 bool IsRomFSUpdatable() const override; 41 bool IsRomFSUpdatable() const override;
42 42
43private: 43private:
44 bool LoadNro(FileSys::VirtualFile file, VAddr load_base); 44 bool LoadNro(const FileSys::VfsFile& file, VAddr load_base);
45 45
46 std::vector<u8> icon_data; 46 std::vector<u8> icon_data;
47 std::unique_ptr<FileSys::NACP> nacp; 47 std::unique_ptr<FileSys::NACP> nacp;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index ba57db9bf..68efca5c0 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -93,17 +93,14 @@ static constexpr u32 PageAlignSize(u32 size) {
93 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 93 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
94} 94}
95 95
96VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, 96std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAddr load_base,
97 bool should_pass_arguments, 97 bool should_pass_arguments,
98 boost::optional<FileSys::PatchManager> pm) { 98 std::optional<FileSys::PatchManager> pm) {
99 if (file == nullptr) 99 if (file.GetSize() < sizeof(NsoHeader))
100 return {};
101
102 if (file->GetSize() < sizeof(NsoHeader))
103 return {}; 100 return {};
104 101
105 NsoHeader nso_header{}; 102 NsoHeader nso_header{};
106 if (sizeof(NsoHeader) != file->ReadObject(&nso_header)) 103 if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
107 return {}; 104 return {};
108 105
109 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) 106 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
@@ -114,7 +111,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
114 std::vector<u8> program_image; 111 std::vector<u8> program_image;
115 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { 112 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
116 std::vector<u8> data = 113 std::vector<u8> data =
117 file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); 114 file.ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
118 if (nso_header.IsSegmentCompressed(i)) { 115 if (nso_header.IsSegmentCompressed(i)) {
119 data = DecompressSegment(data, nso_header.segments[i]); 116 data = DecompressSegment(data, nso_header.segments[i]);
120 } 117 }
@@ -157,7 +154,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
157 program_image.resize(image_size); 154 program_image.resize(image_size);
158 155
159 // Apply patches if necessary 156 // Apply patches if necessary
160 if (pm != boost::none && pm->HasNSOPatch(nso_header.build_id)) { 157 if (pm && pm->HasNSOPatch(nso_header.build_id)) {
161 std::vector<u8> pi_header(program_image.size() + 0x100); 158 std::vector<u8> pi_header(program_image.size() + 0x100);
162 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); 159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
163 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); 160 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
@@ -172,7 +169,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
172 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); 169 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
173 170
174 // Register module with GDBStub 171 // Register module with GDBStub
175 GDBStub::RegisterModule(file->GetName(), load_base, load_base); 172 GDBStub::RegisterModule(file.GetName(), load_base, load_base);
176 173
177 return load_base + image_size; 174 return load_base + image_size;
178} 175}
@@ -184,7 +181,9 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
184 181
185 // Load module 182 // Load module
186 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 183 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
187 LoadModule(file, base_address, true); 184 if (!LoadModule(*file, base_address, true)) {
185 return ResultStatus::ErrorLoadingNSO;
186 }
188 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); 187 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
189 188
190 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); 189 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 70ab3b718..433306139 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <optional>
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "core/file_sys/patch_manager.h" 9#include "core/file_sys/patch_manager.h"
9#include "core/loader/linker.h" 10#include "core/loader/linker.h"
@@ -36,8 +37,9 @@ public:
36 return IdentifyType(file); 37 return IdentifyType(file);
37 } 38 }
38 39
39 static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, bool should_pass_arguments, 40 static std::optional<VAddr> LoadModule(const FileSys::VfsFile& file, VAddr load_base,
40 boost::optional<FileSys::PatchManager> pm = boost::none); 41 bool should_pass_arguments,
42 std::optional<FileSys::PatchManager> pm = {});
41 43
42 ResultStatus Load(Kernel::Process& process) override; 44 ResultStatus Load(Kernel::Process& process) override;
43}; 45};
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 7a619acb4..461607c95 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -59,8 +59,7 @@ ResultStatus AppLoader_XCI::Load(Kernel::Process& process) {
59 if (xci->GetProgramNCAStatus() != ResultStatus::Success) 59 if (xci->GetProgramNCAStatus() != ResultStatus::Success)
60 return xci->GetProgramNCAStatus(); 60 return xci->GetProgramNCAStatus();
61 61
62 const auto nca = xci->GetProgramNCA(); 62 if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false))
63 if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false))
64 return ResultStatus::ErrorMissingProductionKeyFile; 63 return ResultStatus::ErrorMissingProductionKeyFile;
65 64
66 const auto result = nca_loader->Load(process); 65 const auto result = nca_loader->Load(process);
diff --git a/src/core/settings.h b/src/core/settings.h
index 83b9a04c8..8f2da01c8 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -136,7 +136,7 @@ struct Values {
136 float resolution_factor; 136 float resolution_factor;
137 bool use_frame_limit; 137 bool use_frame_limit;
138 u16 frame_limit; 138 u16 frame_limit;
139 bool use_accurate_framebuffers; 139 bool use_accurate_gpu_emulation;
140 140
141 float bg_red; 141 float bg_red;
142 float bg_green; 142 float bg_green;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 7b04792b5..0de13edd3 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -163,8 +163,8 @@ TelemetrySession::TelemetrySession() {
163 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit", 163 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit",
164 Settings::values.use_frame_limit); 164 Settings::values.use_frame_limit);
165 AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit); 165 AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit);
166 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateFramebuffers", 166 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation",
167 Settings::values.use_accurate_framebuffers); 167 Settings::values.use_accurate_gpu_emulation);
168 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", 168 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
169 Settings::values.use_docked_mode); 169 Settings::values.use_docked_mode);
170} 170}
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 912e785b9..74e44c7fe 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -47,9 +47,12 @@ void Fermi2D::HandleSurfaceCopy() {
47 u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format); 47 u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format);
48 48
49 if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst)) { 49 if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst)) {
50 // TODO(bunnei): The below implementation currently will not get hit, as 50 rasterizer.FlushRegion(source_cpu, src_bytes_per_pixel * regs.src.width * regs.src.height);
51 // AccelerateSurfaceCopy tries to always copy and will always return success. This should be 51 // We have to invalidate the destination region to evict any outdated surfaces from the
52 // changed once we properly support flushing. 52 // cache. We do this before actually writing the new data because the destination address
53 // might contain a dirty surface that will have to be written back to memory.
54 rasterizer.InvalidateRegion(dest_cpu,
55 dst_bytes_per_pixel * regs.dst.width * regs.dst.height);
53 56
54 if (regs.src.linear == regs.dst.linear) { 57 if (regs.src.linear == regs.dst.linear) {
55 // If the input layout and the output layout are the same, just perform a raw copy. 58 // If the input layout and the output layout are the same, just perform a raw copy.
@@ -62,14 +65,16 @@ void Fermi2D::HandleSurfaceCopy() {
62 u8* dst_buffer = Memory::GetPointer(dest_cpu); 65 u8* dst_buffer = Memory::GetPointer(dest_cpu);
63 if (!regs.src.linear && regs.dst.linear) { 66 if (!regs.src.linear && regs.dst.linear) {
64 // If the input is tiled and the output is linear, deswizzle the input and copy it over. 67 // If the input is tiled and the output is linear, deswizzle the input and copy it over.
65 Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel, 68 Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
66 dst_bytes_per_pixel, src_buffer, dst_buffer, true, 69 src_bytes_per_pixel, dst_bytes_per_pixel, src_buffer,
67 regs.src.BlockHeight()); 70 dst_buffer, true, regs.src.BlockHeight(),
71 regs.src.BlockDepth());
68 } else { 72 } else {
69 // If the input is linear and the output is tiled, swizzle the input and copy it over. 73 // If the input is linear and the output is tiled, swizzle the input and copy it over.
70 Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel, 74 Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
71 dst_bytes_per_pixel, dst_buffer, src_buffer, false, 75 src_bytes_per_pixel, dst_bytes_per_pixel, dst_buffer,
72 regs.dst.BlockHeight()); 76 src_buffer, false, regs.dst.BlockHeight(),
77 regs.dst.BlockDepth());
73 } 78 }
74 } 79 }
75} 80}
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index 66ae6332d..585290d9f 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -5,10 +5,14 @@
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/memory.h" 6#include "core/memory.h"
7#include "video_core/engines/kepler_memory.h" 7#include "video_core/engines/kepler_memory.h"
8#include "video_core/rasterizer_interface.h"
8 9
9namespace Tegra::Engines { 10namespace Tegra::Engines {
10 11
11KeplerMemory::KeplerMemory(MemoryManager& memory_manager) : memory_manager(memory_manager) {} 12KeplerMemory::KeplerMemory(VideoCore::RasterizerInterface& rasterizer,
13 MemoryManager& memory_manager)
14 : memory_manager(memory_manager), rasterizer{rasterizer} {}
15
12KeplerMemory::~KeplerMemory() = default; 16KeplerMemory::~KeplerMemory() = default;
13 17
14void KeplerMemory::WriteReg(u32 method, u32 value) { 18void KeplerMemory::WriteReg(u32 method, u32 value) {
@@ -37,6 +41,11 @@ void KeplerMemory::ProcessData(u32 data) {
37 VAddr dest_address = 41 VAddr dest_address =
38 *memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32)); 42 *memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32));
39 43
44 // We have to invalidate the destination region to evict any outdated surfaces from the cache.
45 // We do this before actually writing the new data because the destination address might contain
46 // a dirty surface that will have to be written back to memory.
47 rasterizer.InvalidateRegion(dest_address, sizeof(u32));
48
40 Memory::Write32(dest_address, data); 49 Memory::Write32(dest_address, data);
41 50
42 state.write_offset++; 51 state.write_offset++;
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
index b0d0078cf..bf4a13cff 100644
--- a/src/video_core/engines/kepler_memory.h
+++ b/src/video_core/engines/kepler_memory.h
@@ -11,6 +11,10 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/memory_manager.h" 12#include "video_core/memory_manager.h"
13 13
14namespace VideoCore {
15class RasterizerInterface;
16}
17
14namespace Tegra::Engines { 18namespace Tegra::Engines {
15 19
16#define KEPLERMEMORY_REG_INDEX(field_name) \ 20#define KEPLERMEMORY_REG_INDEX(field_name) \
@@ -18,7 +22,7 @@ namespace Tegra::Engines {
18 22
19class KeplerMemory final { 23class KeplerMemory final {
20public: 24public:
21 KeplerMemory(MemoryManager& memory_manager); 25 KeplerMemory(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
22 ~KeplerMemory(); 26 ~KeplerMemory();
23 27
24 /// Write the value to the register identified by method. 28 /// Write the value to the register identified by method.
@@ -72,6 +76,7 @@ public:
72 76
73private: 77private:
74 MemoryManager& memory_manager; 78 MemoryManager& memory_manager;
79 VideoCore::RasterizerInterface& rasterizer;
75 80
76 void ProcessData(u32 data); 81 void ProcessData(u32 data);
77}; 82};
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index c8d1b6478..c8af1c6b6 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -448,7 +448,10 @@ public:
448 BitField<8, 3, u32> block_depth; 448 BitField<8, 3, u32> block_depth;
449 BitField<12, 1, InvMemoryLayout> type; 449 BitField<12, 1, InvMemoryLayout> type;
450 } memory_layout; 450 } memory_layout;
451 u32 array_mode; 451 union {
452 BitField<0, 16, u32> array_mode;
453 BitField<16, 1, u32> volume;
454 };
452 u32 layer_stride; 455 u32 layer_stride;
453 u32 base_layer; 456 u32 base_layer;
454 INSERT_PADDING_WORDS(7); 457 INSERT_PADDING_WORDS(7);
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index aa7481b8c..103cd110e 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -4,12 +4,14 @@
4 4
5#include "core/memory.h" 5#include "core/memory.h"
6#include "video_core/engines/maxwell_dma.h" 6#include "video_core/engines/maxwell_dma.h"
7#include "video_core/rasterizer_interface.h"
7#include "video_core/textures/decoders.h" 8#include "video_core/textures/decoders.h"
8 9
9namespace Tegra { 10namespace Tegra {
10namespace Engines { 11namespace Engines {
11 12
12MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager) : memory_manager(memory_manager) {} 13MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
14 : memory_manager(memory_manager), rasterizer{rasterizer} {}
13 15
14void MaxwellDMA::WriteReg(u32 method, u32 value) { 16void MaxwellDMA::WriteReg(u32 method, u32 value) {
15 ASSERT_MSG(method < Regs::NUM_REGS, 17 ASSERT_MSG(method < Regs::NUM_REGS,
@@ -44,36 +46,79 @@ void MaxwellDMA::HandleCopy() {
44 ASSERT(regs.exec.query_mode == Regs::QueryMode::None); 46 ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
45 ASSERT(regs.exec.query_intr == Regs::QueryIntr::None); 47 ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
46 ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2); 48 ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
47 ASSERT(regs.src_params.pos_x == 0);
48 ASSERT(regs.src_params.pos_y == 0);
49 ASSERT(regs.dst_params.pos_x == 0); 49 ASSERT(regs.dst_params.pos_x == 0);
50 ASSERT(regs.dst_params.pos_y == 0); 50 ASSERT(regs.dst_params.pos_y == 0);
51 51
52 if (regs.exec.is_dst_linear == regs.exec.is_src_linear) { 52 if (!regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
53 std::size_t copy_size = regs.x_count; 53 // If both the source and the destination are in block layout, assert.
54 UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented");
55 return;
56 }
54 57
58 if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
55 // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D 59 // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
56 // buffer of length `x_count`, otherwise we copy a 2D buffer of size (x_count, y_count). 60 // buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
57 if (regs.exec.enable_2d) { 61 // y_count).
58 copy_size = copy_size * regs.y_count; 62 if (!regs.exec.enable_2d) {
63 Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count);
64 return;
59 } 65 }
60 66
61 Memory::CopyBlock(dest_cpu, source_cpu, copy_size); 67 // If both the source and the destination are in linear layout, perform a line-by-line
68 // copy. We're going to take a subrect of size (x_count, y_count) from the source
69 // rectangle. There is no need to manually flush/invalidate the regions because
70 // CopyBlock does that for us.
71 for (u32 line = 0; line < regs.y_count; ++line) {
72 const VAddr source_line = source_cpu + line * regs.src_pitch;
73 const VAddr dest_line = dest_cpu + line * regs.dst_pitch;
74 Memory::CopyBlock(dest_line, source_line, regs.x_count);
75 }
62 return; 76 return;
63 } 77 }
64 78
65 ASSERT(regs.exec.enable_2d == 1); 79 ASSERT(regs.exec.enable_2d == 1);
80
81 std::size_t copy_size = regs.x_count * regs.y_count;
82
83 const auto FlushAndInvalidate = [&](u32 src_size, u32 dst_size) {
84 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
85 // copying.
86 rasterizer.FlushRegion(source_cpu, src_size);
87
88 // We have to invalidate the destination region to evict any outdated surfaces from the
89 // cache. We do this before actually writing the new data because the destination address
90 // might contain a dirty surface that will have to be written back to memory.
91 rasterizer.InvalidateRegion(dest_cpu, dst_size);
92 };
93
66 u8* src_buffer = Memory::GetPointer(source_cpu); 94 u8* src_buffer = Memory::GetPointer(source_cpu);
67 u8* dst_buffer = Memory::GetPointer(dest_cpu); 95 u8* dst_buffer = Memory::GetPointer(dest_cpu);
68 96
69 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { 97 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
98 ASSERT(regs.src_params.size_z == 1);
70 // If the input is tiled and the output is linear, deswizzle the input and copy it over. 99 // If the input is tiled and the output is linear, deswizzle the input and copy it over.
71 Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, 1, 1, src_buffer, 100
72 dst_buffer, true, regs.src_params.BlockHeight()); 101 u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x;
102
103 FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y,
104 copy_size * src_bytes_per_pixel);
105
106 Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
107 regs.src_params.size_x, src_bytes_per_pixel, source_cpu, dest_cpu,
108 regs.src_params.BlockHeight(), regs.src_params.pos_x,
109 regs.src_params.pos_y);
73 } else { 110 } else {
111 ASSERT(regs.dst_params.size_z == 1);
112 ASSERT(regs.src_pitch == regs.x_count);
113
114 u32 src_bpp = regs.src_pitch / regs.x_count;
115
116 FlushAndInvalidate(regs.src_pitch * regs.y_count,
117 regs.dst_params.size_x * regs.dst_params.size_y * src_bpp);
118
74 // If the input is linear and the output is tiled, swizzle the input and copy it over. 119 // If the input is linear and the output is tiled, swizzle the input and copy it over.
75 Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, 1, 1, dst_buffer, 120 Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x,
76 src_buffer, false, regs.dst_params.BlockHeight()); 121 src_bpp, dest_cpu, source_cpu, regs.dst_params.BlockHeight());
77 } 122 }
78} 123}
79 124
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 311ccb616..5f3704f05 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -12,11 +12,15 @@
12#include "video_core/gpu.h" 12#include "video_core/gpu.h"
13#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
14 14
15namespace VideoCore {
16class RasterizerInterface;
17}
18
15namespace Tegra::Engines { 19namespace Tegra::Engines {
16 20
17class MaxwellDMA final { 21class MaxwellDMA final {
18public: 22public:
19 explicit MaxwellDMA(MemoryManager& memory_manager); 23 explicit MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
20 ~MaxwellDMA() = default; 24 ~MaxwellDMA() = default;
21 25
22 /// Write the value to the register identified by method. 26 /// Write the value to the register identified by method.
@@ -43,6 +47,10 @@ public:
43 u32 BlockHeight() const { 47 u32 BlockHeight() const {
44 return 1 << block_height; 48 return 1 << block_height;
45 } 49 }
50
51 u32 BlockDepth() const {
52 return 1 << block_depth;
53 }
46 }; 54 };
47 55
48 static_assert(sizeof(Parameters) == 24, "Parameters has wrong size"); 56 static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
@@ -129,6 +137,8 @@ public:
129 MemoryManager& memory_manager; 137 MemoryManager& memory_manager;
130 138
131private: 139private:
140 VideoCore::RasterizerInterface& rasterizer;
141
132 /// Performs the copy from the source buffer to the destination buffer as configured in the 142 /// Performs the copy from the source buffer to the destination buffer as configured in the
133 /// registers. 143 /// registers.
134 void HandleCopy(); 144 void HandleCopy();
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 39ae065de..e3d67ff87 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -267,7 +267,7 @@ enum class ControlCode : u64 {
267 GTU = 12, 267 GTU = 12,
268 NEU = 13, 268 NEU = 13,
269 GEU = 14, 269 GEU = 14,
270 // 270 T = 15,
271 OFF = 16, 271 OFF = 16,
272 LO = 17, 272 LO = 17,
273 SFF = 18, 273 SFF = 18,
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 9ba7e3533..83c7e5b0b 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -27,8 +27,8 @@ GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
27 maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager); 27 maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
28 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); 28 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
29 maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); 29 maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
30 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager); 30 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(rasterizer, *memory_manager);
31 kepler_memory = std::make_unique<Engines::KeplerMemory>(*memory_manager); 31 kepler_memory = std::make_unique<Engines::KeplerMemory>(rasterizer, *memory_manager);
32} 32}
33 33
34GPU::~GPU() = default; 34GPU::~GPU() = default;
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index ca923d17d..022d4ab74 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -87,6 +87,16 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
87 return gpu_addr; 87 return gpu_addr;
88} 88}
89 89
90GPUVAddr MemoryManager::GetRegionEnd(GPUVAddr region_start) const {
91 for (const auto& region : mapped_regions) {
92 const GPUVAddr region_end{region.gpu_addr + region.size};
93 if (region_start >= region.gpu_addr && region_start < region_end) {
94 return region_end;
95 }
96 }
97 return {};
98}
99
90boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) { 100boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
91 GPUVAddr gpu_addr = 0; 101 GPUVAddr gpu_addr = 0;
92 u64 free_space = 0; 102 u64 free_space = 0;
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 86765e72a..caf80093f 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -26,6 +26,7 @@ public:
26 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); 26 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size);
27 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size); 27 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size);
28 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); 28 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size);
29 GPUVAddr GetRegionEnd(GPUVAddr region_start) const;
29 boost::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr); 30 boost::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr);
30 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const; 31 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const;
31 32
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
index 083b283b0..0a3b3951e 100644
--- a/src/video_core/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache.h
@@ -11,32 +11,77 @@
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/core.h" 13#include "core/core.h"
14#include "core/settings.h"
14#include "video_core/rasterizer_interface.h" 15#include "video_core/rasterizer_interface.h"
15#include "video_core/renderer_base.h" 16#include "video_core/renderer_base.h"
16 17
18class RasterizerCacheObject {
19public:
20 /// Gets the address of the shader in guest memory, required for cache management
21 virtual VAddr GetAddr() const = 0;
22
23 /// Gets the size of the shader in guest memory, required for cache management
24 virtual std::size_t GetSizeInBytes() const = 0;
25
26 /// Wriets any cached resources back to memory
27 virtual void Flush() = 0;
28
29 /// Sets whether the cached object should be considered registered
30 void SetIsRegistered(bool registered) {
31 is_registered = registered;
32 }
33
34 /// Returns true if the cached object is registered
35 bool IsRegistered() const {
36 return is_registered;
37 }
38
39 /// Returns true if the cached object is dirty
40 bool IsDirty() const {
41 return is_dirty;
42 }
43
44 /// Returns ticks from when this cached object was last modified
45 u64 GetLastModifiedTicks() const {
46 return last_modified_ticks;
47 }
48
49 /// Marks an object as recently modified, used to specify whether it is clean or dirty
50 template <class T>
51 void MarkAsModified(bool dirty, T& cache) {
52 is_dirty = dirty;
53 last_modified_ticks = cache.GetModifiedTicks();
54 }
55
56private:
57 bool is_registered{}; ///< Whether the object is currently registered with the cache
58 bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory)
59 u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing
60};
61
17template <class T> 62template <class T>
18class RasterizerCache : NonCopyable { 63class RasterizerCache : NonCopyable {
64 friend class RasterizerCacheObject;
65
19public: 66public:
67 /// Write any cached resources overlapping the specified region back to memory
68 void FlushRegion(Tegra::GPUVAddr addr, size_t size) {
69 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
70 for (auto& object : objects) {
71 FlushObject(object);
72 }
73 }
74
20 /// Mark the specified region as being invalidated 75 /// Mark the specified region as being invalidated
21 void InvalidateRegion(VAddr addr, u64 size) { 76 void InvalidateRegion(VAddr addr, u64 size) {
22 if (size == 0) 77 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
23 return; 78 for (auto& object : objects) {
24 79 if (!object->IsRegistered()) {
25 const ObjectInterval interval{addr, addr + size}; 80 // Skip duplicates
26 for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) { 81 continue;
27 for (auto& cached_object : pair.second) {
28 if (!cached_object)
29 continue;
30
31 remove_objects.emplace(cached_object);
32 } 82 }
83 Unregister(object);
33 } 84 }
34
35 for (auto& remove_object : remove_objects) {
36 Unregister(remove_object);
37 }
38
39 remove_objects.clear();
40 } 85 }
41 86
42 /// Invalidates everything in the cache 87 /// Invalidates everything in the cache
@@ -62,6 +107,7 @@ protected:
62 107
63 /// Register an object into the cache 108 /// Register an object into the cache
64 void Register(const T& object) { 109 void Register(const T& object) {
110 object->SetIsRegistered(true);
65 object_cache.add({GetInterval(object), ObjectSet{object}}); 111 object_cache.add({GetInterval(object), ObjectSet{object}});
66 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer(); 112 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
67 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1); 113 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1);
@@ -69,12 +115,57 @@ protected:
69 115
70 /// Unregisters an object from the cache 116 /// Unregisters an object from the cache
71 void Unregister(const T& object) { 117 void Unregister(const T& object) {
118 object->SetIsRegistered(false);
72 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer(); 119 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
73 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1); 120 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
121
122 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
123 if (Settings::values.use_accurate_gpu_emulation) {
124 FlushObject(object);
125 }
126
74 object_cache.subtract({GetInterval(object), ObjectSet{object}}); 127 object_cache.subtract({GetInterval(object), ObjectSet{object}});
75 } 128 }
76 129
130 /// Returns a ticks counter used for tracking when cached objects were last modified
131 u64 GetModifiedTicks() {
132 return ++modified_ticks;
133 }
134
77private: 135private:
136 /// Returns a list of cached objects from the specified memory region, ordered by access time
137 std::vector<T> GetSortedObjectsFromRegion(VAddr addr, u64 size) {
138 if (size == 0) {
139 return {};
140 }
141
142 std::vector<T> objects;
143 const ObjectInterval interval{addr, addr + size};
144 for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
145 for (auto& cached_object : pair.second) {
146 if (!cached_object) {
147 continue;
148 }
149 objects.push_back(cached_object);
150 }
151 }
152
153 std::sort(objects.begin(), objects.end(), [](const T& a, const T& b) -> bool {
154 return a->GetLastModifiedTicks() < b->GetLastModifiedTicks();
155 });
156
157 return objects;
158 }
159
160 /// Flushes the specified object, updating appropriate cache state as needed
161 void FlushObject(const T& object) {
162 if (!object->IsDirty()) {
163 return;
164 }
165 object->Flush();
166 object->MarkAsModified(false, *this);
167 }
168
78 using ObjectSet = std::set<T>; 169 using ObjectSet = std::set<T>;
79 using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>; 170 using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>;
80 using ObjectInterval = typename ObjectCache::interval_type; 171 using ObjectInterval = typename ObjectCache::interval_type;
@@ -84,6 +175,6 @@ private:
84 object->GetAddr() + object->GetSizeInBytes()); 175 object->GetAddr() + object->GetSizeInBytes());
85 } 176 }
86 177
87 ObjectCache object_cache; 178 ObjectCache object_cache; ///< Cache of objects
88 ObjectSet remove_objects; 179 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
89}; 180};
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 965976334..be29dc8be 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -15,15 +15,18 @@
15 15
16namespace OpenGL { 16namespace OpenGL {
17 17
18struct CachedBufferEntry final { 18struct CachedBufferEntry final : public RasterizerCacheObject {
19 VAddr GetAddr() const { 19 VAddr GetAddr() const override {
20 return addr; 20 return addr;
21 } 21 }
22 22
23 std::size_t GetSizeInBytes() const { 23 std::size_t GetSizeInBytes() const override {
24 return size; 24 return size;
25 } 25 }
26 26
27 // We do not have to flush this cache as things in it are never modified by us.
28 void Flush() override {}
29
27 VAddr addr; 30 VAddr addr;
28 std::size_t size; 31 std::size_t size;
29 GLintptr offset; 32 GLintptr offset;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 84582c777..3daccf82f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -286,7 +286,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
286 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); 286 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
287 287
288 // Bind the buffer 288 // Bind the buffer
289 glBindBufferRange(GL_UNIFORM_BUFFER, stage, buffer_cache.GetHandle(), offset, sizeof(ubo)); 289 glBindBufferRange(GL_UNIFORM_BUFFER, static_cast<GLuint>(stage), buffer_cache.GetHandle(),
290 offset, static_cast<GLsizeiptr>(sizeof(ubo)));
290 291
291 Shader shader{shader_cache.GetStageProgram(program)}; 292 Shader shader{shader_cache.GetStageProgram(program)};
292 293
@@ -423,6 +424,13 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
423 // Used when just a single color attachment is enabled, e.g. for clearing a color buffer 424 // Used when just a single color attachment is enabled, e.g. for clearing a color buffer
424 Surface color_surface = 425 Surface color_surface =
425 res_cache.GetColorBufferSurface(*single_color_target, preserve_contents); 426 res_cache.GetColorBufferSurface(*single_color_target, preserve_contents);
427
428 if (color_surface) {
429 // Assume that a surface will be written to if it is used as a framebuffer, even if
430 // the shader doesn't actually write to it.
431 color_surface->MarkAsModified(true, res_cache);
432 }
433
426 glFramebufferTexture2D( 434 glFramebufferTexture2D(
427 GL_DRAW_FRAMEBUFFER, 435 GL_DRAW_FRAMEBUFFER,
428 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D, 436 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D,
@@ -433,6 +441,13 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
433 std::array<GLenum, Maxwell::NumRenderTargets> buffers; 441 std::array<GLenum, Maxwell::NumRenderTargets> buffers;
434 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { 442 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
435 Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents); 443 Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents);
444
445 if (color_surface) {
446 // Assume that a surface will be written to if it is used as a framebuffer, even
447 // if the shader doesn't actually write to it.
448 color_surface->MarkAsModified(true, res_cache);
449 }
450
436 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); 451 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
437 glFramebufferTexture2D( 452 glFramebufferTexture2D(
438 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), 453 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index),
@@ -452,6 +467,10 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
452 } 467 }
453 468
454 if (depth_surface) { 469 if (depth_surface) {
470 // Assume that a surface will be written to if it is used as a framebuffer, even if
471 // the shader doesn't actually write to it.
472 depth_surface->MarkAsModified(true, res_cache);
473
455 if (regs.stencil_enable) { 474 if (regs.stencil_enable) {
456 // Attach both depth and stencil 475 // Attach both depth and stencil
457 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 476 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
@@ -616,7 +635,14 @@ void RasterizerOpenGL::DrawArrays() {
616 635
617void RasterizerOpenGL::FlushAll() {} 636void RasterizerOpenGL::FlushAll() {}
618 637
619void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {} 638void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
639 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
640
641 if (Settings::values.use_accurate_gpu_emulation) {
642 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
643 res_cache.FlushRegion(addr, size);
644 }
645}
620 646
621void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 647void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
622 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 648 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
@@ -626,12 +652,19 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
626} 652}
627 653
628void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { 654void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
655 FlushRegion(addr, size);
629 InvalidateRegion(addr, size); 656 InvalidateRegion(addr, size);
630} 657}
631 658
632bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, 659bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
633 const Tegra::Engines::Fermi2D::Regs::Surface& dst) { 660 const Tegra::Engines::Fermi2D::Regs::Surface& dst) {
634 MICROPROFILE_SCOPE(OpenGL_Blits); 661 MICROPROFILE_SCOPE(OpenGL_Blits);
662
663 if (Settings::values.use_accurate_gpu_emulation) {
664 // Skip the accelerated copy and perform a slow but more accurate copy
665 return false;
666 }
667
635 res_cache.FermiCopySurface(src, dst); 668 res_cache.FermiCopySurface(src, dst);
636 return true; 669 return true;
637} 670}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 65a220c41..9c8925383 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -34,16 +34,53 @@ struct FormatTuple {
34 bool compressed; 34 bool compressed;
35}; 35};
36 36
37static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { 37static bool IsPixelFormatASTC(PixelFormat format) {
38 auto& gpu{Core::System::GetInstance().GPU()}; 38 switch (format) {
39 const auto cpu_addr{gpu.MemoryManager().GpuToCpuAddress(gpu_addr)}; 39 case PixelFormat::ASTC_2D_4X4:
40 return cpu_addr ? *cpu_addr : 0; 40 case PixelFormat::ASTC_2D_5X4:
41 case PixelFormat::ASTC_2D_8X8:
42 case PixelFormat::ASTC_2D_8X5:
43 return true;
44 default:
45 return false;
46 }
47}
48
49static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
50 switch (format) {
51 case PixelFormat::ASTC_2D_4X4:
52 return {4, 4};
53 case PixelFormat::ASTC_2D_5X4:
54 return {5, 4};
55 case PixelFormat::ASTC_2D_8X8:
56 return {8, 8};
57 case PixelFormat::ASTC_2D_8X5:
58 return {8, 5};
59 default:
60 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
61 UNREACHABLE();
62 }
63}
64
65void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
66 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
67 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)};
68
69 addr = cpu_addr ? *cpu_addr : 0;
70 gpu_addr = gpu_addr_;
71 size_in_bytes = SizeInBytesRaw();
72
73 if (IsPixelFormatASTC(pixel_format)) {
74 // ASTC is uncompressed in software, in emulated as RGBA8
75 size_in_bytes_gl = width * height * depth * 4;
76 } else {
77 size_in_bytes_gl = SizeInBytesGL();
78 }
41} 79}
42 80
43/*static*/ SurfaceParams SurfaceParams::CreateForTexture( 81/*static*/ SurfaceParams SurfaceParams::CreateForTexture(
44 const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { 82 const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) {
45 SurfaceParams params{}; 83 SurfaceParams params{};
46 params.addr = TryGetCpuAddr(config.tic.Address());
47 params.is_tiled = config.tic.IsTiled(); 84 params.is_tiled = config.tic.IsTiled();
48 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0, 85 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
49 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, 86 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
@@ -87,18 +124,18 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
87 break; 124 break;
88 } 125 }
89 126
90 params.size_in_bytes_total = params.SizeInBytesTotal();
91 params.size_in_bytes_2d = params.SizeInBytes2D();
92 params.max_mip_level = config.tic.max_mip_level + 1; 127 params.max_mip_level = config.tic.max_mip_level + 1;
93 params.rt = {}; 128 params.rt = {};
94 129
130 params.InitCacheParameters(config.tic.Address());
131
95 return params; 132 return params;
96} 133}
97 134
98/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) { 135/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) {
99 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]}; 136 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]};
100 SurfaceParams params{}; 137 SurfaceParams params{};
101 params.addr = TryGetCpuAddr(config.Address()); 138
102 params.is_tiled = 139 params.is_tiled =
103 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; 140 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
104 params.block_width = 1 << config.memory_layout.block_width; 141 params.block_width = 1 << config.memory_layout.block_width;
@@ -112,16 +149,17 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
112 params.unaligned_height = config.height; 149 params.unaligned_height = config.height;
113 params.target = SurfaceTarget::Texture2D; 150 params.target = SurfaceTarget::Texture2D;
114 params.depth = 1; 151 params.depth = 1;
115 params.size_in_bytes_total = params.SizeInBytesTotal();
116 params.size_in_bytes_2d = params.SizeInBytes2D();
117 params.max_mip_level = 0; 152 params.max_mip_level = 0;
118 153
119 // Render target specific parameters, not used for caching 154 // Render target specific parameters, not used for caching
120 params.rt.index = static_cast<u32>(index); 155 params.rt.index = static_cast<u32>(index);
121 params.rt.array_mode = config.array_mode; 156 params.rt.array_mode = config.array_mode;
122 params.rt.layer_stride = config.layer_stride; 157 params.rt.layer_stride = config.layer_stride;
158 params.rt.volume = config.volume;
123 params.rt.base_layer = config.base_layer; 159 params.rt.base_layer = config.base_layer;
124 160
161 params.InitCacheParameters(config.Address());
162
125 return params; 163 return params;
126} 164}
127 165
@@ -130,7 +168,7 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
130 u32 block_width, u32 block_height, u32 block_depth, 168 u32 block_width, u32 block_height, u32 block_depth,
131 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) { 169 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
132 SurfaceParams params{}; 170 SurfaceParams params{};
133 params.addr = TryGetCpuAddr(zeta_address); 171
134 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; 172 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
135 params.block_width = 1 << std::min(block_width, 5U); 173 params.block_width = 1 << std::min(block_width, 5U);
136 params.block_height = 1 << std::min(block_height, 5U); 174 params.block_height = 1 << std::min(block_height, 5U);
@@ -143,18 +181,18 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
143 params.unaligned_height = zeta_height; 181 params.unaligned_height = zeta_height;
144 params.target = SurfaceTarget::Texture2D; 182 params.target = SurfaceTarget::Texture2D;
145 params.depth = 1; 183 params.depth = 1;
146 params.size_in_bytes_total = params.SizeInBytesTotal();
147 params.size_in_bytes_2d = params.SizeInBytes2D();
148 params.max_mip_level = 0; 184 params.max_mip_level = 0;
149 params.rt = {}; 185 params.rt = {};
150 186
187 params.InitCacheParameters(zeta_address);
188
151 return params; 189 return params;
152} 190}
153 191
154/*static*/ SurfaceParams SurfaceParams::CreateForFermiCopySurface( 192/*static*/ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
155 const Tegra::Engines::Fermi2D::Regs::Surface& config) { 193 const Tegra::Engines::Fermi2D::Regs::Surface& config) {
156 SurfaceParams params{}; 194 SurfaceParams params{};
157 params.addr = TryGetCpuAddr(config.Address()); 195
158 params.is_tiled = !config.linear; 196 params.is_tiled = !config.linear;
159 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0, 197 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
160 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0, 198 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
@@ -167,11 +205,11 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
167 params.unaligned_height = config.height; 205 params.unaligned_height = config.height;
168 params.target = SurfaceTarget::Texture2D; 206 params.target = SurfaceTarget::Texture2D;
169 params.depth = 1; 207 params.depth = 1;
170 params.size_in_bytes_total = params.SizeInBytesTotal();
171 params.size_in_bytes_2d = params.SizeInBytes2D();
172 params.max_mip_level = 0; 208 params.max_mip_level = 0;
173 params.rt = {}; 209 params.rt = {};
174 210
211 params.InitCacheParameters(config.Address());
212
175 return params; 213 return params;
176} 214}
177 215
@@ -231,6 +269,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
231 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI 269 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI
232 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI 270 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI
233 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 271 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8
272 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5
273 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4
234 274
235 // Depth formats 275 // Depth formats
236 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F 276 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -274,28 +314,6 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType
274 return format; 314 return format;
275} 315}
276 316
277static bool IsPixelFormatASTC(PixelFormat format) {
278 switch (format) {
279 case PixelFormat::ASTC_2D_4X4:
280 case PixelFormat::ASTC_2D_8X8:
281 return true;
282 default:
283 return false;
284 }
285}
286
287static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
288 switch (format) {
289 case PixelFormat::ASTC_2D_4X4:
290 return {4, 4};
291 case PixelFormat::ASTC_2D_8X8:
292 return {8, 8};
293 default:
294 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
295 UNREACHABLE();
296 }
297}
298
299MathUtil::Rectangle<u32> SurfaceParams::GetRect() const { 317MathUtil::Rectangle<u32> SurfaceParams::GetRect() const {
300 u32 actual_height{unaligned_height}; 318 u32 actual_height{unaligned_height};
301 if (IsPixelFormatASTC(pixel_format)) { 319 if (IsPixelFormatASTC(pixel_format)) {
@@ -323,29 +341,27 @@ static bool IsFormatBCn(PixelFormat format) {
323} 341}
324 342
325template <bool morton_to_gl, PixelFormat format> 343template <bool morton_to_gl, PixelFormat format>
326void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::size_t gl_buffer_size, 344void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer,
327 VAddr addr) { 345 std::size_t gl_buffer_size, VAddr addr) {
328 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; 346 constexpr u32 bytes_per_pixel = SurfaceParams::GetBytesPerPixel(format);
329 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); 347
348 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
349 // pixel values.
350 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
330 351
331 if (morton_to_gl) { 352 if (morton_to_gl) {
332 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
333 // pixel values.
334 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
335 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( 353 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
336 addr, tile_size, bytes_per_pixel, stride, height, block_height); 354 addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth);
337 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())}; 355 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
338 memcpy(gl_buffer, data.data(), size_to_copy); 356 memcpy(gl_buffer, data.data(), size_to_copy);
339 } else { 357 } else {
340 // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should 358 Tegra::Texture::CopySwizzledData(stride / tile_size, height / tile_size, depth,
341 // check the configuration for this and perform more generic un/swizzle 359 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr),
342 LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); 360 gl_buffer, false, block_height, block_depth);
343 VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel,
344 Memory::GetPointer(addr), gl_buffer, morton_to_gl);
345 } 361 }
346} 362}
347 363
348static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), 364static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
349 SurfaceParams::MaxPixelFormat> 365 SurfaceParams::MaxPixelFormat>
350 morton_to_gl_fns = { 366 morton_to_gl_fns = {
351 // clang-format off 367 // clang-format off
@@ -395,6 +411,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
395 MortonCopy<true, PixelFormat::RG32UI>, 411 MortonCopy<true, PixelFormat::RG32UI>,
396 MortonCopy<true, PixelFormat::R32UI>, 412 MortonCopy<true, PixelFormat::R32UI>,
397 MortonCopy<true, PixelFormat::ASTC_2D_8X8>, 413 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
414 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
415 MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
398 MortonCopy<true, PixelFormat::Z32F>, 416 MortonCopy<true, PixelFormat::Z32F>,
399 MortonCopy<true, PixelFormat::Z16>, 417 MortonCopy<true, PixelFormat::Z16>,
400 MortonCopy<true, PixelFormat::Z24S8>, 418 MortonCopy<true, PixelFormat::Z24S8>,
@@ -403,7 +421,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
403 // clang-format on 421 // clang-format on
404}; 422};
405 423
406static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), 424static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
407 SurfaceParams::MaxPixelFormat> 425 SurfaceParams::MaxPixelFormat>
408 gl_to_morton_fns = { 426 gl_to_morton_fns = {
409 // clang-format off 427 // clang-format off
@@ -420,17 +438,16 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
420 MortonCopy<false, PixelFormat::RGBA16UI>, 438 MortonCopy<false, PixelFormat::RGBA16UI>,
421 MortonCopy<false, PixelFormat::R11FG11FB10F>, 439 MortonCopy<false, PixelFormat::R11FG11FB10F>,
422 MortonCopy<false, PixelFormat::RGBA32UI>, 440 MortonCopy<false, PixelFormat::RGBA32UI>,
423 // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/BC6H_UF16/BC6H_SF16/ASTC_2D_4X4 441 MortonCopy<false, PixelFormat::DXT1>,
424 // formats are not supported 442 MortonCopy<false, PixelFormat::DXT23>,
425 nullptr, 443 MortonCopy<false, PixelFormat::DXT45>,
426 nullptr, 444 MortonCopy<false, PixelFormat::DXN1>,
427 nullptr, 445 MortonCopy<false, PixelFormat::DXN2UNORM>,
428 nullptr, 446 MortonCopy<false, PixelFormat::DXN2SNORM>,
429 nullptr, 447 MortonCopy<false, PixelFormat::BC7U>,
430 nullptr, 448 MortonCopy<false, PixelFormat::BC6H_UF16>,
431 nullptr, 449 MortonCopy<false, PixelFormat::BC6H_SF16>,
432 nullptr, 450 // TODO(Subv): Swizzling ASTC formats are not supported
433 nullptr,
434 nullptr, 451 nullptr,
435 MortonCopy<false, PixelFormat::G8R8U>, 452 MortonCopy<false, PixelFormat::G8R8U>,
436 MortonCopy<false, PixelFormat::G8R8S>, 453 MortonCopy<false, PixelFormat::G8R8S>,
@@ -455,6 +472,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
455 MortonCopy<false, PixelFormat::RG32UI>, 472 MortonCopy<false, PixelFormat::RG32UI>,
456 MortonCopy<false, PixelFormat::R32UI>, 473 MortonCopy<false, PixelFormat::R32UI>,
457 nullptr, 474 nullptr,
475 nullptr,
476 nullptr,
458 MortonCopy<false, PixelFormat::Z32F>, 477 MortonCopy<false, PixelFormat::Z32F>,
459 MortonCopy<false, PixelFormat::Z16>, 478 MortonCopy<false, PixelFormat::Z16>,
460 MortonCopy<false, PixelFormat::Z24S8>, 479 MortonCopy<false, PixelFormat::Z24S8>,
@@ -614,22 +633,21 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
614 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type); 633 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
615 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type); 634 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
616 635
617 std::size_t buffer_size = 636 std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
618 std::max(src_params.size_in_bytes_total, dst_params.size_in_bytes_total);
619 637
620 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); 638 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
621 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); 639 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
622 if (source_format.compressed) { 640 if (source_format.compressed) {
623 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment, 641 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment,
624 static_cast<GLsizei>(src_params.size_in_bytes_total), nullptr); 642 static_cast<GLsizei>(src_params.size_in_bytes), nullptr);
625 } else { 643 } else {
626 glGetTextureImage(src_surface->Texture().handle, src_attachment, source_format.format, 644 glGetTextureImage(src_surface->Texture().handle, src_attachment, source_format.format,
627 source_format.type, static_cast<GLsizei>(src_params.size_in_bytes_total), 645 source_format.type, static_cast<GLsizei>(src_params.size_in_bytes),
628 nullptr); 646 nullptr);
629 } 647 }
630 // If the new texture is bigger than the previous one, we need to fill in the rest with data 648 // If the new texture is bigger than the previous one, we need to fill in the rest with data
631 // from the CPU. 649 // from the CPU.
632 if (src_params.size_in_bytes_total < dst_params.size_in_bytes_total) { 650 if (src_params.size_in_bytes < dst_params.size_in_bytes) {
633 // Upload the rest of the memory. 651 // Upload the rest of the memory.
634 if (dst_params.is_tiled) { 652 if (dst_params.is_tiled) {
635 // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest 653 // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest
@@ -639,12 +657,12 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
639 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " 657 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
640 "reinterpretation but the texture is tiled."); 658 "reinterpretation but the texture is tiled.");
641 } 659 }
642 std::size_t remaining_size = 660 std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
643 dst_params.size_in_bytes_total - src_params.size_in_bytes_total;
644 std::vector<u8> data(remaining_size); 661 std::vector<u8> data(remaining_size);
645 Memory::ReadBlock(dst_params.addr + src_params.size_in_bytes_total, data.data(), 662 std::memcpy(data.data(), Memory::GetPointer(dst_params.addr + src_params.size_in_bytes),
646 data.size()); 663 data.size());
647 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes_total, remaining_size, 664
665 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
648 data.data()); 666 data.data());
649 } 667 }
650 668
@@ -690,7 +708,8 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
690} 708}
691 709
692CachedSurface::CachedSurface(const SurfaceParams& params) 710CachedSurface::CachedSurface(const SurfaceParams& params)
693 : params(params), gl_target(SurfaceTargetToGL(params.target)) { 711 : params(params), gl_target(SurfaceTargetToGL(params.target)),
712 cached_size_in_bytes(params.size_in_bytes) {
694 texture.Create(); 713 texture.Create();
695 const auto& rect{params.GetRect()}; 714 const auto& rect{params.GetRect()};
696 715
@@ -740,9 +759,21 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
740 759
741 VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, 760 VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr,
742 SurfaceParams::SurfaceTargetName(params.target)); 761 SurfaceParams::SurfaceTargetName(params.target));
762
763 // Clamp size to mapped GPU memory region
764 // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000
765 // R32F render buffer. We do not yet know if this is a game bug or something else, but this
766 // check is necessary to prevent flushing from overwriting unmapped memory.
767
768 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
769 const u64 max_size{memory_manager.GetRegionEnd(params.gpu_addr) - params.gpu_addr};
770 if (cached_size_in_bytes > max_size) {
771 LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size);
772 cached_size_in_bytes = max_size;
773 }
743} 774}
744 775
745static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { 776static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bool reverse) {
746 union S8Z24 { 777 union S8Z24 {
747 BitField<0, 24, u32> z24; 778 BitField<0, 24, u32> z24;
748 BitField<24, 8, u32> s8; 779 BitField<24, 8, u32> s8;
@@ -755,22 +786,29 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
755 }; 786 };
756 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size"); 787 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size");
757 788
758 S8Z24 input_pixel{}; 789 S8Z24 s8z24_pixel{};
759 Z24S8 output_pixel{}; 790 Z24S8 z24s8_pixel{};
760 constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)}; 791 constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::S8Z24)};
761 for (std::size_t y = 0; y < height; ++y) { 792 for (std::size_t y = 0; y < height; ++y) {
762 for (std::size_t x = 0; x < width; ++x) { 793 for (std::size_t x = 0; x < width; ++x) {
763 const std::size_t offset{bpp * (y * width + x)}; 794 const std::size_t offset{bpp * (y * width + x)};
764 std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24)); 795 if (reverse) {
765 output_pixel.s8.Assign(input_pixel.s8); 796 std::memcpy(&z24s8_pixel, &data[offset], sizeof(Z24S8));
766 output_pixel.z24.Assign(input_pixel.z24); 797 s8z24_pixel.s8.Assign(z24s8_pixel.s8);
767 std::memcpy(&data[offset], &output_pixel, sizeof(Z24S8)); 798 s8z24_pixel.z24.Assign(z24s8_pixel.z24);
799 std::memcpy(&data[offset], &s8z24_pixel, sizeof(S8Z24));
800 } else {
801 std::memcpy(&s8z24_pixel, &data[offset], sizeof(S8Z24));
802 z24s8_pixel.s8.Assign(s8z24_pixel.s8);
803 z24s8_pixel.z24.Assign(s8z24_pixel.z24);
804 std::memcpy(&data[offset], &z24s8_pixel, sizeof(Z24S8));
805 }
768 } 806 }
769 } 807 }
770} 808}
771 809
772static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { 810static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
773 constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)}; 811 constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::G8R8U)};
774 for (std::size_t y = 0; y < height; ++y) { 812 for (std::size_t y = 0; y < height; ++y) {
775 for (std::size_t x = 0; x < width; ++x) { 813 for (std::size_t x = 0; x < width; ++x) {
776 const std::size_t offset{bpp * (y * width + x)}; 814 const std::size_t offset{bpp * (y * width + x)};
@@ -790,7 +828,9 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
790 u32 width, u32 height) { 828 u32 width, u32 height) {
791 switch (pixel_format) { 829 switch (pixel_format) {
792 case PixelFormat::ASTC_2D_4X4: 830 case PixelFormat::ASTC_2D_4X4:
793 case PixelFormat::ASTC_2D_8X8: { 831 case PixelFormat::ASTC_2D_8X8:
832 case PixelFormat::ASTC_2D_8X5:
833 case PixelFormat::ASTC_2D_5X4: {
794 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. 834 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
795 u32 block_width{}; 835 u32 block_width{};
796 u32 block_height{}; 836 u32 block_height{};
@@ -800,7 +840,7 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
800 } 840 }
801 case PixelFormat::S8Z24: 841 case PixelFormat::S8Z24:
802 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24. 842 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
803 ConvertS8Z24ToZ24S8(data, width, height); 843 ConvertS8Z24ToZ24S8(data, width, height, false);
804 break; 844 break;
805 845
806 case PixelFormat::G8R8U: 846 case PixelFormat::G8R8U:
@@ -811,54 +851,54 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
811 } 851 }
812} 852}
813 853
854/**
855 * Helper function to perform software conversion (as needed) when flushing a buffer from OpenGL to
856 * Switch memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or
857 * with typical desktop GPUs.
858 */
859static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
860 u32 width, u32 height) {
861 switch (pixel_format) {
862 case PixelFormat::G8R8U:
863 case PixelFormat::G8R8S:
864 case PixelFormat::ASTC_2D_4X4:
865 case PixelFormat::ASTC_2D_8X8: {
866 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
867 static_cast<u32>(pixel_format));
868 UNREACHABLE();
869 break;
870 }
871 case PixelFormat::S8Z24:
872 // Convert the Z24S8 depth format to S8Z24, as OpenGL does not support S8Z24.
873 ConvertS8Z24ToZ24S8(data, width, height, true);
874 break;
875 }
876}
877
814MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); 878MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));
815void CachedSurface::LoadGLBuffer() { 879void CachedSurface::LoadGLBuffer() {
816 ASSERT(params.type != SurfaceType::Fill);
817
818 const u8* const texture_src_data = Memory::GetPointer(params.addr);
819
820 ASSERT(texture_src_data);
821
822 const u32 bytes_per_pixel = GetGLBytesPerPixel(params.pixel_format);
823 const u32 copy_size = params.width * params.height * bytes_per_pixel;
824 const std::size_t total_size = copy_size * params.depth;
825
826 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); 880 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
827 881
882 gl_buffer.resize(params.size_in_bytes_gl);
828 if (params.is_tiled) { 883 if (params.is_tiled) {
829 gl_buffer.resize(total_size); 884 u32 depth = params.depth;
885 u32 block_depth = params.block_depth;
830 886
831 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", 887 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
832 params.block_width, static_cast<u32>(params.target)); 888 params.block_width, static_cast<u32>(params.target));
833 ASSERT_MSG(params.block_depth == 1, "Block depth is defined as {} on texture type {}",
834 params.block_depth, static_cast<u32>(params.target));
835 889
836 // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do 890 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
837 // this for 3D textures, etc. 891 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
838 switch (params.target) { 892 depth = 1U;
839 case SurfaceParams::SurfaceTarget::Texture2D: 893 block_depth = 1U;
840 // Pass impl. to the fallback code below
841 break;
842 case SurfaceParams::SurfaceTarget::Texture2DArray:
843 case SurfaceParams::SurfaceTarget::TextureCubemap:
844 for (std::size_t index = 0; index < params.depth; ++index) {
845 const std::size_t offset{index * copy_size};
846 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
847 params.width, params.block_height, params.height, gl_buffer.data() + offset,
848 copy_size, params.addr + offset);
849 }
850 break;
851 default:
852 LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}",
853 static_cast<u32>(params.target));
854 UNREACHABLE();
855 } 894 }
856 895
857 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( 896 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
858 params.width, params.block_height, params.height, gl_buffer.data(), copy_size, 897 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
859 params.addr); 898 gl_buffer.size(), params.addr);
860 } else { 899 } else {
861 const u8* const texture_src_data_end{texture_src_data + total_size}; 900 const auto texture_src_data{Memory::GetPointer(params.addr)};
901 const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl};
862 gl_buffer.assign(texture_src_data, texture_src_data_end); 902 gl_buffer.assign(texture_src_data, texture_src_data_end);
863 } 903 }
864 904
@@ -867,7 +907,44 @@ void CachedSurface::LoadGLBuffer() {
867 907
868MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); 908MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
869void CachedSurface::FlushGLBuffer() { 909void CachedSurface::FlushGLBuffer() {
870 ASSERT_MSG(false, "Unimplemented"); 910 MICROPROFILE_SCOPE(OpenGL_SurfaceFlush);
911
912 ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented");
913
914 // OpenGL temporary buffer needs to be big enough to store raw texture size
915 gl_buffer.resize(GetSizeInBytes());
916
917 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
918 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
919 ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0);
920 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
921 ASSERT(!tuple.compressed);
922 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
923 glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, gl_buffer.size(),
924 gl_buffer.data());
925 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
926 ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width,
927 params.height);
928 ASSERT(params.type != SurfaceType::Fill);
929 const u8* const texture_src_data = Memory::GetPointer(params.addr);
930 ASSERT(texture_src_data);
931 if (params.is_tiled) {
932 u32 depth = params.depth;
933 u32 block_depth = params.block_depth;
934
935 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
936 params.block_width, static_cast<u32>(params.target));
937
938 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
939 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
940 depth = 1U;
941 }
942 gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
943 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
944 gl_buffer.size(), GetAddr());
945 } else {
946 std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes());
947 }
871} 948}
872 949
873MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); 950MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
@@ -877,9 +954,6 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
877 954
878 MICROPROFILE_SCOPE(OpenGL_TextureUL); 955 MICROPROFILE_SCOPE(OpenGL_TextureUL);
879 956
880 ASSERT(gl_buffer.size() == static_cast<std::size_t>(params.width) * params.height *
881 GetGLBytesPerPixel(params.pixel_format) * params.depth);
882
883 const auto& rect{params.GetRect()}; 957 const auto& rect{params.GetRect()};
884 958
885 // Load data from memory to the surface 959 // Load data from memory to the surface
@@ -888,7 +962,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
888 std::size_t buffer_offset = 962 std::size_t buffer_offset =
889 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width + 963 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width +
890 static_cast<std::size_t>(x0)) * 964 static_cast<std::size_t>(x0)) *
891 GetGLBytesPerPixel(params.pixel_format); 965 SurfaceParams::GetBytesPerPixel(params.pixel_format);
892 966
893 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); 967 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
894 const GLuint target_tex = texture.handle; 968 const GLuint target_tex = texture.handle;
@@ -904,7 +978,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
904 cur_state.Apply(); 978 cur_state.Apply();
905 979
906 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT 980 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
907 ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0); 981 ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0);
908 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width)); 982 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width));
909 983
910 glActiveTexture(GL_TEXTURE0); 984 glActiveTexture(GL_TEXTURE0);
@@ -914,7 +988,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
914 glCompressedTexImage2D( 988 glCompressedTexImage2D(
915 SurfaceTargetToGL(params.target), 0, tuple.internal_format, 989 SurfaceTargetToGL(params.target), 0, tuple.internal_format,
916 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0, 990 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0,
917 static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]); 991 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
918 break; 992 break;
919 case SurfaceParams::SurfaceTarget::Texture3D: 993 case SurfaceParams::SurfaceTarget::Texture3D:
920 case SurfaceParams::SurfaceTarget::Texture2DArray: 994 case SurfaceParams::SurfaceTarget::Texture2DArray:
@@ -922,16 +996,16 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
922 SurfaceTargetToGL(params.target), 0, tuple.internal_format, 996 SurfaceTargetToGL(params.target), 0, tuple.internal_format,
923 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 997 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height),
924 static_cast<GLsizei>(params.depth), 0, 998 static_cast<GLsizei>(params.depth), 0,
925 static_cast<GLsizei>(params.size_in_bytes_total), &gl_buffer[buffer_offset]); 999 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
926 break; 1000 break;
927 case SurfaceParams::SurfaceTarget::TextureCubemap: 1001 case SurfaceParams::SurfaceTarget::TextureCubemap:
928 for (std::size_t face = 0; face < params.depth; ++face) { 1002 for (std::size_t face = 0; face < params.depth; ++face) {
929 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), 1003 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face),
930 0, tuple.internal_format, static_cast<GLsizei>(params.width), 1004 0, tuple.internal_format, static_cast<GLsizei>(params.width),
931 static_cast<GLsizei>(params.height), 0, 1005 static_cast<GLsizei>(params.height), 0,
932 static_cast<GLsizei>(params.size_in_bytes_2d), 1006 static_cast<GLsizei>(params.SizeInBytesCubeFaceGL()),
933 &gl_buffer[buffer_offset]); 1007 &gl_buffer[buffer_offset]);
934 buffer_offset += params.size_in_bytes_2d; 1008 buffer_offset += params.SizeInBytesCubeFace();
935 } 1009 }
936 break; 1010 break;
937 default: 1011 default:
@@ -941,7 +1015,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
941 glCompressedTexImage2D( 1015 glCompressedTexImage2D(
942 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), 1016 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width),
943 static_cast<GLsizei>(params.height), 0, 1017 static_cast<GLsizei>(params.height), 0,
944 static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]); 1018 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
945 } 1019 }
946 } else { 1020 } else {
947 1021
@@ -970,7 +1044,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
970 y0, static_cast<GLsizei>(rect.GetWidth()), 1044 y0, static_cast<GLsizei>(rect.GetWidth()),
971 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, 1045 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
972 &gl_buffer[buffer_offset]); 1046 &gl_buffer[buffer_offset]);
973 buffer_offset += params.size_in_bytes_2d; 1047 buffer_offset += params.SizeInBytesCubeFace();
974 } 1048 }
975 break; 1049 break;
976 default: 1050 default:
@@ -1032,10 +1106,7 @@ Surface RasterizerCacheOpenGL::GetColorBufferSurface(std::size_t index, bool pre
1032void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { 1106void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
1033 surface->LoadGLBuffer(); 1107 surface->LoadGLBuffer();
1034 surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle); 1108 surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
1035} 1109 surface->MarkAsModified(false, *this);
1036
1037void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) {
1038 surface->FlushGLBuffer();
1039} 1110}
1040 1111
1041Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) { 1112Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) {
@@ -1052,8 +1123,8 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
1052 } else if (preserve_contents) { 1123 } else if (preserve_contents) {
1053 // If surface parameters changed and we care about keeping the previous data, recreate 1124 // If surface parameters changed and we care about keeping the previous data, recreate
1054 // the surface from the old one 1125 // the surface from the old one
1055 Unregister(surface);
1056 Surface new_surface{RecreateSurface(surface, params)}; 1126 Surface new_surface{RecreateSurface(surface, params)};
1127 Unregister(surface);
1057 Register(new_surface); 1128 Register(new_surface);
1058 return new_surface; 1129 return new_surface;
1059 } else { 1130 } else {
@@ -1104,6 +1175,14 @@ void RasterizerCacheOpenGL::FermiCopySurface(
1104 FastCopySurface(GetSurface(src_params, true), GetSurface(dst_params, false)); 1175 FastCopySurface(GetSurface(src_params, true), GetSurface(dst_params, false));
1105} 1176}
1106 1177
1178void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
1179 const Surface& dst_surface) {
1180 const auto& src_params{src_surface->GetSurfaceParams()};
1181 const auto& dst_params{dst_surface->GetSurfaceParams()};
1182 FlushRegion(src_params.addr, dst_params.size_in_bytes);
1183 LoadSurface(dst_surface);
1184}
1185
1107Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, 1186Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1108 const SurfaceParams& new_params) { 1187 const SurfaceParams& new_params) {
1109 // Verify surface is compatible for blitting 1188 // Verify surface is compatible for blitting
@@ -1112,6 +1191,12 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1112 // Get a new surface with the new parameters, and blit the previous surface to it 1191 // Get a new surface with the new parameters, and blit the previous surface to it
1113 Surface new_surface{GetUncachedSurface(new_params)}; 1192 Surface new_surface{GetUncachedSurface(new_params)};
1114 1193
1194 // With use_accurate_gpu_emulation enabled, do an accurate surface copy
1195 if (Settings::values.use_accurate_gpu_emulation) {
1196 AccurateCopySurface(old_surface, new_surface);
1197 return new_surface;
1198 }
1199
1115 // For compatible surfaces, we can just do fast glCopyImageSubData based copy 1200 // For compatible surfaces, we can just do fast glCopyImageSubData based copy
1116 if (old_params.target == new_params.target && old_params.type == new_params.type && 1201 if (old_params.target == new_params.target && old_params.type == new_params.type &&
1117 old_params.depth == new_params.depth && old_params.depth == 1 && 1202 old_params.depth == new_params.depth && old_params.depth == 1 &&
@@ -1123,11 +1208,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1123 1208
1124 // If the format is the same, just do a framebuffer blit. This is significantly faster than 1209 // If the format is the same, just do a framebuffer blit. This is significantly faster than
1125 // using PBOs. The is also likely less accurate, as textures will be converted rather than 1210 // using PBOs. The is also likely less accurate, as textures will be converted rather than
1126 // reinterpreted. When use_accurate_framebuffers setting is enabled, perform a more accurate 1211 // reinterpreted. When use_accurate_gpu_emulation setting is enabled, perform a more accurate
1127 // surface copy, where pixels are reinterpreted as a new format (without conversion). This 1212 // surface copy, where pixels are reinterpreted as a new format (without conversion). This
1128 // code path uses OpenGL PBOs and is quite slow. 1213 // code path uses OpenGL PBOs and is quite slow.
1129 const bool is_blit{old_params.pixel_format == new_params.pixel_format || 1214 const bool is_blit{old_params.pixel_format == new_params.pixel_format};
1130 !Settings::values.use_accurate_framebuffers};
1131 1215
1132 switch (new_params.target) { 1216 switch (new_params.target) {
1133 case SurfaceParams::SurfaceTarget::Texture2D: 1217 case SurfaceParams::SurfaceTarget::Texture2D:
@@ -1137,6 +1221,9 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1137 CopySurface(old_surface, new_surface, copy_pbo.handle); 1221 CopySurface(old_surface, new_surface, copy_pbo.handle);
1138 } 1222 }
1139 break; 1223 break;
1224 case SurfaceParams::SurfaceTarget::Texture3D:
1225 AccurateCopySurface(old_surface, new_surface);
1226 break;
1140 case SurfaceParams::SurfaceTarget::TextureCubemap: { 1227 case SurfaceParams::SurfaceTarget::TextureCubemap: {
1141 if (old_params.rt.array_mode != 1) { 1228 if (old_params.rt.array_mode != 1) {
1142 // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this 1229 // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 66d98ad4e..0dd0d90a3 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -18,6 +18,7 @@
18#include "video_core/rasterizer_cache.h" 18#include "video_core/rasterizer_cache.h"
19#include "video_core/renderer_opengl/gl_resource_manager.h" 19#include "video_core/renderer_opengl/gl_resource_manager.h"
20#include "video_core/renderer_opengl/gl_shader_gen.h" 20#include "video_core/renderer_opengl/gl_shader_gen.h"
21#include "video_core/textures/decoders.h"
21#include "video_core/textures/texture.h" 22#include "video_core/textures/texture.h"
22 23
23namespace OpenGL { 24namespace OpenGL {
@@ -74,19 +75,21 @@ struct SurfaceParams {
74 RG32UI = 43, 75 RG32UI = 43,
75 R32UI = 44, 76 R32UI = 44,
76 ASTC_2D_8X8 = 45, 77 ASTC_2D_8X8 = 45,
78 ASTC_2D_8X5 = 46,
79 ASTC_2D_5X4 = 47,
77 80
78 MaxColorFormat, 81 MaxColorFormat,
79 82
80 // Depth formats 83 // Depth formats
81 Z32F = 46, 84 Z32F = 48,
82 Z16 = 47, 85 Z16 = 49,
83 86
84 MaxDepthFormat, 87 MaxDepthFormat,
85 88
86 // DepthStencil formats 89 // DepthStencil formats
87 Z24S8 = 48, 90 Z24S8 = 50,
88 S8Z24 = 49, 91 S8Z24 = 51,
89 Z32FS8 = 50, 92 Z32FS8 = 52,
90 93
91 MaxDepthStencilFormat, 94 MaxDepthStencilFormat,
92 95
@@ -129,6 +132,8 @@ struct SurfaceParams {
129 case Tegra::Texture::TextureType::Texture2D: 132 case Tegra::Texture::TextureType::Texture2D:
130 case Tegra::Texture::TextureType::Texture2DNoMipmap: 133 case Tegra::Texture::TextureType::Texture2DNoMipmap:
131 return SurfaceTarget::Texture2D; 134 return SurfaceTarget::Texture2D;
135 case Tegra::Texture::TextureType::Texture3D:
136 return SurfaceTarget::Texture3D;
132 case Tegra::Texture::TextureType::TextureCubemap: 137 case Tegra::Texture::TextureType::TextureCubemap:
133 return SurfaceTarget::TextureCubemap; 138 return SurfaceTarget::TextureCubemap;
134 case Tegra::Texture::TextureType::Texture1DArray: 139 case Tegra::Texture::TextureType::Texture1DArray:
@@ -220,6 +225,8 @@ struct SurfaceParams {
220 1, // RG32UI 225 1, // RG32UI
221 1, // R32UI 226 1, // R32UI
222 4, // ASTC_2D_8X8 227 4, // ASTC_2D_8X8
228 4, // ASTC_2D_8X5
229 4, // ASTC_2D_5X4
223 1, // Z32F 230 1, // Z32F
224 1, // Z16 231 1, // Z16
225 1, // Z24S8 232 1, // Z24S8
@@ -282,6 +289,8 @@ struct SurfaceParams {
282 64, // RG32UI 289 64, // RG32UI
283 32, // R32UI 290 32, // R32UI
284 16, // ASTC_2D_8X8 291 16, // ASTC_2D_8X8
292 32, // ASTC_2D_8X5
293 32, // ASTC_2D_5X4
285 32, // Z32F 294 32, // Z32F
286 16, // Z16 295 16, // Z16
287 32, // Z24S8 296 32, // Z24S8
@@ -553,8 +562,12 @@ struct SurfaceParams {
553 return PixelFormat::BC6H_SF16; 562 return PixelFormat::BC6H_SF16;
554 case Tegra::Texture::TextureFormat::ASTC_2D_4X4: 563 case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
555 return PixelFormat::ASTC_2D_4X4; 564 return PixelFormat::ASTC_2D_4X4;
565 case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
566 return PixelFormat::ASTC_2D_5X4;
556 case Tegra::Texture::TextureFormat::ASTC_2D_8X8: 567 case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
557 return PixelFormat::ASTC_2D_8X8; 568 return PixelFormat::ASTC_2D_8X8;
569 case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
570 return PixelFormat::ASTC_2D_8X5;
558 case Tegra::Texture::TextureFormat::R16_G16: 571 case Tegra::Texture::TextureFormat::R16_G16:
559 switch (component_type) { 572 switch (component_type) {
560 case Tegra::Texture::ComponentType::FLOAT: 573 case Tegra::Texture::ComponentType::FLOAT:
@@ -691,21 +704,42 @@ struct SurfaceParams {
691 return SurfaceType::Invalid; 704 return SurfaceType::Invalid;
692 } 705 }
693 706
707 /// Returns the sizer in bytes of the specified pixel format
708 static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) {
709 if (pixel_format == SurfaceParams::PixelFormat::Invalid) {
710 return 0;
711 }
712 return GetFormatBpp(pixel_format) / CHAR_BIT;
713 }
714
694 /// Returns the rectangle corresponding to this surface 715 /// Returns the rectangle corresponding to this surface
695 MathUtil::Rectangle<u32> GetRect() const; 716 MathUtil::Rectangle<u32> GetRect() const;
696 717
697 /// Returns the size of this surface as a 2D texture in bytes, adjusted for compression 718 /// Returns the total size of this surface in bytes, adjusted for compression
698 std::size_t SizeInBytes2D() const { 719 std::size_t SizeInBytesRaw(bool ignore_tiled = false) const {
699 const u32 compression_factor{GetCompressionFactor(pixel_format)}; 720 const u32 compression_factor{GetCompressionFactor(pixel_format)};
700 ASSERT(width % compression_factor == 0); 721 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
701 ASSERT(height % compression_factor == 0); 722 const size_t uncompressed_size{
702 return (width / compression_factor) * (height / compression_factor) * 723 Tegra::Texture::CalculateSize((ignore_tiled ? false : is_tiled), bytes_per_pixel, width,
703 GetFormatBpp(pixel_format) / CHAR_BIT; 724 height, depth, block_height, block_depth)};
725
726 // Divide by compression_factor^2, as height and width are factored by this
727 return uncompressed_size / (compression_factor * compression_factor);
704 } 728 }
705 729
706 /// Returns the total size of this surface in bytes, adjusted for compression 730 /// Returns the size of this surface as an OpenGL texture in bytes
707 std::size_t SizeInBytesTotal() const { 731 std::size_t SizeInBytesGL() const {
708 return SizeInBytes2D() * depth; 732 return SizeInBytesRaw(true);
733 }
734
735 /// Returns the size of this surface as a cube face in bytes
736 std::size_t SizeInBytesCubeFace() const {
737 return size_in_bytes / 6;
738 }
739
740 /// Returns the size of this surface as an OpenGL cube face in bytes
741 std::size_t SizeInBytesCubeFaceGL() const {
742 return size_in_bytes_gl / 6;
709 } 743 }
710 744
711 /// Creates SurfaceParams from a texture configuration 745 /// Creates SurfaceParams from a texture configuration
@@ -732,7 +766,9 @@ struct SurfaceParams {
732 other.depth); 766 other.depth);
733 } 767 }
734 768
735 VAddr addr; 769 /// Initializes parameters for caching, should be called after everything has been initialized
770 void InitCacheParameters(Tegra::GPUVAddr gpu_addr);
771
736 bool is_tiled; 772 bool is_tiled;
737 u32 block_width; 773 u32 block_width;
738 u32 block_height; 774 u32 block_height;
@@ -744,15 +780,20 @@ struct SurfaceParams {
744 u32 height; 780 u32 height;
745 u32 depth; 781 u32 depth;
746 u32 unaligned_height; 782 u32 unaligned_height;
747 std::size_t size_in_bytes_total;
748 std::size_t size_in_bytes_2d;
749 SurfaceTarget target; 783 SurfaceTarget target;
750 u32 max_mip_level; 784 u32 max_mip_level;
751 785
786 // Parameters used for caching
787 VAddr addr;
788 Tegra::GPUVAddr gpu_addr;
789 std::size_t size_in_bytes;
790 std::size_t size_in_bytes_gl;
791
752 // Render target specific parameters, not used in caching 792 // Render target specific parameters, not used in caching
753 struct { 793 struct {
754 u32 index; 794 u32 index;
755 u32 array_mode; 795 u32 array_mode;
796 u32 volume;
756 u32 layer_stride; 797 u32 layer_stride;
757 u32 base_layer; 798 u32 base_layer;
758 } rt; 799 } rt;
@@ -765,7 +806,8 @@ struct SurfaceReserveKey : Common::HashableStruct<OpenGL::SurfaceParams> {
765 static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) { 806 static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) {
766 SurfaceReserveKey res; 807 SurfaceReserveKey res;
767 res.state = params; 808 res.state = params;
768 res.state.rt = {}; // Ignore rt config in caching 809 res.state.gpu_addr = {}; // Ignore GPU vaddr in caching
810 res.state.rt = {}; // Ignore rt config in caching
769 return res; 811 return res;
770 } 812 }
771}; 813};
@@ -780,16 +822,20 @@ struct hash<SurfaceReserveKey> {
780 822
781namespace OpenGL { 823namespace OpenGL {
782 824
783class CachedSurface final { 825class CachedSurface final : public RasterizerCacheObject {
784public: 826public:
785 CachedSurface(const SurfaceParams& params); 827 CachedSurface(const SurfaceParams& params);
786 828
787 VAddr GetAddr() const { 829 VAddr GetAddr() const override {
788 return params.addr; 830 return params.addr;
789 } 831 }
790 832
791 std::size_t GetSizeInBytes() const { 833 std::size_t GetSizeInBytes() const override {
792 return params.size_in_bytes_total; 834 return cached_size_in_bytes;
835 }
836
837 void Flush() override {
838 FlushGLBuffer();
793 } 839 }
794 840
795 const OGLTexture& Texture() const { 841 const OGLTexture& Texture() const {
@@ -800,13 +846,6 @@ public:
800 return gl_target; 846 return gl_target;
801 } 847 }
802 848
803 static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) {
804 if (format == SurfaceParams::PixelFormat::Invalid)
805 return 0;
806
807 return SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
808 }
809
810 const SurfaceParams& GetSurfaceParams() const { 849 const SurfaceParams& GetSurfaceParams() const {
811 return params; 850 return params;
812 } 851 }
@@ -823,6 +862,7 @@ private:
823 std::vector<u8> gl_buffer; 862 std::vector<u8> gl_buffer;
824 SurfaceParams params; 863 SurfaceParams params;
825 GLenum gl_target; 864 GLenum gl_target;
865 std::size_t cached_size_in_bytes;
826}; 866};
827 867
828class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { 868class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
@@ -839,9 +879,6 @@ public:
839 /// Get the color surface based on the framebuffer configuration and the specified render target 879 /// Get the color surface based on the framebuffer configuration and the specified render target
840 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents); 880 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents);
841 881
842 /// Flushes the surface to Switch memory
843 void FlushSurface(const Surface& surface);
844
845 /// Tries to find a framebuffer using on the provided CPU address 882 /// Tries to find a framebuffer using on the provided CPU address
846 Surface TryFindFramebufferSurface(VAddr addr) const; 883 Surface TryFindFramebufferSurface(VAddr addr) const;
847 884
@@ -865,6 +902,9 @@ private:
865 /// Tries to get a reserved surface for the specified parameters 902 /// Tries to get a reserved surface for the specified parameters
866 Surface TryGetReservedSurface(const SurfaceParams& params); 903 Surface TryGetReservedSurface(const SurfaceParams& params);
867 904
905 /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data
906 void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface);
907
868 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have 908 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
869 /// previously been used. This is to prevent surfaces from being constantly created and 909 /// previously been used. This is to prevent surfaces from being constantly created and
870 /// destroyed when used with different surface parameters. 910 /// destroyed when used with different surface parameters.
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 7bb287f56..a210f1731 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -19,20 +19,21 @@ class CachedShader;
19using Shader = std::shared_ptr<CachedShader>; 19using Shader = std::shared_ptr<CachedShader>;
20using Maxwell = Tegra::Engines::Maxwell3D::Regs; 20using Maxwell = Tegra::Engines::Maxwell3D::Regs;
21 21
22class CachedShader final { 22class CachedShader final : public RasterizerCacheObject {
23public: 23public:
24 CachedShader(VAddr addr, Maxwell::ShaderProgram program_type); 24 CachedShader(VAddr addr, Maxwell::ShaderProgram program_type);
25 25
26 /// Gets the address of the shader in guest memory, required for cache management 26 VAddr GetAddr() const override {
27 VAddr GetAddr() const {
28 return addr; 27 return addr;
29 } 28 }
30 29
31 /// Gets the size of the shader in guest memory, required for cache management 30 std::size_t GetSizeInBytes() const override {
32 std::size_t GetSizeInBytes() const {
33 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); 31 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64);
34 } 32 }
35 33
34 // We do not have to flush this cache as things in it are never modified by us.
35 void Flush() override {}
36
36 /// Gets the shader entries for the shader 37 /// Gets the shader entries for the shader
37 const GLShader::ShaderEntries& GetShaderEntries() const { 38 const GLShader::ShaderEntries& GetShaderEntries() const {
38 return entries; 39 return entries;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 23349b1a1..e050b063a 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1233,6 +1233,7 @@ private:
1233 case Tegra::Shader::TextureType::Texture2D: { 1233 case Tegra::Shader::TextureType::Texture2D: {
1234 return 2; 1234 return 2;
1235 } 1235 }
1236 case Tegra::Shader::TextureType::Texture3D:
1236 case Tegra::Shader::TextureType::TextureCube: { 1237 case Tegra::Shader::TextureType::TextureCube: {
1237 return 3; 1238 return 3;
1238 } 1239 }
@@ -1527,7 +1528,6 @@ private:
1527 1528
1528 break; 1529 break;
1529 } 1530 }
1530
1531 case OpCode::Type::Shift: { 1531 case OpCode::Type::Shift: {
1532 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); 1532 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);
1533 std::string op_b; 1533 std::string op_b;
@@ -1569,7 +1569,6 @@ private:
1569 } 1569 }
1570 break; 1570 break;
1571 } 1571 }
1572
1573 case OpCode::Type::ArithmeticIntegerImmediate: { 1572 case OpCode::Type::ArithmeticIntegerImmediate: {
1574 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); 1573 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
1575 std::string op_b = std::to_string(instr.alu.imm20_32.Value()); 1574 std::string op_b = std::to_string(instr.alu.imm20_32.Value());
@@ -2262,9 +2261,9 @@ private:
2262 break; 2261 break;
2263 } 2262 }
2264 case OpCode::Id::TEX: { 2263 case OpCode::Id::TEX: {
2265 ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented");
2266 Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; 2264 Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
2267 std::string coord; 2265 std::string coord;
2266 const bool is_array = instr.tex.array != 0;
2268 2267
2269 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2268 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2270 "NODEP is not implemented"); 2269 "NODEP is not implemented");
@@ -2279,21 +2278,59 @@ private:
2279 2278
2280 switch (num_coordinates) { 2279 switch (num_coordinates) {
2281 case 1: { 2280 case 1: {
2282 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2281 if (is_array) {
2283 coord = "float coords = " + x + ';'; 2282 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2283 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2284 coord = "vec2 coords = vec2(" + x + ", " + index + ");";
2285 } else {
2286 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2287 coord = "float coords = " + x + ';';
2288 }
2284 break; 2289 break;
2285 } 2290 }
2286 case 2: { 2291 case 2: {
2287 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2292 if (is_array) {
2288 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2293 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2289 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2294 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2295 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2296 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");";
2297 } else {
2298 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2299 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2300 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2301 }
2290 break; 2302 break;
2291 } 2303 }
2292 case 3: { 2304 case 3: {
2293 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2305 if (depth_compare) {
2294 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2306 if (is_array) {
2295 const std::string z = regs.GetRegisterAsFloat(instr.gpr20); 2307 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2296 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; 2308 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2309 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2310 const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2311 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
2312 ");";
2313 } else {
2314 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2315 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2316 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2317 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2318 }
2319 } else {
2320 if (is_array) {
2321 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2322 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2323 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2324 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 3);
2325 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
2326 ");";
2327 } else {
2328 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2329 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2330 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2331 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2332 }
2333 }
2297 break; 2334 break;
2298 } 2335 }
2299 default: 2336 default:
@@ -2312,7 +2349,7 @@ private:
2312 std::string op_c; 2349 std::string op_c;
2313 2350
2314 const std::string sampler = 2351 const std::string sampler =
2315 GetSampler(instr.sampler, texture_type, false, depth_compare); 2352 GetSampler(instr.sampler, texture_type, is_array, depth_compare);
2316 // Add an extra scope and declare the texture coords inside to prevent 2353 // Add an extra scope and declare the texture coords inside to prevent
2317 // overwriting them in case they are used as outputs of the texs instruction. 2354 // overwriting them in case they are used as outputs of the texs instruction.
2318 2355
@@ -2332,10 +2369,13 @@ private:
2332 } 2369 }
2333 case Tegra::Shader::TextureProcessMode::LB: 2370 case Tegra::Shader::TextureProcessMode::LB:
2334 case Tegra::Shader::TextureProcessMode::LBA: { 2371 case Tegra::Shader::TextureProcessMode::LBA: {
2335 if (num_coordinates <= 2) { 2372 if (depth_compare) {
2336 op_c = regs.GetRegisterAsFloat(instr.gpr20); 2373 if (is_array)
2374 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 2);
2375 else
2376 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2337 } else { 2377 } else {
2338 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); 2378 op_c = regs.GetRegisterAsFloat(instr.gpr20);
2339 } 2379 }
2340 // TODO: Figure if A suffix changes the equation at all. 2380 // TODO: Figure if A suffix changes the equation at all.
2341 texture = "texture(" + sampler + ", coords, " + op_c + ')'; 2381 texture = "texture(" + sampler + ", coords, " + op_c + ')';
@@ -2478,6 +2518,8 @@ private:
2478 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), 2518 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ),
2479 "MZ is not implemented"); 2519 "MZ is not implemented");
2480 2520
2521 u32 op_c_offset = 0;
2522
2481 switch (texture_type) { 2523 switch (texture_type) {
2482 case Tegra::Shader::TextureType::Texture1D: { 2524 case Tegra::Shader::TextureType::Texture1D: {
2483 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2525 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
@@ -2492,6 +2534,7 @@ private:
2492 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2534 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
2493 const std::string y = regs.GetRegisterAsInteger(instr.gpr20); 2535 const std::string y = regs.GetRegisterAsInteger(instr.gpr20);
2494 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");"; 2536 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");";
2537 op_c_offset = 1;
2495 } 2538 }
2496 break; 2539 break;
2497 } 2540 }
@@ -2503,13 +2546,14 @@ private:
2503 const std::string sampler = 2546 const std::string sampler =
2504 GetSampler(instr.sampler, texture_type, is_array, false); 2547 GetSampler(instr.sampler, texture_type, is_array, false);
2505 std::string texture = "texelFetch(" + sampler + ", coords, 0)"; 2548 std::string texture = "texelFetch(" + sampler + ", coords, 0)";
2506 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr20.Value() + 1);
2507 switch (instr.tlds.GetTextureProcessMode()) { 2549 switch (instr.tlds.GetTextureProcessMode()) {
2508 case Tegra::Shader::TextureProcessMode::LZ: { 2550 case Tegra::Shader::TextureProcessMode::LZ: {
2509 texture = "texelFetch(" + sampler + ", coords, 0)"; 2551 texture = "texelFetch(" + sampler + ", coords, 0)";
2510 break; 2552 break;
2511 } 2553 }
2512 case Tegra::Shader::TextureProcessMode::LL: { 2554 case Tegra::Shader::TextureProcessMode::LL: {
2555 const std::string op_c =
2556 regs.GetRegisterAsInteger(instr.gpr20.Value() + op_c_offset);
2513 texture = "texelFetch(" + sampler + ", coords, " + op_c + ')'; 2557 texture = "texelFetch(" + sampler + ", coords, " + op_c + ')';
2514 break; 2558 break;
2515 } 2559 }
@@ -2895,14 +2939,14 @@ private:
2895 const std::string pred = 2939 const std::string pred =
2896 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); 2940 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);
2897 const std::string combiner = GetPredicateCombiner(instr.csetp.op); 2941 const std::string combiner = GetPredicateCombiner(instr.csetp.op);
2898 const std::string controlCode = regs.GetControlCode(instr.csetp.cc); 2942 const std::string control_code = regs.GetControlCode(instr.csetp.cc);
2899 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { 2943 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {
2900 SetPredicate(instr.csetp.pred3, 2944 SetPredicate(instr.csetp.pred3,
2901 '(' + controlCode + ") " + combiner + " (" + pred + ')'); 2945 '(' + control_code + ") " + combiner + " (" + pred + ')');
2902 } 2946 }
2903 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { 2947 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
2904 SetPredicate(instr.csetp.pred0, 2948 SetPredicate(instr.csetp.pred0,
2905 "!(" + controlCode + ") " + combiner + " (" + pred + ')'); 2949 "!(" + control_code + ") " + combiner + " (" + pred + ')');
2906 } 2950 }
2907 break; 2951 break;
2908 } 2952 }
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 0d2456b56..f1b40e7f5 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -40,72 +40,146 @@ struct alignas(64) SwizzleTable {
40constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>(); 40constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>();
41constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>(); 41constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>();
42 42
43static void LegacySwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 43/**
44 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, 44 * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
45 u32 block_height) { 45 * Instead of going gob by gob, we map the coordinates inside a block and manage from
46 * those. Block_Width is assumed to be 1.
47 */
48void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
49 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
50 const u32 y_end, const u32 z_end, const u32 tile_offset,
51 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
52 const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
46 std::array<u8*, 2> data_ptrs; 53 std::array<u8*, 2> data_ptrs;
47 const std::size_t stride = width * bytes_per_pixel; 54 u32 z_address = tile_offset;
48 const std::size_t gobs_in_x = 64; 55 const u32 gob_size_x = 64;
49 const std::size_t gobs_in_y = 8; 56 const u32 gob_size_y = 8;
50 const std::size_t gobs_size = gobs_in_x * gobs_in_y; 57 const u32 gob_size_z = 1;
51 const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; 58 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
52 for (std::size_t y = 0; y < height; ++y) { 59 for (u32 z = z_start; z < z_end; z++) {
53 const std::size_t gob_y_address = 60 u32 y_address = z_address;
54 (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + 61 u32 pixel_base = layer_z * z + y_start * stride_x;
55 (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; 62 for (u32 y = y_start; y < y_end; y++) {
56 const auto& table = legacy_swizzle_table[y % gobs_in_y]; 63 const auto& table = legacy_swizzle_table[y % gob_size_y];
57 for (std::size_t x = 0; x < width; ++x) { 64 for (u32 x = x_start; x < x_end; x++) {
58 const std::size_t gob_address = 65 const u32 swizzle_offset{y_address + table[x * bytes_per_pixel % gob_size_x]};
59 gob_y_address + (x * bytes_per_pixel / gobs_in_x) * gobs_size * block_height; 66 const u32 pixel_index{x * out_bytes_per_pixel + pixel_base};
60 const std::size_t x2 = x * bytes_per_pixel; 67 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
61 const std::size_t swizzle_offset = gob_address + table[x2 % gobs_in_x]; 68 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
62 const std::size_t pixel_index = (x + y * width) * out_bytes_per_pixel; 69 std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
63 70 }
64 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 71 pixel_base += stride_x;
65 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 72 if ((y + 1) % gob_size_y == 0)
66 73 y_address += gob_size;
67 std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
68 } 74 }
75 z_address += xy_block_size;
69 } 76 }
70} 77}
71 78
72static void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 79/**
73 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, 80 * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
74 u32 block_height) { 81 * Instead of going gob by gob, we map the coordinates inside a block and manage from
82 * those. Block_Width is assumed to be 1.
83 */
84void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
85 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
86 const u32 y_end, const u32 z_end, const u32 tile_offset,
87 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
88 const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
75 std::array<u8*, 2> data_ptrs; 89 std::array<u8*, 2> data_ptrs;
76 const std::size_t stride{width * bytes_per_pixel}; 90 u32 z_address = tile_offset;
77 const std::size_t gobs_in_x = 64; 91 const u32 x_startb = x_start * bytes_per_pixel;
78 const std::size_t gobs_in_y = 8; 92 const u32 x_endb = x_end * bytes_per_pixel;
79 const std::size_t gobs_size = gobs_in_x * gobs_in_y; 93 const u32 copy_size = 16;
80 const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; 94 const u32 gob_size_x = 64;
81 const std::size_t copy_size{16}; 95 const u32 gob_size_y = 8;
82 for (std::size_t y = 0; y < height; ++y) { 96 const u32 gob_size_z = 1;
83 const std::size_t initial_gob = 97 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
84 (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + 98 for (u32 z = z_start; z < z_end; z++) {
85 (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; 99 u32 y_address = z_address;
86 const std::size_t pixel_base{y * width * out_bytes_per_pixel}; 100 u32 pixel_base = layer_z * z + y_start * stride_x;
87 const auto& table = fast_swizzle_table[y % gobs_in_y]; 101 for (u32 y = y_start; y < y_end; y++) {
88 for (std::size_t xb = 0; xb < stride; xb += copy_size) { 102 const auto& table = fast_swizzle_table[y % gob_size_y];
89 const std::size_t gob_address{initial_gob + 103 for (u32 xb = x_startb; xb < x_endb; xb += copy_size) {
90 (xb / gobs_in_x) * gobs_size * block_height}; 104 const u32 swizzle_offset{y_address + table[(xb / copy_size) % 4]};
91 const std::size_t swizzle_offset{gob_address + table[(xb / 16) % 4]}; 105 const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
92 const std::size_t out_x = xb * out_bytes_per_pixel / bytes_per_pixel; 106 const u32 pixel_index{out_x + pixel_base};
93 const std::size_t pixel_index{out_x + pixel_base}; 107 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
94 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 108 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
95 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 109 std::memcpy(data_ptrs[0], data_ptrs[1], copy_size);
96 std::memcpy(data_ptrs[0], data_ptrs[1], copy_size); 110 }
111 pixel_base += stride_x;
112 if ((y + 1) % gob_size_y == 0)
113 y_address += gob_size;
97 } 114 }
115 z_address += xy_block_size;
98 } 116 }
99} 117}
100 118
101void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 119/**
102 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height) { 120 * This function unswizzles or swizzles a texture by mapping Linear to BlockLinear Textue.
121 * The body of this function takes care of splitting the swizzled texture into blocks,
122 * and managing the extents of it. Once all the parameters of a single block are obtained,
123 * the function calls 'ProcessBlock' to process that particular Block.
124 *
125 * Documentation for the memory layout and decoding can be found at:
126 * https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces
127 */
128template <bool fast>
129void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, const u32 width,
130 const u32 height, const u32 depth, const u32 bytes_per_pixel,
131 const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) {
132 auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
133 const u32 stride_x = width * out_bytes_per_pixel;
134 const u32 layer_z = height * stride_x;
135 const u32 gob_x_bytes = 64;
136 const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel;
137 const u32 gob_elements_y = 8;
138 const u32 gob_elements_z = 1;
139 const u32 block_x_elements = gob_elements_x;
140 const u32 block_y_elements = gob_elements_y * block_height;
141 const u32 block_z_elements = gob_elements_z * block_depth;
142 const u32 blocks_on_x = div_ceil(width, block_x_elements);
143 const u32 blocks_on_y = div_ceil(height, block_y_elements);
144 const u32 blocks_on_z = div_ceil(depth, block_z_elements);
145 const u32 blocks = blocks_on_x * blocks_on_y * blocks_on_z;
146 const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
147 const u32 xy_block_size = gob_size * block_height;
148 const u32 block_size = xy_block_size * block_depth;
149 u32 tile_offset = 0;
150 for (u32 zb = 0; zb < blocks_on_z; zb++) {
151 const u32 z_start = zb * block_z_elements;
152 const u32 z_end = std::min(depth, z_start + block_z_elements);
153 for (u32 yb = 0; yb < blocks_on_y; yb++) {
154 const u32 y_start = yb * block_y_elements;
155 const u32 y_end = std::min(height, y_start + block_y_elements);
156 for (u32 xb = 0; xb < blocks_on_x; xb++) {
157 const u32 x_start = xb * block_x_elements;
158 const u32 x_end = std::min(width, x_start + block_x_elements);
159 if (fast) {
160 FastProcessBlock(swizzled_data, unswizzled_data, unswizzle, x_start, y_start,
161 z_start, x_end, y_end, z_end, tile_offset, xy_block_size,
162 layer_z, stride_x, bytes_per_pixel, out_bytes_per_pixel);
163 } else {
164 PreciseProcessBlock(swizzled_data, unswizzled_data, unswizzle, x_start, y_start,
165 z_start, x_end, y_end, z_end, tile_offset, xy_block_size,
166 layer_z, stride_x, bytes_per_pixel, out_bytes_per_pixel);
167 }
168 tile_offset += block_size;
169 }
170 }
171 }
172}
173
174void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
175 u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
176 bool unswizzle, u32 block_height, u32 block_depth) {
103 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { 177 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
104 FastSwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, 178 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
105 unswizzled_data, unswizzle, block_height); 179 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
106 } else { 180 } else {
107 LegacySwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, 181 SwizzledData<false>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
108 unswizzled_data, unswizzle, block_height); 182 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
109 } 183 }
110} 184}
111 185
@@ -126,7 +200,9 @@ u32 BytesPerPixel(TextureFormat format) {
126 case TextureFormat::R32_G32_B32: 200 case TextureFormat::R32_G32_B32:
127 return 12; 201 return 12;
128 case TextureFormat::ASTC_2D_4X4: 202 case TextureFormat::ASTC_2D_4X4:
203 case TextureFormat::ASTC_2D_5X4:
129 case TextureFormat::ASTC_2D_8X8: 204 case TextureFormat::ASTC_2D_8X8:
205 case TextureFormat::ASTC_2D_8X5:
130 case TextureFormat::A8R8G8B8: 206 case TextureFormat::A8R8G8B8:
131 case TextureFormat::A2B10G10R10: 207 case TextureFormat::A2B10G10R10:
132 case TextureFormat::BF10GF11RF11: 208 case TextureFormat::BF10GF11RF11:
@@ -153,13 +229,54 @@ u32 BytesPerPixel(TextureFormat format) {
153} 229}
154 230
155std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 231std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
156 u32 height, u32 block_height) { 232 u32 height, u32 depth, u32 block_height, u32 block_depth) {
157 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); 233 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
158 CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel, 234 CopySwizzledData(width / tile_size, height / tile_size, depth, bytes_per_pixel, bytes_per_pixel,
159 Memory::GetPointer(address), unswizzled_data.data(), true, block_height); 235 Memory::GetPointer(address), unswizzled_data.data(), true, block_height,
236 block_depth);
160 return unswizzled_data; 237 return unswizzled_data;
161} 238}
162 239
240void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
241 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
242 u32 block_height) {
243 const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + 63) / 64};
244 for (u32 line = 0; line < subrect_height; ++line) {
245 const u32 gob_address_y =
246 (line / (8 * block_height)) * 512 * block_height * image_width_in_gobs +
247 (line % (8 * block_height) / 8) * 512;
248 const auto& table = legacy_swizzle_table[line % 8];
249 for (u32 x = 0; x < subrect_width; ++x) {
250 const u32 gob_address = gob_address_y + (x * bytes_per_pixel / 64) * 512 * block_height;
251 const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % 64];
252 const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
253 const VAddr dest_addr = swizzled_data + swizzled_offset;
254
255 Memory::CopyBlock(dest_addr, source_line, bytes_per_pixel);
256 }
257 }
258}
259
260void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
261 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
262 u32 block_height, u32 offset_x, u32 offset_y) {
263 for (u32 line = 0; line < subrect_height; ++line) {
264 const u32 y2 = line + offset_y;
265 const u32 gob_address_y =
266 (y2 / (8 * block_height)) * 512 * block_height + (y2 % (8 * block_height) / 8) * 512;
267 const auto& table = legacy_swizzle_table[y2 % 8];
268 for (u32 x = 0; x < subrect_width; ++x) {
269 const u32 x2 = (x + offset_x) * bytes_per_pixel;
270 const u32 gob_address = gob_address_y + (x2 / 64) * 512 * block_height;
271 const u32 swizzled_offset = gob_address + table[x2 % 64];
272 const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
273 const VAddr source_addr = swizzled_data + swizzled_offset;
274
275 Memory::CopyBlock(dest_line, source_addr, bytes_per_pixel);
276 }
277 }
278}
279
163std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, 280std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
164 u32 height) { 281 u32 height) {
165 std::vector<u8> rgba_data; 282 std::vector<u8> rgba_data;
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 234d250af..4726f54a5 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -14,17 +14,14 @@ namespace Tegra::Texture {
14 * Unswizzles a swizzled texture without changing its format. 14 * Unswizzles a swizzled texture without changing its format.
15 */ 15 */
16std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 16std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
17 u32 height, u32 block_height = TICEntry::DefaultBlockHeight); 17 u32 height, u32 depth,
18 18 u32 block_height = TICEntry::DefaultBlockHeight,
19/** 19 u32 block_depth = TICEntry::DefaultBlockHeight);
20 * Unswizzles a swizzled depth texture without changing its format.
21 */
22std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height,
23 u32 block_height = TICEntry::DefaultBlockHeight);
24 20
25/// Copies texture data from a buffer and performs swizzling/unswizzling as necessary. 21/// Copies texture data from a buffer and performs swizzling/unswizzling as necessary.
26void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 22void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
27 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height); 23 u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
24 bool unswizzle, u32 block_height, u32 block_depth);
28 25
29/** 26/**
30 * Decodes an unswizzled texture into a A8R8G8B8 texture. 27 * Decodes an unswizzled texture into a A8R8G8B8 texture.
@@ -38,4 +35,13 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
38std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 35std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
39 u32 block_height, u32 block_depth); 36 u32 block_height, u32 block_depth);
40 37
38/// Copies an untiled subrectangle into a tiled surface.
39void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
40 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
41 u32 block_height);
42/// Copies a tiled subrectangle into a linear surface.
43void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
44 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
45 u32 block_height, u32 offset_x, u32 offset_y);
46
41} // namespace Tegra::Texture 47} // namespace Tegra::Texture
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 58d17abcb..5947bd2b9 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -141,6 +141,7 @@ static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size");
141 141
142struct TICEntry { 142struct TICEntry {
143 static constexpr u32 DefaultBlockHeight = 16; 143 static constexpr u32 DefaultBlockHeight = 16;
144 static constexpr u32 DefaultBlockDepth = 1;
144 145
145 union { 146 union {
146 u32 raw; 147 u32 raw;
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index 033ea1ea4..0a8f2bd9e 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -2,96 +2,114 @@
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 <thread> 5#include <json.hpp>
6#include "common/assert.h"
7#include "common/detached_tasks.h" 6#include "common/detached_tasks.h"
7#include "common/web_result.h"
8#include "web_service/telemetry_json.h" 8#include "web_service/telemetry_json.h"
9#include "web_service/web_backend.h" 9#include "web_service/web_backend.h"
10 10
11namespace WebService { 11namespace WebService {
12 12
13TelemetryJson::TelemetryJson(const std::string& host, const std::string& username, 13struct TelemetryJson::Impl {
14 const std::string& token) 14 Impl(std::string host, std::string username, std::string token)
15 : host(std::move(host)), username(std::move(username)), token(std::move(token)) {} 15 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {}
16TelemetryJson::~TelemetryJson() = default;
17 16
18template <class T> 17 nlohmann::json& TopSection() {
19void TelemetryJson::Serialize(Telemetry::FieldType type, const std::string& name, T value) { 18 return sections[static_cast<u8>(Telemetry::FieldType::None)];
20 sections[static_cast<u8>(type)][name] = value; 19 }
21}
22 20
23void TelemetryJson::SerializeSection(Telemetry::FieldType type, const std::string& name) { 21 const nlohmann::json& TopSection() const {
24 TopSection()[name] = sections[static_cast<unsigned>(type)]; 22 return sections[static_cast<u8>(Telemetry::FieldType::None)];
25} 23 }
24
25 template <class T>
26 void Serialize(Telemetry::FieldType type, const std::string& name, T value) {
27 sections[static_cast<u8>(type)][name] = value;
28 }
29
30 void SerializeSection(Telemetry::FieldType type, const std::string& name) {
31 TopSection()[name] = sections[static_cast<unsigned>(type)];
32 }
33
34 nlohmann::json output;
35 std::array<nlohmann::json, 7> sections;
36 std::string host;
37 std::string username;
38 std::string token;
39};
40
41TelemetryJson::TelemetryJson(std::string host, std::string username, std::string token)
42 : impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {}
43TelemetryJson::~TelemetryJson() = default;
26 44
27void TelemetryJson::Visit(const Telemetry::Field<bool>& field) { 45void TelemetryJson::Visit(const Telemetry::Field<bool>& field) {
28 Serialize(field.GetType(), field.GetName(), field.GetValue()); 46 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
29} 47}
30 48
31void TelemetryJson::Visit(const Telemetry::Field<double>& field) { 49void TelemetryJson::Visit(const Telemetry::Field<double>& field) {
32 Serialize(field.GetType(), field.GetName(), field.GetValue()); 50 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
33} 51}
34 52
35void TelemetryJson::Visit(const Telemetry::Field<float>& field) { 53void TelemetryJson::Visit(const Telemetry::Field<float>& field) {
36 Serialize(field.GetType(), field.GetName(), field.GetValue()); 54 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
37} 55}
38 56
39void TelemetryJson::Visit(const Telemetry::Field<u8>& field) { 57void TelemetryJson::Visit(const Telemetry::Field<u8>& field) {
40 Serialize(field.GetType(), field.GetName(), field.GetValue()); 58 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
41} 59}
42 60
43void TelemetryJson::Visit(const Telemetry::Field<u16>& field) { 61void TelemetryJson::Visit(const Telemetry::Field<u16>& field) {
44 Serialize(field.GetType(), field.GetName(), field.GetValue()); 62 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
45} 63}
46 64
47void TelemetryJson::Visit(const Telemetry::Field<u32>& field) { 65void TelemetryJson::Visit(const Telemetry::Field<u32>& field) {
48 Serialize(field.GetType(), field.GetName(), field.GetValue()); 66 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
49} 67}
50 68
51void TelemetryJson::Visit(const Telemetry::Field<u64>& field) { 69void TelemetryJson::Visit(const Telemetry::Field<u64>& field) {
52 Serialize(field.GetType(), field.GetName(), field.GetValue()); 70 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
53} 71}
54 72
55void TelemetryJson::Visit(const Telemetry::Field<s8>& field) { 73void TelemetryJson::Visit(const Telemetry::Field<s8>& field) {
56 Serialize(field.GetType(), field.GetName(), field.GetValue()); 74 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
57} 75}
58 76
59void TelemetryJson::Visit(const Telemetry::Field<s16>& field) { 77void TelemetryJson::Visit(const Telemetry::Field<s16>& field) {
60 Serialize(field.GetType(), field.GetName(), field.GetValue()); 78 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
61} 79}
62 80
63void TelemetryJson::Visit(const Telemetry::Field<s32>& field) { 81void TelemetryJson::Visit(const Telemetry::Field<s32>& field) {
64 Serialize(field.GetType(), field.GetName(), field.GetValue()); 82 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
65} 83}
66 84
67void TelemetryJson::Visit(const Telemetry::Field<s64>& field) { 85void TelemetryJson::Visit(const Telemetry::Field<s64>& field) {
68 Serialize(field.GetType(), field.GetName(), field.GetValue()); 86 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
69} 87}
70 88
71void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) { 89void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) {
72 Serialize(field.GetType(), field.GetName(), field.GetValue()); 90 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
73} 91}
74 92
75void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) { 93void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) {
76 Serialize(field.GetType(), field.GetName(), std::string(field.GetValue())); 94 impl->Serialize(field.GetType(), field.GetName(), std::string(field.GetValue()));
77} 95}
78 96
79void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) { 97void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) {
80 Serialize(field.GetType(), field.GetName(), field.GetValue().count()); 98 impl->Serialize(field.GetType(), field.GetName(), field.GetValue().count());
81} 99}
82 100
83void TelemetryJson::Complete() { 101void TelemetryJson::Complete() {
84 SerializeSection(Telemetry::FieldType::App, "App"); 102 impl->SerializeSection(Telemetry::FieldType::App, "App");
85 SerializeSection(Telemetry::FieldType::Session, "Session"); 103 impl->SerializeSection(Telemetry::FieldType::Session, "Session");
86 SerializeSection(Telemetry::FieldType::Performance, "Performance"); 104 impl->SerializeSection(Telemetry::FieldType::Performance, "Performance");
87 SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); 105 impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
88 SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); 106 impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
89 SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); 107 impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
90 108
91 auto content = TopSection().dump(); 109 auto content = impl->TopSection().dump();
92 // Send the telemetry async but don't handle the errors since they were written to the log 110 // Send the telemetry async but don't handle the errors since they were written to the log
93 Common::DetachedTasks::AddTask( 111 Common::DetachedTasks::AddTask(
94 [host{this->host}, username{this->username}, token{this->token}, content]() { 112 [host{impl->host}, username{impl->username}, token{impl->token}, content]() {
95 Client{host, username, token}.PostJson("/telemetry", content, true); 113 Client{host, username, token}.PostJson("/telemetry", content, true);
96 }); 114 });
97} 115}
diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h
index 0fe6f9a3e..93371414a 100644
--- a/src/web_service/telemetry_json.h
+++ b/src/web_service/telemetry_json.h
@@ -4,11 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <chrono>
8#include <string> 8#include <string>
9#include <json.hpp>
10#include "common/telemetry.h" 9#include "common/telemetry.h"
11#include "common/web_result.h"
12 10
13namespace WebService { 11namespace WebService {
14 12
@@ -18,8 +16,8 @@ namespace WebService {
18 */ 16 */
19class TelemetryJson : public Telemetry::VisitorInterface { 17class TelemetryJson : public Telemetry::VisitorInterface {
20public: 18public:
21 TelemetryJson(const std::string& host, const std::string& username, const std::string& token); 19 TelemetryJson(std::string host, std::string username, std::string token);
22 ~TelemetryJson(); 20 ~TelemetryJson() override;
23 21
24 void Visit(const Telemetry::Field<bool>& field) override; 22 void Visit(const Telemetry::Field<bool>& field) override;
25 void Visit(const Telemetry::Field<double>& field) override; 23 void Visit(const Telemetry::Field<double>& field) override;
@@ -39,20 +37,8 @@ public:
39 void Complete() override; 37 void Complete() override;
40 38
41private: 39private:
42 nlohmann::json& TopSection() { 40 struct Impl;
43 return sections[static_cast<u8>(Telemetry::FieldType::None)]; 41 std::unique_ptr<Impl> impl;
44 }
45
46 template <class T>
47 void Serialize(Telemetry::FieldType type, const std::string& name, T value);
48
49 void SerializeSection(Telemetry::FieldType type, const std::string& name);
50
51 nlohmann::json output;
52 std::array<nlohmann::json, 7> sections;
53 std::string host;
54 std::string username;
55 std::string token;
56}; 42};
57 43
58} // namespace WebService 44} // namespace WebService
diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp
index 124aa3863..ca4b43b93 100644
--- a/src/web_service/verify_login.cpp
+++ b/src/web_service/verify_login.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 <json.hpp> 5#include <json.hpp>
6#include "common/web_result.h"
6#include "web_service/verify_login.h" 7#include "web_service/verify_login.h"
7#include "web_service/web_backend.h" 8#include "web_service/web_backend.h"
8 9
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 787b0fbcb..b7737b615 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -3,9 +3,11 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstdlib> 5#include <cstdlib>
6#include <mutex>
6#include <string> 7#include <string>
7#include <thread>
8#include <LUrlParser.h> 8#include <LUrlParser.h>
9#include <httplib.h>
10#include "common/common_types.h"
9#include "common/logging/log.h" 11#include "common/logging/log.h"
10#include "common/web_result.h" 12#include "common/web_result.h"
11#include "core/settings.h" 13#include "core/settings.h"
@@ -20,99 +22,132 @@ constexpr u32 HTTPS_PORT = 443;
20 22
21constexpr u32 TIMEOUT_SECONDS = 30; 23constexpr u32 TIMEOUT_SECONDS = 30;
22 24
23Client::JWTCache Client::jwt_cache{}; 25struct Client::Impl {
26 Impl(std::string host, std::string username, std::string token)
27 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {
28 std::lock_guard<std::mutex> lock(jwt_cache.mutex);
29 if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
30 jwt = jwt_cache.jwt;
31 }
32 }
33
34 /// A generic function handles POST, GET and DELETE request together
35 Common::WebResult GenericJson(const std::string& method, const std::string& path,
36 const std::string& data, bool allow_anonymous) {
37 if (jwt.empty()) {
38 UpdateJWT();
39 }
40
41 if (jwt.empty() && !allow_anonymous) {
42 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
43 return Common::WebResult{Common::WebResult::Code::CredentialsMissing,
44 "Credentials needed"};
45 }
46
47 auto result = GenericJson(method, path, data, jwt);
48 if (result.result_string == "401") {
49 // Try again with new JWT
50 UpdateJWT();
51 result = GenericJson(method, path, data, jwt);
52 }
24 53
25Client::Client(const std::string& host, const std::string& username, const std::string& token) 54 return result;
26 : host(host), username(username), token(token) {
27 std::lock_guard<std::mutex> lock(jwt_cache.mutex);
28 if (username == jwt_cache.username && token == jwt_cache.token) {
29 jwt = jwt_cache.jwt;
30 } 55 }
31}
32 56
33Common::WebResult Client::GenericJson(const std::string& method, const std::string& path, 57 /**
34 const std::string& data, const std::string& jwt, 58 * A generic function with explicit authentication method specified
35 const std::string& username, const std::string& token) { 59 * JWT is used if the jwt parameter is not empty
36 if (cli == nullptr) { 60 * username + token is used if jwt is empty but username and token are not empty
37 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); 61 * anonymous if all of jwt, username and token are empty
38 int port; 62 */
39 if (parsedUrl.m_Scheme == "http") { 63 Common::WebResult GenericJson(const std::string& method, const std::string& path,
40 if (!parsedUrl.GetPort(&port)) { 64 const std::string& data, const std::string& jwt = "",
41 port = HTTP_PORT; 65 const std::string& username = "", const std::string& token = "") {
42 } 66 if (cli == nullptr) {
43 cli = 67 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
44 std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port, TIMEOUT_SECONDS); 68 int port;
45 } else if (parsedUrl.m_Scheme == "https") { 69 if (parsedUrl.m_Scheme == "http") {
46 if (!parsedUrl.GetPort(&port)) { 70 if (!parsedUrl.GetPort(&port)) {
47 port = HTTPS_PORT; 71 port = HTTP_PORT;
72 }
73 cli = std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port,
74 TIMEOUT_SECONDS);
75 } else if (parsedUrl.m_Scheme == "https") {
76 if (!parsedUrl.GetPort(&port)) {
77 port = HTTPS_PORT;
78 }
79 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port,
80 TIMEOUT_SECONDS);
81 } else {
82 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
83 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};
48 } 84 }
49 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port,
50 TIMEOUT_SECONDS);
51 } else {
52 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
53 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};
54 } 85 }
55 } 86 if (cli == nullptr) {
56 if (cli == nullptr) { 87 LOG_ERROR(WebService, "Invalid URL {}", host + path);
57 LOG_ERROR(WebService, "Invalid URL {}", host + path); 88 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"};
58 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"}; 89 }
59 }
60 90
61 httplib::Headers params; 91 httplib::Headers params;
62 if (!jwt.empty()) { 92 if (!jwt.empty()) {
63 params = { 93 params = {
64 {std::string("Authorization"), fmt::format("Bearer {}", jwt)}, 94 {std::string("Authorization"), fmt::format("Bearer {}", jwt)},
65 }; 95 };
66 } else if (!username.empty()) { 96 } else if (!username.empty()) {
67 params = { 97 params = {
68 {std::string("x-username"), username}, 98 {std::string("x-username"), username},
69 {std::string("x-token"), token}, 99 {std::string("x-token"), token},
100 };
101 }
102
103 params.emplace(std::string("api-version"),
104 std::string(API_VERSION.begin(), API_VERSION.end()));
105 if (method != "GET") {
106 params.emplace(std::string("Content-Type"), std::string("application/json"));
70 }; 107 };
71 }
72 108
73 params.emplace(std::string("api-version"), std::string(API_VERSION.begin(), API_VERSION.end())); 109 httplib::Request request;
74 if (method != "GET") { 110 request.method = method;
75 params.emplace(std::string("Content-Type"), std::string("application/json")); 111 request.path = path;
76 }; 112 request.headers = params;
113 request.body = data;
77 114
78 httplib::Request request; 115 httplib::Response response;
79 request.method = method;
80 request.path = path;
81 request.headers = params;
82 request.body = data;
83 116
84 httplib::Response response; 117 if (!cli->send(request, response)) {
118 LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
119 return Common::WebResult{Common::WebResult::Code::LibError, "Null response"};
120 }
85 121
86 if (!cli->send(request, response)) { 122 if (response.status >= 400) {
87 LOG_ERROR(WebService, "{} to {} returned null", method, host + path); 123 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
88 return Common::WebResult{Common::WebResult::Code::LibError, "Null response"}; 124 response.status);
89 } 125 return Common::WebResult{Common::WebResult::Code::HttpError,
126 std::to_string(response.status)};
127 }
90 128
91 if (response.status >= 400) { 129 auto content_type = response.headers.find("content-type");
92 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
93 response.status);
94 return Common::WebResult{Common::WebResult::Code::HttpError,
95 std::to_string(response.status)};
96 }
97 130
98 auto content_type = response.headers.find("content-type"); 131 if (content_type == response.headers.end()) {
132 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path);
133 return Common::WebResult{Common::WebResult::Code::WrongContent, ""};
134 }
99 135
100 if (content_type == response.headers.end()) { 136 if (content_type->second.find("application/json") == std::string::npos &&
101 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path); 137 content_type->second.find("text/html; charset=utf-8") == std::string::npos) {
102 return Common::WebResult{Common::WebResult::Code::WrongContent, ""}; 138 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path,
139 content_type->second);
140 return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"};
141 }
142 return Common::WebResult{Common::WebResult::Code::Success, "", response.body};
103 } 143 }
104 144
105 if (content_type->second.find("application/json") == std::string::npos && 145 // Retrieve a new JWT from given username and token
106 content_type->second.find("text/html; charset=utf-8") == std::string::npos) { 146 void UpdateJWT() {
107 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path, 147 if (username.empty() || token.empty()) {
108 content_type->second); 148 return;
109 return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"}; 149 }
110 }
111 return Common::WebResult{Common::WebResult::Code::Success, "", response.body};
112}
113 150
114void Client::UpdateJWT() {
115 if (!username.empty() && !token.empty()) {
116 auto result = GenericJson("POST", "/jwt/internal", "", "", username, token); 151 auto result = GenericJson("POST", "/jwt/internal", "", "", username, token);
117 if (result.result_code != Common::WebResult::Code::Success) { 152 if (result.result_code != Common::WebResult::Code::Success) {
118 LOG_ERROR(WebService, "UpdateJWT failed"); 153 LOG_ERROR(WebService, "UpdateJWT failed");
@@ -123,27 +158,39 @@ void Client::UpdateJWT() {
123 jwt_cache.jwt = jwt = result.returned_data; 158 jwt_cache.jwt = jwt = result.returned_data;
124 } 159 }
125 } 160 }
126}
127 161
128Common::WebResult Client::GenericJson(const std::string& method, const std::string& path, 162 std::string host;
129 const std::string& data, bool allow_anonymous) { 163 std::string username;
130 if (jwt.empty()) { 164 std::string token;
131 UpdateJWT(); 165 std::string jwt;
132 } 166 std::unique_ptr<httplib::Client> cli;
167
168 struct JWTCache {
169 std::mutex mutex;
170 std::string username;
171 std::string token;
172 std::string jwt;
173 };
174 static inline JWTCache jwt_cache;
175};
133 176
134 if (jwt.empty() && !allow_anonymous) { 177Client::Client(std::string host, std::string username, std::string token)
135 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); 178 : impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {}
136 return Common::WebResult{Common::WebResult::Code::CredentialsMissing, "Credentials needed"};
137 }
138 179
139 auto result = GenericJson(method, path, data, jwt); 180Client::~Client() = default;
140 if (result.result_string == "401") { 181
141 // Try again with new JWT 182Common::WebResult Client::PostJson(const std::string& path, const std::string& data,
142 UpdateJWT(); 183 bool allow_anonymous) {
143 result = GenericJson(method, path, data, jwt); 184 return impl->GenericJson("POST", path, data, allow_anonymous);
144 } 185}
186
187Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
188 return impl->GenericJson("GET", path, "", allow_anonymous);
189}
145 190
146 return result; 191Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data,
192 bool allow_anonymous) {
193 return impl->GenericJson("DELETE", path, data, allow_anonymous);
147} 194}
148 195
149} // namespace WebService 196} // namespace WebService
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h
index d75fbcc15..c637e09df 100644
--- a/src/web_service/web_backend.h
+++ b/src/web_service/web_backend.h
@@ -4,23 +4,19 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional> 7#include <memory>
8#include <mutex>
9#include <string> 8#include <string>
10#include <tuple>
11#include <httplib.h>
12#include "common/common_types.h"
13#include "common/web_result.h"
14 9
15namespace httplib { 10namespace Common {
16class Client; 11struct WebResult;
17} 12}
18 13
19namespace WebService { 14namespace WebService {
20 15
21class Client { 16class Client {
22public: 17public:
23 Client(const std::string& host, const std::string& username, const std::string& token); 18 Client(std::string host, std::string username, std::string token);
19 ~Client();
24 20
25 /** 21 /**
26 * Posts JSON to the specified path. 22 * Posts JSON to the specified path.
@@ -30,9 +26,7 @@ public:
30 * @return the result of the request. 26 * @return the result of the request.
31 */ 27 */
32 Common::WebResult PostJson(const std::string& path, const std::string& data, 28 Common::WebResult PostJson(const std::string& path, const std::string& data,
33 bool allow_anonymous) { 29 bool allow_anonymous);
34 return GenericJson("POST", path, data, allow_anonymous);
35 }
36 30
37 /** 31 /**
38 * Gets JSON from the specified path. 32 * Gets JSON from the specified path.
@@ -40,9 +34,7 @@ public:
40 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 34 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
41 * @return the result of the request. 35 * @return the result of the request.
42 */ 36 */
43 Common::WebResult GetJson(const std::string& path, bool allow_anonymous) { 37 Common::WebResult GetJson(const std::string& path, bool allow_anonymous);
44 return GenericJson("GET", path, "", allow_anonymous);
45 }
46 38
47 /** 39 /**
48 * Deletes JSON to the specified path. 40 * Deletes JSON to the specified path.
@@ -52,41 +44,11 @@ public:
52 * @return the result of the request. 44 * @return the result of the request.
53 */ 45 */
54 Common::WebResult DeleteJson(const std::string& path, const std::string& data, 46 Common::WebResult DeleteJson(const std::string& path, const std::string& data,
55 bool allow_anonymous) { 47 bool allow_anonymous);
56 return GenericJson("DELETE", path, data, allow_anonymous);
57 }
58 48
59private: 49private:
60 /// A generic function handles POST, GET and DELETE request together 50 struct Impl;
61 Common::WebResult GenericJson(const std::string& method, const std::string& path, 51 std::unique_ptr<Impl> impl;
62 const std::string& data, bool allow_anonymous);
63
64 /**
65 * A generic function with explicit authentication method specified
66 * JWT is used if the jwt parameter is not empty
67 * username + token is used if jwt is empty but username and token are not empty
68 * anonymous if all of jwt, username and token are empty
69 */
70 Common::WebResult GenericJson(const std::string& method, const std::string& path,
71 const std::string& data, const std::string& jwt = "",
72 const std::string& username = "", const std::string& token = "");
73
74 // Retrieve a new JWT from given username and token
75 void UpdateJWT();
76
77 std::string host;
78 std::string username;
79 std::string token;
80 std::string jwt;
81 std::unique_ptr<httplib::Client> cli;
82
83 struct JWTCache {
84 std::mutex mutex;
85 std::string username;
86 std::string token;
87 std::string jwt;
88 };
89 static JWTCache jwt_cache;
90}; 52};
91 53
92} // namespace WebService 54} // namespace WebService
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 7fec15991..71c6ebb41 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -85,8 +85,8 @@ void Config::ReadValues() {
85 Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat(); 85 Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat();
86 Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool(); 86 Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool();
87 Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt(); 87 Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt();
88 Settings::values.use_accurate_framebuffers = 88 Settings::values.use_accurate_gpu_emulation =
89 qt_config->value("use_accurate_framebuffers", false).toBool(); 89 qt_config->value("use_accurate_gpu_emulation", false).toBool();
90 90
91 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); 91 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat();
92 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); 92 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat();
@@ -233,7 +233,7 @@ void Config::SaveValues() {
233 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor); 233 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor);
234 qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit); 234 qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit);
235 qt_config->setValue("frame_limit", Settings::values.frame_limit); 235 qt_config->setValue("frame_limit", Settings::values.frame_limit);
236 qt_config->setValue("use_accurate_framebuffers", Settings::values.use_accurate_framebuffers); 236 qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation);
237 237
238 // Cast to double because Qt's written float values are not human-readable 238 // Cast to double because Qt's written float values are not human-readable
239 qt_config->setValue("bg_red", (double)Settings::values.bg_red); 239 qt_config->setValue("bg_red", (double)Settings::values.bg_red);
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index cd1549462..8290b4384 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -75,7 +75,7 @@ void ConfigureGraphics::setConfiguration() {
75 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); 75 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
76 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); 76 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
77 ui->frame_limit->setValue(Settings::values.frame_limit); 77 ui->frame_limit->setValue(Settings::values.frame_limit);
78 ui->use_accurate_framebuffers->setChecked(Settings::values.use_accurate_framebuffers); 78 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
79 bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, 79 bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
80 Settings::values.bg_blue); 80 Settings::values.bg_blue);
81 ui->bg_button->setStyleSheet( 81 ui->bg_button->setStyleSheet(
@@ -87,7 +87,7 @@ void ConfigureGraphics::applyConfiguration() {
87 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); 87 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
88 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); 88 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
89 Settings::values.frame_limit = ui->frame_limit->value(); 89 Settings::values.frame_limit = ui->frame_limit->value();
90 Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked(); 90 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
91 Settings::values.bg_red = static_cast<float>(bg_color.redF()); 91 Settings::values.bg_red = static_cast<float>(bg_color.redF());
92 Settings::values.bg_green = static_cast<float>(bg_color.greenF()); 92 Settings::values.bg_green = static_cast<float>(bg_color.greenF());
93 Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); 93 Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 8fc00af1b..91fcad994 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -50,9 +50,9 @@
50 </layout> 50 </layout>
51 </item> 51 </item>
52 <item> 52 <item>
53 <widget class="QCheckBox" name="use_accurate_framebuffers"> 53 <widget class="QCheckBox" name="use_accurate_gpu_emulation">
54 <property name="text"> 54 <property name="text">
55 <string>Use accurate framebuffers (slow)</string> 55 <string>Use accurate GPU emulation (slow)</string>
56 </property> 56 </property>
57 </widget> 57 </widget>
58 </item> 58 </item>
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index cbcd5dd5f..44d423da2 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -386,8 +386,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
386 386
387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. 387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. 388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
389 auto unswizzled_data = Tegra::Texture::UnswizzleTexture( 389 auto unswizzled_data =
390 *address, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height); 390 Tegra::Texture::UnswizzleTexture(*address, 1, Tegra::Texture::BytesPerPixel(surface_format),
391 surface_width, surface_height, 1U);
391 392
392 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, 393 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
393 surface_width, surface_height); 394 surface_width, surface_height);
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 4a09da685..7403e9ccd 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -66,10 +66,11 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList()
66 } 66 }
67 }; 67 };
68 68
69 add_threads(Core::System::GetInstance().Scheduler(0)->GetThreadList()); 69 const auto& system = Core::System::GetInstance();
70 add_threads(Core::System::GetInstance().Scheduler(1)->GetThreadList()); 70 add_threads(system.Scheduler(0).GetThreadList());
71 add_threads(Core::System::GetInstance().Scheduler(2)->GetThreadList()); 71 add_threads(system.Scheduler(1).GetThreadList());
72 add_threads(Core::System::GetInstance().Scheduler(3)->GetThreadList()); 72 add_threads(system.Scheduler(2).GetThreadList());
73 add_threads(system.Scheduler(3).GetThreadList());
73 74
74 return item_list; 75 return item_list;
75} 76}
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 8f99a1c78..3881aba5f 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -96,7 +96,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
96 FileSys::ContentRecordType::Program); 96 FileSys::ContentRecordType::Program);
97 97
98 for (const auto& game : installed_games) { 98 for (const auto& game : installed_games) {
99 const auto& file = cache->GetEntryUnparsed(game); 99 const auto file = cache->GetEntryUnparsed(game);
100 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); 100 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
101 if (!loader) 101 if (!loader)
102 continue; 102 continue;
@@ -107,7 +107,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
107 loader->ReadProgramId(program_id); 107 loader->ReadProgramId(program_id);
108 108
109 const FileSys::PatchManager patch{program_id}; 109 const FileSys::PatchManager patch{program_id};
110 const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); 110 const auto control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
111 if (control != nullptr) 111 if (control != nullptr)
112 GetMetadataFromControlNCA(patch, *control, icon, name); 112 GetMetadataFromControlNCA(patch, *control, icon, name);
113 113
@@ -135,9 +135,10 @@ void GameListWorker::AddInstalledTitlesToGameList() {
135 FileSys::ContentRecordType::Control); 135 FileSys::ContentRecordType::Control);
136 136
137 for (const auto& entry : control_data) { 137 for (const auto& entry : control_data) {
138 const auto nca = cache->GetEntry(entry); 138 auto nca = cache->GetEntry(entry);
139 if (nca != nullptr) 139 if (nca != nullptr) {
140 nca_control_map.insert_or_assign(entry.title_id, nca); 140 nca_control_map.insert_or_assign(entry.title_id, std::move(nca));
141 }
141 } 142 }
142} 143}
143 144
@@ -153,9 +154,11 @@ void GameListWorker::FillControlMap(const std::string& dir_path) {
153 QFileInfo file_info(physical_name.c_str()); 154 QFileInfo file_info(physical_name.c_str());
154 if (!is_dir && file_info.suffix().toStdString() == "nca") { 155 if (!is_dir && file_info.suffix().toStdString() == "nca") {
155 auto nca = 156 auto nca =
156 std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); 157 std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
157 if (nca->GetType() == FileSys::NCAContentType::Control) 158 if (nca->GetType() == FileSys::NCAContentType::Control) {
158 nca_control_map.insert_or_assign(nca->GetTitleId(), nca); 159 const u64 title_id = nca->GetTitleId();
160 nca_control_map.insert_or_assign(title_id, std::move(nca));
161 }
159 } 162 }
160 return true; 163 return true;
161 }; 164 };
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h
index 09d20c42f..0e42d0bde 100644
--- a/src/yuzu/game_list_worker.h
+++ b/src/yuzu/game_list_worker.h
@@ -63,7 +63,7 @@ private:
63 void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); 63 void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);
64 64
65 std::shared_ptr<FileSys::VfsFilesystem> vfs; 65 std::shared_ptr<FileSys::VfsFilesystem> vfs;
66 std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map; 66 std::map<u64, std::unique_ptr<FileSys::NCA>> nca_control_map;
67 QStringList watch_list; 67 QStringList watch_list;
68 QString dir_path; 68 QString dir_path;
69 bool deep_scan; 69 bool deep_scan;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index fc186dc2d..bef9df00d 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -176,7 +176,7 @@ GMainWindow::GMainWindow()
176 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); 176 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
177 177
178 // Necessary to load titles from nand in gamelist. 178 // Necessary to load titles from nand in gamelist.
179 Service::FileSystem::CreateFactories(vfs); 179 Service::FileSystem::CreateFactories(*vfs);
180 game_list->LoadCompatibilityList(); 180 game_list->LoadCompatibilityList();
181 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 181 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
182 182
@@ -908,22 +908,20 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
908} 908}
909 909
910void GMainWindow::OnMenuLoadFile() { 910void GMainWindow::OnMenuLoadFile() {
911 QString extensions; 911 const QString extensions =
912 for (const auto& piece : game_list->supported_file_extensions) 912 QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main");
913 extensions += "*." + piece + " "; 913 const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)",
914 "%1 is an identifier for the Switch executable file extensions.")
915 .arg(extensions);
916 const QString filename = QFileDialog::getOpenFileName(
917 this, tr("Load File"), UISettings::values.roms_path, file_filter);
914 918
915 extensions += "main "; 919 if (filename.isEmpty()) {
916 920 return;
917 QString file_filter = tr("Switch Executable") + " (" + extensions + ")";
918 file_filter += ";;" + tr("All Files (*.*)");
919
920 QString filename = QFileDialog::getOpenFileName(this, tr("Load File"),
921 UISettings::values.roms_path, file_filter);
922 if (!filename.isEmpty()) {
923 UISettings::values.roms_path = QFileInfo(filename).path();
924
925 BootGame(filename);
926 } 921 }
922
923 UISettings::values.roms_path = QFileInfo(filename).path();
924 BootGame(filename);
927} 925}
928 926
929void GMainWindow::OnMenuLoadFolder() { 927void GMainWindow::OnMenuLoadFolder() {
@@ -1139,7 +1137,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
1139 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir 1137 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
1140 : FileUtil::UserPath::NANDDir, 1138 : FileUtil::UserPath::NANDDir,
1141 dir_path.toStdString()); 1139 dir_path.toStdString());
1142 Service::FileSystem::CreateFactories(vfs); 1140 Service::FileSystem::CreateFactories(*vfs);
1143 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1141 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
1144 } 1142 }
1145} 1143}
@@ -1410,7 +1408,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1410 1408
1411 const auto function = [this, &keys, &pdm] { 1409 const auto function = [this, &keys, &pdm] {
1412 keys.PopulateFromPartitionData(pdm); 1410 keys.PopulateFromPartitionData(pdm);
1413 Service::FileSystem::CreateFactories(vfs); 1411 Service::FileSystem::CreateFactories(*vfs);
1414 keys.DeriveETicket(pdm); 1412 keys.DeriveETicket(pdm);
1415 }; 1413 };
1416 1414
@@ -1430,8 +1428,12 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1430 QMessageBox::warning( 1428 QMessageBox::warning(
1431 this, tr("Warning Missing Derivation Components"), 1429 this, tr("Warning Missing Derivation Components"),
1432 tr("The following are missing from your configuration that may hinder key " 1430 tr("The following are missing from your configuration that may hinder key "
1433 "derivation. It will be attempted but may not complete.\n\n") + 1431 "derivation. It will be attempted but may not complete.<br><br>") +
1434 errors); 1432 errors +
1433 tr("<br><br>You can get all of these and dump all of your games easily by "
1434 "following <a href='https://yuzu-emu.org/help/quickstart/quickstart/'>the "
1435 "quickstart guide</a>. Alternatively, you can use another method of dumping "
1436 "to obtain all of your keys."));
1435 } 1437 }
1436 1438
1437 QProgressDialog prog; 1439 QProgressDialog prog;
@@ -1450,7 +1452,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1450 prog.close(); 1452 prog.close();
1451 } 1453 }
1452 1454
1453 Service::FileSystem::CreateFactories(vfs); 1455 Service::FileSystem::CreateFactories(*vfs);
1454 1456
1455 if (behavior == ReinitializeKeyBehavior::Warning) { 1457 if (behavior == ReinitializeKeyBehavior::Warning) {
1456 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1458 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
@@ -1565,7 +1567,7 @@ void GMainWindow::UpdateUITheme() {
1565 emit UpdateThemedIcons(); 1567 emit UpdateThemedIcons();
1566} 1568}
1567 1569
1568void GMainWindow::SetDiscordEnabled(bool state) { 1570void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
1569#ifdef USE_DISCORD_PRESENCE 1571#ifdef USE_DISCORD_PRESENCE
1570 if (state) { 1572 if (state) {
1571 discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>(); 1573 discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>();
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 2470f4640..5e42e48b2 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -99,8 +99,8 @@ void Config::ReadValues() {
99 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); 99 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
100 Settings::values.frame_limit = 100 Settings::values.frame_limit =
101 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); 101 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
102 Settings::values.use_accurate_framebuffers = 102 Settings::values.use_accurate_gpu_emulation =
103 sdl2_config->GetBoolean("Renderer", "use_accurate_framebuffers", false); 103 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
104 104
105 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0); 105 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
106 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); 106 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 762396e3b..a97b75f7b 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -110,9 +110,9 @@ use_frame_limit =
110# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) 110# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
111frame_limit = 111frame_limit =
112 112
113# Whether to use accurate framebuffers 113# Whether to use accurate GPU emulation
114# 0 (default): Off (fast), 1 : On (slow) 114# 0 (default): Off (fast), 1 : On (slow)
115use_accurate_framebuffers = 115use_accurate_gpu_emulation =
116 116
117# The clear color for the renderer. What shows up on the sides of the bottom screen. 117# The clear color for the renderer. What shows up on the sides of the bottom screen.
118# Must be in range of 0.0-1.0. Defaults to 1.0 for all. 118# Must be in range of 0.0-1.0. Defaults to 1.0 for all.
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 27aba95f6..c8b93b85b 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -175,7 +175,7 @@ int main(int argc, char** argv) {
175 175
176 Core::System& system{Core::System::GetInstance()}; 176 Core::System& system{Core::System::GetInstance()};
177 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 177 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
178 Service::FileSystem::CreateFactories(system.GetFilesystem()); 178 Service::FileSystem::CreateFactories(*system.GetFilesystem());
179 179
180 SCOPE_EXIT({ system.Shutdown(); }); 180 SCOPE_EXIT({ system.Shutdown(); });
181 181