summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/web_result.h1
-rw-r--r--src/core/CMakeLists.txt7
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp9
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h4
-rw-r--r--src/core/core.cpp50
-rw-r--r--src/core/core.h18
-rw-r--r--src/core/core_cpu.cpp14
-rw-r--r--src/core/core_cpu.h17
-rw-r--r--src/core/crypto/key_manager.cpp826
-rw-r--r--src/core/crypto/key_manager.h104
-rw-r--r--src/core/crypto/partition_data_manager.cpp593
-rw-r--r--src/core/crypto/partition_data_manager.h109
-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/content_archive.cpp2
-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/ips_layer.cpp6
-rw-r--r--src/core/file_sys/patch_manager.cpp27
-rw-r--r--src/core/file_sys/patch_manager.h5
-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/file_sys/vfs.h10
-rw-r--r--src/core/file_sys/vfs_types.h21
-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/errors.h2
-rw-r--r--src/core/hle/kernel/kernel.cpp12
-rw-r--r--src/core/hle/kernel/kernel.h10
-rw-r--r--src/core/hle/kernel/object.cpp1
-rw-r--r--src/core/hle/kernel/object.h1
-rw-r--r--src/core/hle/kernel/process.cpp34
-rw-r--r--src/core/hle/kernel/process.h49
-rw-r--r--src/core/hle/kernel/scheduler.cpp8
-rw-r--r--src/core/hle/kernel/svc.cpp165
-rw-r--r--src/core/hle/kernel/svc.h4
-rw-r--r--src/core/hle/kernel/svc_wrap.h64
-rw-r--r--src/core/hle/kernel/thread.cpp29
-rw-r--r--src/core/hle/kernel/thread.h8
-rw-r--r--src/core/hle/kernel/vm_manager.cpp35
-rw-r--r--src/core/hle/kernel/vm_manager.h12
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp18
-rw-r--r--src/core/hle/service/audio/hwopus.cpp37
-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/ns/pl_u.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp72
-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/vi/vi.cpp50
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp22
-rw-r--r--src/core/loader/elf.cpp32
-rw-r--r--src/core/loader/loader.cpp3
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/nro.cpp29
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp42
-rw-r--r--src/core/loader/nso.h6
-rw-r--r--src/core/loader/nsp.cpp2
-rw-r--r--src/core/loader/nsp.h2
-rw-r--r--src/core/loader/xci.cpp2
-rw-r--r--src/core/loader/xci.h2
-rw-r--r--src/core/settings.h2
-rw-r--r--src/core/telemetry_session.cpp4
-rw-r--r--src/tests/core/arm/arm_test_common.cpp3
-rw-r--r--src/video_core/engines/fermi_2d.cpp14
-rw-r--r--src/video_core/engines/fermi_2d.h14
-rw-r--r--src/video_core/engines/maxwell_3d.h41
-rw-r--r--src/video_core/engines/maxwell_dma.cpp10
-rw-r--r--src/video_core/engines/maxwell_dma.h4
-rw-r--r--src/video_core/engines/shader_bytecode.h150
-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.cpp88
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h9
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp394
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h110
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp34
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h57
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp461
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp84
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h7
-rw-r--r--src/video_core/textures/decoders.cpp209
-rw-r--r--src/video_core/textures/decoders.h21
-rw-r--r--src/video_core/textures/texture.h18
-rw-r--r--src/video_core/utils.h24
-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.cpp30
-rw-r--r--src/yuzu/game_list_worker.h2
-rw-r--r--src/yuzu/main.cpp118
-rw-r--r--src/yuzu/main.h6
-rw-r--r--src/yuzu/main.ui6
-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
112 files changed, 4114 insertions, 1155 deletions
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 1a9ec459f..4755ec822 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -18,6 +18,8 @@ add_library(core STATIC
18 crypto/encryption_layer.h 18 crypto/encryption_layer.h
19 crypto/key_manager.cpp 19 crypto/key_manager.cpp
20 crypto/key_manager.h 20 crypto/key_manager.h
21 crypto/partition_data_manager.cpp
22 crypto/partition_data_manager.h
21 crypto/ctr_encryption_layer.cpp 23 crypto/ctr_encryption_layer.cpp
22 crypto/ctr_encryption_layer.h 24 crypto/ctr_encryption_layer.h
23 crypto/xts_encryption_layer.cpp 25 crypto/xts_encryption_layer.cpp
@@ -70,6 +72,7 @@ add_library(core STATIC
70 file_sys/vfs_real.cpp 72 file_sys/vfs_real.cpp
71 file_sys/vfs_real.h 73 file_sys/vfs_real.h
72 file_sys/vfs_static.h 74 file_sys/vfs_static.h
75 file_sys/vfs_types.h
73 file_sys/vfs_vector.cpp 76 file_sys/vfs_vector.cpp
74 file_sys/vfs_vector.h 77 file_sys/vfs_vector.h
75 file_sys/xts_archive.cpp 78 file_sys/xts_archive.cpp
@@ -415,8 +418,8 @@ create_target_directory_groups(core)
415target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 418target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
416target_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)
417if (ENABLE_WEB_SERVICE) 420if (ENABLE_WEB_SERVICE)
418 add_definitions(-DENABLE_WEB_SERVICE) 421 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
419 target_link_libraries(core PUBLIC json-headers web_service) 422 target_link_libraries(core PRIVATE web_service)
420endif() 423endif()
421 424
422if (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 7e978cf7a..4d2491870 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -129,7 +129,7 @@ public:
129}; 129};
130 130
131std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { 131std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
132 auto& current_process = Core::CurrentProcess(); 132 auto* current_process = Core::CurrentProcess();
133 auto** const page_table = current_process->VMManager().page_table.pointers.data(); 133 auto** const page_table = current_process->VMManager().page_table.pointers.data();
134 134
135 Dynarmic::A64::UserConfig config; 135 Dynarmic::A64::UserConfig config;
@@ -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 b6acfb3e4..3c57a62ec 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();
@@ -136,18 +136,19 @@ struct System::Impl {
136 if (virtual_filesystem == nullptr) 136 if (virtual_filesystem == nullptr)
137 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); 137 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
138 138
139 kernel.MakeCurrentProcess(Kernel::Process::Create(kernel, "main")); 139 auto main_process = Kernel::Process::Create(kernel, "main");
140 kernel.MakeCurrentProcess(main_process.get());
140 141
141 cpu_barrier = std::make_shared<CpuBarrier>(); 142 cpu_barrier = std::make_unique<CpuBarrier>();
142 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); 143 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
143 for (std::size_t index = 0; index < cpu_cores.size(); ++index) { 144 for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
144 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);
145 } 146 }
146 147
147 telemetry_session = std::make_unique<Core::TelemetrySession>(); 148 telemetry_session = std::make_unique<Core::TelemetrySession>();
148 service_manager = std::make_shared<Service::SM::ServiceManager>(); 149 service_manager = std::make_shared<Service::SM::ServiceManager>();
149 150
150 Service::Init(service_manager, virtual_filesystem); 151 Service::Init(service_manager, *virtual_filesystem);
151 GDBStub::Init(); 152 GDBStub::Init();
152 153
153 renderer = VideoCore::CreateRenderer(emu_window); 154 renderer = VideoCore::CreateRenderer(emu_window);
@@ -159,12 +160,12 @@ struct System::Impl {
159 160
160 // 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
161 // CPU core 0 is run on the main thread 162 // CPU core 0 is run on the main thread
162 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 163 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
163 if (Settings::values.use_multi_core) { 164 if (Settings::values.use_multi_core) {
164 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) {
165 cpu_core_threads[index] = 166 cpu_core_threads[index] =
166 std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]); 167 std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
167 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();
168 } 169 }
169 } 170 }
170 171
@@ -244,6 +245,7 @@ struct System::Impl {
244 for (auto& cpu_core : cpu_cores) { 245 for (auto& cpu_core : cpu_cores) {
245 cpu_core.reset(); 246 cpu_core.reset();
246 } 247 }
248 cpu_exclusive_monitor.reset();
247 cpu_barrier.reset(); 249 cpu_barrier.reset();
248 250
249 // Shutdown kernel and core timing 251 // Shutdown kernel and core timing
@@ -281,9 +283,9 @@ struct System::Impl {
281 std::unique_ptr<VideoCore::RendererBase> renderer; 283 std::unique_ptr<VideoCore::RendererBase> renderer;
282 std::unique_ptr<Tegra::GPU> gpu_core; 284 std::unique_ptr<Tegra::GPU> gpu_core;
283 std::shared_ptr<Tegra::DebugContext> debug_context; 285 std::shared_ptr<Tegra::DebugContext> debug_context;
284 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor; 286 std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
285 std::shared_ptr<CpuBarrier> cpu_barrier; 287 std::unique_ptr<CpuBarrier> cpu_barrier;
286 std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; 288 std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
287 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;
288 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
289 291
@@ -297,7 +299,7 @@ struct System::Impl {
297 std::string status_details = ""; 299 std::string status_details = "";
298 300
299 /// Map of guest threads to CPU cores 301 /// Map of guest threads to CPU cores
300 std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu; 302 std::map<std::thread::id, Cpu*> thread_to_cpu;
301 303
302 Core::PerfStats perf_stats; 304 Core::PerfStats perf_stats;
303 Core::FrameLimiter frame_limiter; 305 Core::FrameLimiter frame_limiter;
@@ -353,19 +355,22 @@ std::size_t System::CurrentCoreIndex() {
353} 355}
354 356
355Kernel::Scheduler& System::CurrentScheduler() { 357Kernel::Scheduler& System::CurrentScheduler() {
356 return *CurrentCpuCore().Scheduler(); 358 return CurrentCpuCore().Scheduler();
357} 359}
358 360
359const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_index) { 361Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
360 ASSERT(core_index < NUM_CPU_CORES); 362 return CpuCore(core_index).Scheduler();
361 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();
362} 367}
363 368
364Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() { 369Kernel::Process* System::CurrentProcess() {
365 return impl->kernel.CurrentProcess(); 370 return impl->kernel.CurrentProcess();
366} 371}
367 372
368const Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() const { 373const Kernel::Process* System::CurrentProcess() const {
369 return impl->kernel.CurrentProcess(); 374 return impl->kernel.CurrentProcess();
370} 375}
371 376
@@ -379,6 +384,11 @@ Cpu& System::CpuCore(std::size_t core_index) {
379 return *impl->cpu_cores[core_index]; 384 return *impl->cpu_cores[core_index];
380} 385}
381 386
387const Cpu& System::CpuCore(std::size_t core_index) const {
388 ASSERT(core_index < NUM_CPU_CORES);
389 return *impl->cpu_cores[core_index];
390}
391
382ExclusiveMonitor& System::Monitor() { 392ExclusiveMonitor& System::Monitor() {
383 return *impl->cpu_exclusive_monitor; 393 return *impl->cpu_exclusive_monitor;
384} 394}
diff --git a/src/core/core.h b/src/core/core.h
index f9a3e97e3..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,13 +175,16 @@ 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 reference to the current process 183 /// Provides a pointer to the current process
178 Kernel::SharedPtr<Kernel::Process>& CurrentProcess(); 184 Kernel::Process* CurrentProcess();
179 185
180 /// Provides a constant reference to the current process. 186 /// Provides a constant pointer to the current process.
181 const Kernel::SharedPtr<Kernel::Process>& CurrentProcess() const; 187 const Kernel::Process* CurrentProcess() const;
182 188
183 /// Provides a reference to the kernel instance. 189 /// Provides a reference to the kernel instance.
184 Kernel::KernelCore& Kernel(); 190 Kernel::KernelCore& Kernel();
@@ -246,7 +252,7 @@ inline TelemetrySession& Telemetry() {
246 return System::GetInstance().TelemetrySession(); 252 return System::GetInstance().TelemetrySession();
247} 253}
248 254
249inline Kernel::SharedPtr<Kernel::Process>& CurrentProcess() { 255inline Kernel::Process* CurrentProcess() {
250 return System::GetInstance().CurrentProcess(); 256 return System::GetInstance().CurrentProcess();
251} 257}
252 258
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 bf3a70944..fd0786068 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -4,23 +4,56 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7#include <bitset>
8#include <cctype>
7#include <fstream> 9#include <fstream>
8#include <locale> 10#include <locale>
11#include <map>
9#include <sstream> 12#include <sstream>
10#include <string_view> 13#include <string_view>
11#include <tuple> 14#include <tuple>
12#include <vector> 15#include <vector>
16#include <mbedtls/bignum.h>
17#include <mbedtls/cipher.h>
18#include <mbedtls/cmac.h>
19#include <mbedtls/sha256.h>
20#include "common/common_funcs.h"
13#include "common/common_paths.h" 21#include "common/common_paths.h"
14#include "common/file_util.h" 22#include "common/file_util.h"
15#include "common/hex_util.h" 23#include "common/hex_util.h"
16#include "common/logging/log.h" 24#include "common/logging/log.h"
17#include "core/crypto/aes_util.h" 25#include "core/crypto/aes_util.h"
18#include "core/crypto/key_manager.h" 26#include "core/crypto/key_manager.h"
27#include "core/crypto/partition_data_manager.h"
28#include "core/file_sys/content_archive.h"
29#include "core/file_sys/nca_metadata.h"
30#include "core/file_sys/partition_filesystem.h"
31#include "core/file_sys/registered_cache.h"
32#include "core/hle/service/filesystem/filesystem.h"
19#include "core/loader/loader.h" 33#include "core/loader/loader.h"
20#include "core/settings.h" 34#include "core/settings.h"
21 35
22namespace Core::Crypto { 36namespace Core::Crypto {
23 37
38constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
39
40using namespace Common;
41
42const std::array<SHA256Hash, 2> eticket_source_hashes{
43 "B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source
44 "E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source
45};
46
47const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
48 {{S128KeyType::Master, 0}, "master_key_"},
49 {{S128KeyType::Package1, 0}, "package1_key_"},
50 {{S128KeyType::Package2, 0}, "package2_key_"},
51 {{S128KeyType::Titlekek, 0}, "titlekek_"},
52 {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"},
53 {{S128KeyType::Keyblob, 0}, "keyblob_key_"},
54 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
55};
56
24Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) { 57Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) {
25 Key128 out{}; 58 Key128 out{};
26 59
@@ -37,57 +70,136 @@ Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, K
37 return out; 70 return out;
38} 71}
39 72
73Key128 DeriveKeyblobKey(const Key128& sbk, const Key128& tsec, Key128 source) {
74 AESCipher<Key128> sbk_cipher(sbk, Mode::ECB);
75 AESCipher<Key128> tsec_cipher(tsec, Mode::ECB);
76 tsec_cipher.Transcode(source.data(), source.size(), source.data(), Op::Decrypt);
77 sbk_cipher.Transcode(source.data(), source.size(), source.data(), Op::Decrypt);
78 return source;
79}
80
81Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master_source) {
82 Key128 master_root;
83 std::memcpy(master_root.data(), keyblob.data(), sizeof(Key128));
84
85 AESCipher<Key128> master_cipher(master_root, Mode::ECB);
86
87 Key128 master{};
88 master_cipher.Transcode(master_source.data(), master_source.size(), master.data(), Op::Decrypt);
89 return master;
90}
91
92std::array<u8, 144> DecryptKeyblob(const std::array<u8, 176>& encrypted_keyblob,
93 const Key128& key) {
94 std::array<u8, 0x90> keyblob;
95 AESCipher<Key128> cipher(key, Mode::CTR);
96 cipher.SetIV(std::vector<u8>(encrypted_keyblob.data() + 0x10, encrypted_keyblob.data() + 0x20));
97 cipher.Transcode(encrypted_keyblob.data() + 0x20, keyblob.size(), keyblob.data(), Op::Decrypt);
98 return keyblob;
99}
100
101void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
102 const auto kek_generation_source =
103 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
104 const auto key_generation_source =
105 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
106
107 if (HasKey(S128KeyType::Master, crypto_revision)) {
108 for (auto kak_type :
109 {KeyAreaKeyType::Application, KeyAreaKeyType::Ocean, KeyAreaKeyType::System}) {
110 if (HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
111 static_cast<u64>(kak_type))) {
112 const auto source =
113 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
114 static_cast<u64>(kak_type));
115 const auto kek =
116 GenerateKeyEncryptionKey(source, GetKey(S128KeyType::Master, crypto_revision),
117 kek_generation_source, key_generation_source);
118 SetKey(S128KeyType::KeyArea, kek, crypto_revision, static_cast<u64>(kak_type));
119 }
120 }
121
122 AESCipher<Key128> master_cipher(GetKey(S128KeyType::Master, crypto_revision), Mode::ECB);
123 for (auto key_type : {SourceKeyType::Titlekek, SourceKeyType::Package2}) {
124 if (HasKey(S128KeyType::Source, static_cast<u64>(key_type))) {
125 Key128 key{};
126 master_cipher.Transcode(
127 GetKey(S128KeyType::Source, static_cast<u64>(key_type)).data(), key.size(),
128 key.data(), Op::Decrypt);
129 SetKey(key_type == SourceKeyType::Titlekek ? S128KeyType::Titlekek
130 : S128KeyType::Package2,
131 key, crypto_revision);
132 }
133 }
134 }
135}
136
137Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) {
138 AESCipher<Key128> mac_cipher(keyblob_key, Mode::ECB);
139 Key128 mac_key{};
140 mac_cipher.Transcode(mac_source.data(), mac_key.size(), mac_key.data(), Op::Decrypt);
141 return mac_key;
142}
143
40boost::optional<Key128> DeriveSDSeed() { 144boost::optional<Key128> DeriveSDSeed() {
41 const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 145 const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
42 "/system/save/8000000000000043", 146 "/system/save/8000000000000043",
43 "rb+"); 147 "rb+");
44 if (!save_43.IsOpen()) 148 if (!save_43.IsOpen())
45 return boost::none; 149 return boost::none;
150
46 const FileUtil::IOFile sd_private( 151 const FileUtil::IOFile sd_private(
47 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); 152 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
48 if (!sd_private.IsOpen()) 153 if (!sd_private.IsOpen())
49 return boost::none; 154 return boost::none;
50 155
51 sd_private.Seek(0, SEEK_SET);
52 std::array<u8, 0x10> private_seed{}; 156 std::array<u8, 0x10> private_seed{};
53 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()) {
54 return boost::none; 158 return boost::none;
159 }
55 160
56 std::array<u8, 0x10> buffer{}; 161 std::array<u8, 0x10> buffer{};
57 std::size_t offset = 0; 162 std::size_t offset = 0;
58 for (; offset + 0x10 < save_43.GetSize(); ++offset) { 163 for (; offset + 0x10 < save_43.GetSize(); ++offset) {
59 save_43.Seek(offset, SEEK_SET); 164 if (!save_43.Seek(offset, SEEK_SET)) {
165 return boost::none;
166 }
167
60 save_43.ReadBytes(buffer.data(), buffer.size()); 168 save_43.ReadBytes(buffer.data(), buffer.size());
61 if (buffer == private_seed) 169 if (buffer == private_seed) {
62 break; 170 break;
171 }
63 } 172 }
64 173
65 if (offset + 0x10 >= save_43.GetSize()) 174 if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
66 return boost::none; 175 return boost::none;
176 }
67 177
68 Key128 seed{}; 178 Key128 seed{};
69 save_43.Seek(offset + 0x10, SEEK_SET); 179 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
70 save_43.ReadBytes(seed.data(), seed.size()); 180 return boost::none;
181 }
71 return seed; 182 return seed;
72} 183}
73 184
74Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys) { 185Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) {
75 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK))) 186 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek)))
76 return Loader::ResultStatus::ErrorMissingSDKEKSource; 187 return Loader::ResultStatus::ErrorMissingSDKEKSource;
77 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration))) 188 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)))
78 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource; 189 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource;
79 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) 190 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)))
80 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource; 191 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource;
81 192
82 const auto sd_kek_source = 193 const auto sd_kek_source =
83 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK)); 194 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek));
84 const auto aes_kek_gen = 195 const auto aes_kek_gen =
85 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration)); 196 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
86 const auto aes_key_gen = 197 const auto aes_key_gen =
87 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)); 198 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
88 const auto master_00 = keys.GetKey(S128KeyType::Master); 199 const auto master_00 = keys.GetKey(S128KeyType::Master);
89 const auto sd_kek = 200 const auto sd_kek =
90 GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen); 201 GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen);
202 keys.SetKey(S128KeyType::SDKek, sd_kek);
91 203
92 if (!keys.HasKey(S128KeyType::SDSeed)) 204 if (!keys.HasKey(S128KeyType::SDSeed))
93 return Loader::ResultStatus::ErrorMissingSDSeed; 205 return Loader::ResultStatus::ErrorMissingSDSeed;
@@ -118,9 +230,147 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManag
118 return source; ///< Return unaltered source to satisfy output requirement. 230 return source; ///< Return unaltered source to satisfy output requirement.
119 }); 231 });
120 232
233 keys.SetKey(S256KeyType::SDKey, sd_keys[0], static_cast<u64>(SDKeyType::Save));
234 keys.SetKey(S256KeyType::SDKey, sd_keys[1], static_cast<u64>(SDKeyType::NCA));
235
121 return Loader::ResultStatus::Success; 236 return Loader::ResultStatus::Success;
122} 237}
123 238
239std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
240 if (!ticket_save.IsOpen())
241 return {};
242
243 std::vector<u8> buffer(ticket_save.GetSize());
244 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
245 return {};
246 }
247
248 std::vector<TicketRaw> out;
249 u32 magic{};
250 for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) {
251 if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 &&
252 buffer[offset + 3] == 0x0) {
253 out.emplace_back();
254 auto& next = out.back();
255 std::memcpy(&next, buffer.data() + offset, sizeof(TicketRaw));
256 offset += next.size();
257 }
258 }
259
260 return out;
261}
262
263template <size_t size>
264static std::array<u8, size> operator^(const std::array<u8, size>& lhs,
265 const std::array<u8, size>& rhs) {
266 std::array<u8, size> out{};
267 std::transform(lhs.begin(), lhs.end(), rhs.begin(), out.begin(), std::bit_xor<>());
268 return out;
269}
270
271template <size_t target_size, size_t in_size>
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
276 std::array<u8, in_size + 4> seed_exp{};
277 std::memcpy(seed_exp.data(), seed.data(), in_size);
278
279 std::vector<u8> out;
280 size_t i = 0;
281 while (out.size() < target_size) {
282 out.resize(out.size() + 0x20);
283 seed_exp[in_size + 3] = static_cast<u8>(i);
284 mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
285 ++i;
286 }
287
288 std::array<u8, target_size> target;
289 std::memcpy(target.data(), out.data(), target_size);
290 return target;
291}
292
293template <size_t size>
294static boost::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
295 u64 offset = 0;
296 for (size_t i = 0x20; i < data.size() - 0x10; ++i) {
297 if (data[i] == 0x1) {
298 offset = i + 1;
299 break;
300 } else if (data[i] != 0x0) {
301 return boost::none;
302 }
303 }
304
305 return offset;
306}
307
308boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
309 const RSAKeyPair<2048>& key) {
310 u32 cert_authority;
311 std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
312 if (cert_authority == 0)
313 return boost::none;
314 if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {
315 LOG_INFO(Crypto,
316 "Attempting to parse ticket with non-standard certificate authority {:08X}.",
317 cert_authority);
318 }
319
320 Key128 rights_id;
321 std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
322
323 if (rights_id == Key128{})
324 return boost::none;
325
326 Key128 key_temp{};
327
328 if (!std::any_of(ticket.begin() + 0x190, ticket.begin() + 0x280, [](u8 b) { return b != 0; })) {
329 std::memcpy(key_temp.data(), ticket.data() + 0x180, key_temp.size());
330 return std::make_pair(rights_id, key_temp);
331 }
332
333 mbedtls_mpi D; // RSA Private Exponent
334 mbedtls_mpi N; // RSA Modulus
335 mbedtls_mpi S; // Input
336 mbedtls_mpi M; // Output
337
338 mbedtls_mpi_init(&D);
339 mbedtls_mpi_init(&N);
340 mbedtls_mpi_init(&S);
341 mbedtls_mpi_init(&M);
342
343 mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size());
344 mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size());
345 mbedtls_mpi_read_binary(&S, ticket.data() + 0x180, 0x100);
346
347 mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr);
348
349 std::array<u8, 0x100> rsa_step;
350 mbedtls_mpi_write_binary(&M, rsa_step.data(), rsa_step.size());
351
352 u8 m_0 = rsa_step[0];
353 std::array<u8, 0x20> m_1;
354 std::memcpy(m_1.data(), rsa_step.data() + 0x01, m_1.size());
355 std::array<u8, 0xDF> m_2;
356 std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
357
358 if (m_0 != 0)
359 return boost::none;
360
361 m_1 = m_1 ^ MGF1<0x20>(m_2);
362 m_2 = m_2 ^ MGF1<0xDF>(m_1);
363
364 const auto offset = FindTicketOffset(m_2);
365 if (offset == boost::none)
366 return boost::none;
367 ASSERT(offset.get() > 0);
368
369 std::memcpy(key_temp.data(), m_2.data() + offset.get(), key_temp.size());
370
371 return std::make_pair(rights_id, key_temp);
372}
373
124KeyManager::KeyManager() { 374KeyManager::KeyManager() {
125 // Initialize keys 375 // Initialize keys
126 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); 376 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
@@ -137,6 +387,15 @@ KeyManager::KeyManager() {
137 387
138 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true); 388 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
139 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true); 389 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true);
390 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "console.keys", false);
391 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "console.keys_autogenerated", false);
392}
393
394static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
395 if (base.size() < begin + length)
396 return false;
397 return std::all_of(base.begin() + begin, base.begin() + begin + length,
398 [](u8 c) { return std::isdigit(c); });
140} 399}
141 400
142void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { 401void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
@@ -158,6 +417,9 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
158 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end()); 417 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
159 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end()); 418 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
160 419
420 if (out[0].compare(0, 1, "#") == 0)
421 continue;
422
161 if (is_title_keys) { 423 if (is_title_keys) {
162 auto rights_id_raw = Common::HexStringToArray<16>(out[0]); 424 auto rights_id_raw = Common::HexStringToArray<16>(out[0]);
163 u128 rights_id{}; 425 u128 rights_id{};
@@ -174,6 +436,50 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
174 const auto index = s256_file_id.at(out[0]); 436 const auto index = s256_file_id.at(out[0]);
175 Key256 key = Common::HexStringToArray<32>(out[1]); 437 Key256 key = Common::HexStringToArray<32>(out[1]);
176 s256_keys[{index.type, index.field1, index.field2}] = key; 438 s256_keys[{index.type, index.field1, index.field2}] = key;
439 } else if (out[0].compare(0, 8, "keyblob_") == 0 &&
440 out[0].compare(0, 9, "keyblob_k") != 0) {
441 if (!ValidCryptoRevisionString(out[0], 8, 2))
442 continue;
443
444 const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16);
445 keyblobs[index] = Common::HexStringToArray<0x90>(out[1]);
446 } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) {
447 if (!ValidCryptoRevisionString(out[0], 18, 2))
448 continue;
449
450 const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16);
451 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
452 } else {
453 for (const auto& kv : KEYS_VARIABLE_LENGTH) {
454 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2))
455 continue;
456 if (out[0].compare(0, kv.second.size(), kv.second) == 0) {
457 const auto index =
458 std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16);
459 const auto sub = kv.first.second;
460 if (sub == 0) {
461 s128_keys[{kv.first.first, index, 0}] =
462 Common::HexStringToArray<16>(out[1]);
463 } else {
464 s128_keys[{kv.first.first, kv.first.second, index}] =
465 Common::HexStringToArray<16>(out[1]);
466 }
467
468 break;
469 }
470 }
471
472 static constexpr std::array<const char*, 3> kak_names = {
473 "key_area_key_application_", "key_area_key_ocean_", "key_area_key_system_"};
474 for (size_t j = 0; j < kak_names.size(); ++j) {
475 const auto& match = kak_names[j];
476 if (out[0].compare(0, std::strlen(match), match) == 0) {
477 const auto index =
478 std::stoul(out[0].substr(std::strlen(match), 2), nullptr, 16);
479 s128_keys[{S128KeyType::KeyArea, index, j}] =
480 Common::HexStringToArray<16>(out[1]);
481 }
482 }
177 } 483 }
178 } 484 }
179 } 485 }
@@ -187,6 +493,28 @@ void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string&
187 LoadFromFile(dir2 + DIR_SEP + filename, title); 493 LoadFromFile(dir2 + DIR_SEP + filename, title);
188} 494}
189 495
496bool KeyManager::BaseDeriveNecessary() const {
497 const auto check_key_existence = [this](auto key_type, u64 index1 = 0, u64 index2 = 0) {
498 return !HasKey(key_type, index1, index2);
499 };
500
501 if (check_key_existence(S256KeyType::Header))
502 return true;
503
504 for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) {
505 if (check_key_existence(S128KeyType::Master, i) ||
506 check_key_existence(S128KeyType::KeyArea, i,
507 static_cast<u64>(KeyAreaKeyType::Application)) ||
508 check_key_existence(S128KeyType::KeyArea, i, static_cast<u64>(KeyAreaKeyType::Ocean)) ||
509 check_key_existence(S128KeyType::KeyArea, i,
510 static_cast<u64>(KeyAreaKeyType::System)) ||
511 check_key_existence(S128KeyType::Titlekek, i))
512 return true;
513 }
514
515 return false;
516}
517
190bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const { 518bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const {
191 return s128_keys.find({id, field1, field2}) != s128_keys.end(); 519 return s128_keys.find({id, field1, field2}) != s128_keys.end();
192} 520}
@@ -207,13 +535,30 @@ Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
207 return s256_keys.at({id, field1, field2}); 535 return s256_keys.at({id, field1, field2});
208} 536}
209 537
210template <std::size_t Size> 538Key256 KeyManager::GetBISKey(u8 partition_id) const {
211void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname, 539 Key256 out{};
540
541 for (const auto& bis_type : {BISKeyType::Crypto, BISKeyType::Tweak}) {
542 if (HasKey(S128KeyType::BIS, partition_id, static_cast<u64>(bis_type))) {
543 std::memcpy(
544 out.data() + sizeof(Key128) * static_cast<u64>(bis_type),
545 s128_keys.at({S128KeyType::BIS, partition_id, static_cast<u64>(bis_type)}).data(),
546 sizeof(Key128));
547 }
548 }
549
550 return out;
551}
552
553template <size_t Size>
554void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
212 const std::array<u8, Size>& key) { 555 const std::array<u8, Size>& key) {
213 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 556 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
214 std::string filename = "title.keys_autogenerated"; 557 std::string filename = "title.keys_autogenerated";
215 if (!title_key) 558 if (category == KeyCategory::Standard)
216 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated"; 559 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
560 else if (category == KeyCategory::Console)
561 filename = "console.keys_autogenerated";
217 const auto add_info_text = !FileUtil::Exists(yuzu_keys_dir + DIR_SEP + filename); 562 const auto add_info_text = !FileUtil::Exists(yuzu_keys_dir + DIR_SEP + filename);
218 FileUtil::CreateFullPath(yuzu_keys_dir + DIR_SEP + filename); 563 FileUtil::CreateFullPath(yuzu_keys_dir + DIR_SEP + filename);
219 std::ofstream file(yuzu_keys_dir + DIR_SEP + filename, std::ios::app); 564 std::ofstream file(yuzu_keys_dir + DIR_SEP + filename, std::ios::app);
@@ -227,7 +572,7 @@ void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
227 } 572 }
228 573
229 file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key)); 574 file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key));
230 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, title_key); 575 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, category == KeyCategory::Title);
231} 576}
232 577
233void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { 578void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
@@ -237,8 +582,15 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
237 Key128 rights_id; 582 Key128 rights_id;
238 std::memcpy(rights_id.data(), &field2, sizeof(u64)); 583 std::memcpy(rights_id.data(), &field2, sizeof(u64));
239 std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64)); 584 std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
240 WriteKeyToFile(true, Common::HexArrayToString(rights_id), key); 585 WriteKeyToFile(KeyCategory::Title, Common::HexArrayToString(rights_id), key);
241 } 586 }
587
588 auto category = KeyCategory::Standard;
589 if (id == S128KeyType::Keyblob || id == S128KeyType::KeyblobMAC || id == S128KeyType::TSEC ||
590 id == S128KeyType::SecureBoot || id == S128KeyType::SDSeed || id == S128KeyType::BIS) {
591 category = KeyCategory::Console;
592 }
593
242 const auto iter2 = std::find_if( 594 const auto iter2 = std::find_if(
243 s128_file_id.begin(), s128_file_id.end(), 595 s128_file_id.begin(), s128_file_id.end(),
244 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) { 596 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
@@ -246,7 +598,30 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
246 std::tie(id, field1, field2); 598 std::tie(id, field1, field2);
247 }); 599 });
248 if (iter2 != s128_file_id.end()) 600 if (iter2 != s128_file_id.end())
249 WriteKeyToFile(false, iter2->first, key); 601 WriteKeyToFile(category, iter2->first, key);
602
603 // Variable cases
604 if (id == S128KeyType::KeyArea) {
605 static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}",
606 "key_area_key_ocean_{:02X}",
607 "key_area_key_system_{:02X}"};
608 WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key);
609 } else if (id == S128KeyType::Master) {
610 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
611 } else if (id == S128KeyType::Package1) {
612 WriteKeyToFile(category, fmt::format("package1_key_{:02X}", field1), key);
613 } else if (id == S128KeyType::Package2) {
614 WriteKeyToFile(category, fmt::format("package2_key_{:02X}", field1), key);
615 } else if (id == S128KeyType::Titlekek) {
616 WriteKeyToFile(category, fmt::format("titlekek_{:02X}", field1), key);
617 } else if (id == S128KeyType::Keyblob) {
618 WriteKeyToFile(category, fmt::format("keyblob_key_{:02X}", field1), key);
619 } else if (id == S128KeyType::KeyblobMAC) {
620 WriteKeyToFile(category, fmt::format("keyblob_mac_key_{:02X}", field1), key);
621 } else if (id == S128KeyType::Source && field1 == static_cast<u64>(SourceKeyType::Keyblob)) {
622 WriteKeyToFile(category, fmt::format("keyblob_key_source_{:02X}", field2), key);
623 }
624
250 s128_keys[{id, field1, field2}] = key; 625 s128_keys[{id, field1, field2}] = key;
251} 626}
252 627
@@ -260,7 +635,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
260 std::tie(id, field1, field2); 635 std::tie(id, field1, field2);
261 }); 636 });
262 if (iter != s256_file_id.end()) 637 if (iter != s256_file_id.end())
263 WriteKeyToFile(false, iter->first, key); 638 WriteKeyToFile(KeyCategory::Standard, iter->first, key);
264 s256_keys[{id, field1, field2}] = key; 639 s256_keys[{id, field1, field2}] = key;
265} 640}
266 641
@@ -290,59 +665,388 @@ void KeyManager::DeriveSDSeedLazy() {
290 SetKey(S128KeyType::SDSeed, res.get()); 665 SetKey(S128KeyType::SDSeed, res.get());
291} 666}
292 667
668static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
669 Key128 out{};
670
671 mbedtls_cipher_cmac(mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB), key.data(),
672 key.size() * 8, source, size, out.data());
673 return out;
674}
675
676void KeyManager::DeriveBase() {
677 if (!BaseDeriveNecessary())
678 return;
679
680 if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC))
681 return;
682
683 const auto has_bis = [this](u64 id) {
684 return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) &&
685 HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Tweak));
686 };
687
688 const auto copy_bis = [this](u64 id_from, u64 id_to) {
689 SetKey(S128KeyType::BIS,
690 GetKey(S128KeyType::BIS, id_from, static_cast<u64>(BISKeyType::Crypto)), id_to,
691 static_cast<u64>(BISKeyType::Crypto));
692
693 SetKey(S128KeyType::BIS,
694 GetKey(S128KeyType::BIS, id_from, static_cast<u64>(BISKeyType::Tweak)), id_to,
695 static_cast<u64>(BISKeyType::Tweak));
696 };
697
698 if (has_bis(2) && !has_bis(3))
699 copy_bis(2, 3);
700 else if (has_bis(3) && !has_bis(2))
701 copy_bis(3, 2);
702
703 std::bitset<32> revisions(0xFFFFFFFF);
704 for (size_t i = 0; i < revisions.size(); ++i) {
705 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob), i) ||
706 encrypted_keyblobs[i] == std::array<u8, 0xB0>{}) {
707 revisions.reset(i);
708 }
709 }
710
711 if (!revisions.any())
712 return;
713
714 const auto sbk = GetKey(S128KeyType::SecureBoot);
715 const auto tsec = GetKey(S128KeyType::TSEC);
716 const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master));
717
718 for (size_t i = 0; i < revisions.size(); ++i) {
719 if (!revisions[i])
720 continue;
721
722 // Derive keyblob key
723 const auto key = DeriveKeyblobKey(
724 sbk, tsec, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob), i));
725
726 SetKey(S128KeyType::Keyblob, key, i);
727
728 // Derive keyblob MAC key
729 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)))
730 continue;
731
732 const auto mac_key = DeriveKeyblobMACKey(
733 key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)));
734 SetKey(S128KeyType::KeyblobMAC, mac_key, i);
735
736 Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key);
737 if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0)
738 continue;
739
740 // Decrypt keyblob
741 if (keyblobs[i] == std::array<u8, 0x90>{}) {
742 keyblobs[i] = DecryptKeyblob(encrypted_keyblobs[i], key);
743 WriteKeyToFile<0x90>(KeyCategory::Console, fmt::format("keyblob_{:02X}", i),
744 keyblobs[i]);
745 }
746
747 Key128 package1;
748 std::memcpy(package1.data(), keyblobs[i].data() + 0x80, sizeof(Key128));
749 SetKey(S128KeyType::Package1, package1, i);
750
751 // Derive master key
752 if (HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master))) {
753 SetKey(S128KeyType::Master,
754 DeriveMasterKey(keyblobs[i], GetKey(S128KeyType::Source,
755 static_cast<u64>(SourceKeyType::Master))),
756 i);
757 }
758 }
759
760 revisions.set();
761 for (size_t i = 0; i < revisions.size(); ++i) {
762 if (!HasKey(S128KeyType::Master, i))
763 revisions.reset(i);
764 }
765
766 if (!revisions.any())
767 return;
768
769 for (size_t i = 0; i < revisions.size(); ++i) {
770 if (!revisions[i])
771 continue;
772
773 // Derive general purpose keys
774 DeriveGeneralPurposeKeys(i);
775 }
776
777 if (HasKey(S128KeyType::Master, 0) &&
778 HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)) &&
779 HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)) &&
780 HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek)) &&
781 HasKey(S256KeyType::HeaderSource)) {
782 const auto header_kek = GenerateKeyEncryptionKey(
783 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek)),
784 GetKey(S128KeyType::Master, 0),
785 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)),
786 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)));
787 SetKey(S128KeyType::HeaderKek, header_kek);
788
789 AESCipher<Key128> header_cipher(header_kek, Mode::ECB);
790 Key256 out = GetKey(S256KeyType::HeaderSource);
791 header_cipher.Transcode(out.data(), out.size(), out.data(), Op::Decrypt);
792 SetKey(S256KeyType::Header, out);
793 }
794}
795
796void KeyManager::DeriveETicket(PartitionDataManager& data) {
797 // ETicket keys
798 const auto es = Service::FileSystem::GetUnionContents()->GetEntry(
799 0x0100000000000033, FileSys::ContentRecordType::Program);
800
801 if (es == nullptr)
802 return;
803
804 const auto exefs = es->GetExeFS();
805 if (exefs == nullptr)
806 return;
807
808 const auto main = exefs->GetFile("main");
809 if (main == nullptr)
810 return;
811
812 const auto bytes = main->ReadAllBytes();
813
814 const auto eticket_kek = FindKeyFromHex16(bytes, eticket_source_hashes[0]);
815 const auto eticket_kekek = FindKeyFromHex16(bytes, eticket_source_hashes[1]);
816
817 const auto seed3 = data.GetRSAKekSeed3();
818 const auto mask0 = data.GetRSAKekMask0();
819
820 if (eticket_kek != Key128{})
821 SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek));
822 if (eticket_kekek != Key128{}) {
823 SetKey(S128KeyType::Source, eticket_kekek,
824 static_cast<size_t>(SourceKeyType::ETicketKekek));
825 }
826 if (seed3 != Key128{})
827 SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3));
828 if (mask0 != Key128{})
829 SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0));
830 if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} ||
831 mask0 == Key128{}) {
832 return;
833 }
834
835 Key128 rsa_oaep_kek{};
836 std::transform(seed3.begin(), seed3.end(), mask0.begin(), rsa_oaep_kek.begin(),
837 std::bit_xor<>());
838
839 if (rsa_oaep_kek == Key128{})
840 return;
841
842 SetKey(S128KeyType::Source, rsa_oaep_kek,
843 static_cast<u64>(SourceKeyType::RSAOaepKekGeneration));
844
845 Key128 temp_kek{};
846 Key128 temp_kekek{};
847 Key128 eticket_final{};
848
849 // Derive ETicket RSA Kek
850 AESCipher<Key128> es_master(GetKey(S128KeyType::Master), Mode::ECB);
851 es_master.Transcode(rsa_oaep_kek.data(), rsa_oaep_kek.size(), temp_kek.data(), Op::Decrypt);
852 AESCipher<Key128> es_kekek(temp_kek, Mode::ECB);
853 es_kekek.Transcode(eticket_kekek.data(), eticket_kekek.size(), temp_kekek.data(), Op::Decrypt);
854 AESCipher<Key128> es_kek(temp_kekek, Mode::ECB);
855 es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt);
856
857 if (eticket_final == Key128{})
858 return;
859
860 SetKey(S128KeyType::ETicketRSAKek, eticket_final);
861
862 // Titlekeys
863 data.DecryptProdInfo(GetBISKey(0));
864
865 const auto eticket_extended_kek = data.GetETicketExtendedKek();
866
867 std::vector<u8> extended_iv(0x10);
868 std::memcpy(extended_iv.data(), eticket_extended_kek.data(), extended_iv.size());
869 std::array<u8, 0x230> extended_dec{};
870 AESCipher<Key128> rsa_1(eticket_final, Mode::CTR);
871 rsa_1.SetIV(extended_iv);
872 rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10,
873 extended_dec.data(), Op::Decrypt);
874
875 RSAKeyPair<2048> rsa_key{};
876 std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size());
877 std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size());
878 std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size());
879
880 const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
881 "/system/save/80000000000000e1",
882 "rb+");
883 const FileUtil::IOFile save2(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
884 "/system/save/80000000000000e2",
885 "rb+");
886
887 const auto blob2 = GetTicketblob(save2);
888 auto res = GetTicketblob(save1);
889 res.insert(res.end(), blob2.begin(), blob2.end());
890
891 for (const auto& raw : res) {
892 const auto pair = ParseTicket(raw, rsa_key);
893 if (pair == boost::none)
894 continue;
895 const auto& [rid, key] = pair.value();
896 u128 rights_id;
897 std::memcpy(rights_id.data(), rid.data(), rid.size());
898 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
899 }
900}
901
902void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
903 if (key == Key128{})
904 return;
905 SetKey(id, key, field1, field2);
906}
907
908void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) {
909 if (key == Key256{})
910 return;
911 SetKey(id, key, field1, field2);
912}
913
914void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
915 if (!BaseDeriveNecessary())
916 return;
917
918 if (!data.HasBoot0())
919 return;
920
921 for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) {
922 if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{})
923 continue;
924 encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i);
925 WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i),
926 encrypted_keyblobs[i]);
927 }
928
929 SetKeyWrapped(S128KeyType::Source, data.GetPackage2KeySource(),
930 static_cast<u64>(SourceKeyType::Package2));
931 SetKeyWrapped(S128KeyType::Source, data.GetAESKekGenerationSource(),
932 static_cast<u64>(SourceKeyType::AESKekGeneration));
933 SetKeyWrapped(S128KeyType::Source, data.GetTitlekekSource(),
934 static_cast<u64>(SourceKeyType::Titlekek));
935 SetKeyWrapped(S128KeyType::Source, data.GetMasterKeySource(),
936 static_cast<u64>(SourceKeyType::Master));
937 SetKeyWrapped(S128KeyType::Source, data.GetKeyblobMACKeySource(),
938 static_cast<u64>(SourceKeyType::KeyblobMAC));
939
940 for (size_t i = 0; i < PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH; ++i) {
941 SetKeyWrapped(S128KeyType::Source, data.GetKeyblobKeySource(i),
942 static_cast<u64>(SourceKeyType::Keyblob), i);
943 }
944
945 if (data.HasFuses())
946 SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey());
947
948 DeriveBase();
949
950 Key128 latest_master{};
951 for (s8 i = 0x1F; i >= 0; --i) {
952 if (GetKey(S128KeyType::Master, static_cast<u8>(i)) != Key128{}) {
953 latest_master = GetKey(S128KeyType::Master, static_cast<u8>(i));
954 break;
955 }
956 }
957
958 const auto masters = data.GetTZMasterKeys(latest_master);
959 for (size_t i = 0; i < masters.size(); ++i) {
960 if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i))
961 SetKey(S128KeyType::Master, masters[i], i);
962 }
963
964 DeriveBase();
965
966 if (!data.HasPackage2())
967 return;
968
969 std::array<Key128, 0x20> package2_keys{};
970 for (size_t i = 0; i < package2_keys.size(); ++i) {
971 if (HasKey(S128KeyType::Package2, i))
972 package2_keys[i] = GetKey(S128KeyType::Package2, i);
973 }
974 data.DecryptPackage2(package2_keys, Package2Type::NormalMain);
975
976 SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeyApplicationSource(),
977 static_cast<u64>(SourceKeyType::KeyAreaKey),
978 static_cast<u64>(KeyAreaKeyType::Application));
979 SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeyOceanSource(),
980 static_cast<u64>(SourceKeyType::KeyAreaKey),
981 static_cast<u64>(KeyAreaKeyType::Ocean));
982 SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeySystemSource(),
983 static_cast<u64>(SourceKeyType::KeyAreaKey),
984 static_cast<u64>(KeyAreaKeyType::System));
985 SetKeyWrapped(S128KeyType::Source, data.GetSDKekSource(),
986 static_cast<u64>(SourceKeyType::SDKek));
987 SetKeyWrapped(S256KeyType::SDKeySource, data.GetSDSaveKeySource(),
988 static_cast<u64>(SDKeyType::Save));
989 SetKeyWrapped(S256KeyType::SDKeySource, data.GetSDNCAKeySource(),
990 static_cast<u64>(SDKeyType::NCA));
991 SetKeyWrapped(S128KeyType::Source, data.GetHeaderKekSource(),
992 static_cast<u64>(SourceKeyType::HeaderKek));
993 SetKeyWrapped(S256KeyType::HeaderSource, data.GetHeaderKeySource());
994 SetKeyWrapped(S128KeyType::Source, data.GetAESKeyGenerationSource(),
995 static_cast<u64>(SourceKeyType::AESKeyGeneration));
996
997 DeriveBase();
998}
999
293const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = { 1000const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
294 {"master_key_00", {S128KeyType::Master, 0, 0}},
295 {"master_key_01", {S128KeyType::Master, 1, 0}},
296 {"master_key_02", {S128KeyType::Master, 2, 0}},
297 {"master_key_03", {S128KeyType::Master, 3, 0}},
298 {"master_key_04", {S128KeyType::Master, 4, 0}},
299 {"package1_key_00", {S128KeyType::Package1, 0, 0}},
300 {"package1_key_01", {S128KeyType::Package1, 1, 0}},
301 {"package1_key_02", {S128KeyType::Package1, 2, 0}},
302 {"package1_key_03", {S128KeyType::Package1, 3, 0}},
303 {"package1_key_04", {S128KeyType::Package1, 4, 0}},
304 {"package2_key_00", {S128KeyType::Package2, 0, 0}},
305 {"package2_key_01", {S128KeyType::Package2, 1, 0}},
306 {"package2_key_02", {S128KeyType::Package2, 2, 0}},
307 {"package2_key_03", {S128KeyType::Package2, 3, 0}},
308 {"package2_key_04", {S128KeyType::Package2, 4, 0}},
309 {"titlekek_00", {S128KeyType::Titlekek, 0, 0}},
310 {"titlekek_01", {S128KeyType::Titlekek, 1, 0}},
311 {"titlekek_02", {S128KeyType::Titlekek, 2, 0}},
312 {"titlekek_03", {S128KeyType::Titlekek, 3, 0}},
313 {"titlekek_04", {S128KeyType::Titlekek, 4, 0}},
314 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, 1001 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
315 {"key_area_key_application_00", 1002 {"eticket_rsa_kek_source",
316 {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Application)}}, 1003 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
317 {"key_area_key_application_01", 1004 {"eticket_rsa_kekek_source",
318 {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Application)}}, 1005 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
319 {"key_area_key_application_02", 1006 {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
320 {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Application)}}, 1007 {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
321 {"key_area_key_application_03", 1008 {"rsa_oaep_kek_generation_source",
322 {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Application)}}, 1009 {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
323 {"key_area_key_application_04", 1010 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
324 {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Application)}},
325 {"key_area_key_ocean_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}},
326 {"key_area_key_ocean_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}},
327 {"key_area_key_ocean_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}},
328 {"key_area_key_ocean_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}},
329 {"key_area_key_ocean_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}},
330 {"key_area_key_system_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::System)}},
331 {"key_area_key_system_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::System)}},
332 {"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}},
333 {"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}},
334 {"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}},
335 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK), 0}},
336 {"aes_kek_generation_source", 1011 {"aes_kek_generation_source",
337 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration), 0}}, 1012 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
338 {"aes_key_generation_source", 1013 {"aes_key_generation_source",
339 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}}, 1014 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
1015 {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
1016 {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
1017 {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
1018 {"key_area_key_application_source",
1019 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1020 static_cast<u64>(KeyAreaKeyType::Application)}},
1021 {"key_area_key_ocean_source",
1022 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1023 static_cast<u64>(KeyAreaKeyType::Ocean)}},
1024 {"key_area_key_system_source",
1025 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1026 static_cast<u64>(KeyAreaKeyType::System)}},
1027 {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
1028 {"keyblob_mac_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)}},
1029 {"tsec_key", {S128KeyType::TSEC, 0, 0}},
1030 {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
340 {"sd_seed", {S128KeyType::SDSeed, 0, 0}}, 1031 {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
1032 {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
1033 {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
1034 {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
1035 {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
1036 {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
1037 {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
1038 {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
1039 {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
1040 {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
1041 {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
341}; 1042};
342 1043
343const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = { 1044const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
344 {"header_key", {S256KeyType::Header, 0, 0}}, 1045 {"header_key", {S256KeyType::Header, 0, 0}},
345 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}}, 1046 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
346 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}}, 1047 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
1048 {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
1049 {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
1050 {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
347}; 1051};
348} // namespace Core::Crypto 1052} // namespace Core::Crypto
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 978eec8dc..cccb3c0ae 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -5,11 +5,18 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <map>
8#include <string> 9#include <string>
9#include <boost/container/flat_map.hpp> 10#include <boost/container/flat_map.hpp>
10#include <boost/optional.hpp> 11#include <boost/optional.hpp>
11#include <fmt/format.h> 12#include <fmt/format.h>
12#include "common/common_types.h" 13#include "common/common_types.h"
14#include "core/crypto/partition_data_manager.h"
15#include "core/file_sys/vfs_types.h"
16
17namespace FileUtil {
18class IOFile;
19}
13 20
14namespace Loader { 21namespace Loader {
15enum class ResultStatus : u16; 22enum class ResultStatus : u16;
@@ -22,13 +29,30 @@ constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
22using Key128 = std::array<u8, 0x10>; 29using Key128 = std::array<u8, 0x10>;
23using Key256 = std::array<u8, 0x20>; 30using Key256 = std::array<u8, 0x20>;
24using SHA256Hash = std::array<u8, 0x20>; 31using SHA256Hash = std::array<u8, 0x20>;
32using TicketRaw = std::array<u8, 0x400>;
25 33
26static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); 34static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
27static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big."); 35static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big.");
36
37template <size_t bit_size, size_t byte_size = (bit_size >> 3)>
38struct RSAKeyPair {
39 std::array<u8, byte_size> encryption_key;
40 std::array<u8, byte_size> decryption_key;
41 std::array<u8, byte_size> modulus;
42 std::array<u8, 4> exponent;
43};
44
45enum class KeyCategory : u8 {
46 Standard,
47 Title,
48 Console,
49};
28 50
29enum class S256KeyType : u64 { 51enum class S256KeyType : u64 {
30 Header, // 52 SDKey, // f1=SDKeyType
31 SDKeySource, // f1=SDKeyType 53 Header, //
54 SDKeySource, // f1=SDKeyType
55 HeaderSource, //
32}; 56};
33 57
34enum class S128KeyType : u64 { 58enum class S128KeyType : u64 {
@@ -41,6 +65,14 @@ enum class S128KeyType : u64 {
41 SDSeed, // 65 SDSeed, //
42 Titlekey, // f1=rights id LSB f2=rights id MSB 66 Titlekey, // f1=rights id LSB f2=rights id MSB
43 Source, // f1=source type, f2= sub id 67 Source, // f1=source type, f2= sub id
68 Keyblob, // f1=crypto revision
69 KeyblobMAC, // f1=crypto revision
70 TSEC, //
71 SecureBoot, //
72 BIS, // f1=partition (0-3), f2=type {crypt, tweak}
73 HeaderKek, //
74 SDKek, //
75 RSAKek, //
44}; 76};
45 77
46enum class KeyAreaKeyType : u8 { 78enum class KeyAreaKeyType : u8 {
@@ -50,9 +82,19 @@ enum class KeyAreaKeyType : u8 {
50}; 82};
51 83
52enum class SourceKeyType : u8 { 84enum class SourceKeyType : u8 {
53 SDKEK, 85 SDKek, //
54 AESKEKGeneration, 86 AESKekGeneration, //
55 AESKeyGeneration, 87 AESKeyGeneration, //
88 RSAOaepKekGeneration, //
89 Master, //
90 Keyblob, // f2=crypto revision
91 KeyAreaKey, // f2=KeyAreaKeyType
92 Titlekek, //
93 Package2, //
94 HeaderKek, //
95 KeyblobMAC, //
96 ETicketKek, //
97 ETicketKekek, //
56}; 98};
57 99
58enum class SDKeyType : u8 { 100enum class SDKeyType : u8 {
@@ -60,6 +102,16 @@ enum class SDKeyType : u8 {
60 NCA, 102 NCA,
61}; 103};
62 104
105enum class BISKeyType : u8 {
106 Crypto,
107 Tweak,
108};
109
110enum class RSAKekType : u8 {
111 Mask0,
112 Seed3,
113};
114
63template <typename KeyType> 115template <typename KeyType>
64struct KeyIndex { 116struct KeyIndex {
65 KeyType type; 117 KeyType type;
@@ -91,6 +143,8 @@ public:
91 Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; 143 Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
92 Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; 144 Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
93 145
146 Key256 GetBISKey(u8 partition_id) const;
147
94 void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); 148 void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
95 void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); 149 void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
96 150
@@ -100,23 +154,51 @@ public:
100 // 8*43 and the private file to exist. 154 // 8*43 and the private file to exist.
101 void DeriveSDSeedLazy(); 155 void DeriveSDSeedLazy();
102 156
157 bool BaseDeriveNecessary() const;
158 void DeriveBase();
159 void DeriveETicket(PartitionDataManager& data);
160
161 void PopulateFromPartitionData(PartitionDataManager& data);
162
103private: 163private:
104 boost::container::flat_map<KeyIndex<S128KeyType>, Key128> s128_keys; 164 std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
105 boost::container::flat_map<KeyIndex<S256KeyType>, Key256> s256_keys; 165 std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
166
167 std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{};
168 std::array<std::array<u8, 0x90>, 0x20> keyblobs{};
106 169
107 bool dev_mode; 170 bool dev_mode;
108 void LoadFromFile(const std::string& filename, bool is_title_keys); 171 void LoadFromFile(const std::string& filename, bool is_title_keys);
109 void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, 172 void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
110 const std::string& filename, bool title); 173 const std::string& filename, bool title);
111 template <std::size_t Size> 174 template <size_t Size>
112 void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key); 175 void WriteKeyToFile(KeyCategory category, std::string_view keyname,
176 const std::array<u8, Size>& key);
177
178 void DeriveGeneralPurposeKeys(std::size_t crypto_revision);
179
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);
113 182
114 static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id; 183 static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
115 static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id; 184 static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
116}; 185};
117 186
118Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); 187Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
188Key128 DeriveKeyblobKey(const Key128& sbk, const Key128& tsec, Key128 source);
189Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source);
190Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master_source);
191std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblob,
192 const Key128& key);
193
119boost::optional<Key128> DeriveSDSeed(); 194boost::optional<Key128> DeriveSDSeed();
120Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys); 195Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
196
197std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save);
198
199// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset
200// 0x140-0x144 is zero)
201boost::optional<std::pair<Key128, Key128>> ParseTicket(
202 const TicketRaw& ticket, const RSAKeyPair<2048>& eticket_extended_key);
121 203
122} // namespace Core::Crypto 204} // namespace Core::Crypto
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
new file mode 100644
index 000000000..25cee1f3a
--- /dev/null
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -0,0 +1,593 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5// NOTE TO FUTURE MAINTAINERS:
6// When a new version of switch cryptography is released,
7// hash the new keyblob source and master key and add the hashes to
8// the arrays below.
9
10#include <algorithm>
11#include <array>
12#include <cctype>
13#include <cstring>
14#include <mbedtls/sha256.h>
15#include "common/assert.h"
16#include "common/common_funcs.h"
17#include "common/common_types.h"
18#include "common/hex_util.h"
19#include "common/logging/log.h"
20#include "common/string_util.h"
21#include "common/swap.h"
22#include "core/crypto/key_manager.h"
23#include "core/crypto/partition_data_manager.h"
24#include "core/crypto/xts_encryption_layer.h"
25#include "core/file_sys/vfs.h"
26#include "core/file_sys/vfs_offset.h"
27
28using namespace Common;
29
30namespace Core::Crypto {
31
32struct Package2Header {
33 std::array<u8, 0x100> signature;
34 Key128 header_ctr;
35 std::array<Key128, 4> section_ctr;
36 u32_le magic;
37 u32_le base_offset;
38 INSERT_PADDING_BYTES(4);
39 u8 version_max;
40 u8 version_min;
41 INSERT_PADDING_BYTES(2);
42 std::array<u32_le, 4> section_size;
43 std::array<u32_le, 4> section_offset;
44 std::array<SHA256Hash, 4> section_hash;
45};
46static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
47
48struct INIHeader {
49 u32_le magic;
50 u32_le size;
51 u32_le process_count;
52 INSERT_PADDING_BYTES(4);
53};
54static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
55
56struct SectionHeader {
57 u32_le offset;
58 u32_le size_decompressed;
59 u32_le size_compressed;
60 u32_le attribute;
61};
62static_assert(sizeof(SectionHeader) == 0x10, "SectionHeader has incorrect size.");
63
64struct KIPHeader {
65 u32_le magic;
66 std::array<char, 12> name;
67 u64_le title_id;
68 u32_le category;
69 u8 priority;
70 u8 core;
71 INSERT_PADDING_BYTES(1);
72 u8 flags;
73 std::array<SectionHeader, 6> sections;
74 std::array<u32, 0x20> capabilities;
75};
76static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
77
78const std::array<SHA256Hash, 0x10> source_hashes{
79 "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
80 "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
81 "21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source
82 "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source
83 "FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source
84 "C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source
85 "04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source
86 "FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source
87 "1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source
88 "6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source
89 "D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source
90 "2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source
91 "1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source
92 "8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source
93 "D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3
94 "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0
95};
96
97const std::array<SHA256Hash, 0x20> keyblob_source_hashes{
98 "8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00
99 "2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01
100 "61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02
101 "8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03
102 "95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04
103 "3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05
104 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06
105 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07
106
107 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08
108 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09
109 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A
110 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B
111 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C
112 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D
113 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E
114 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F
115
116 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10
117 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11
118 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12
119 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13
120 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14
121 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15
122 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16
123 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17
124
125 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18
126 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19
127 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A
128 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B
129 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C
130 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D
131 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E
132 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F
133};
134
135const std::array<SHA256Hash, 0x20> master_key_hashes{
136 "0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00
137 "4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01
138 "79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02
139 "4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03
140 "75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04
141 "EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05
142 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06
143 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07
144
145 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08
146 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09
147 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A
148 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B
149 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C
150 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D
151 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E
152 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F
153
154 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10
155 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11
156 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12
157 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13
158 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14
159 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15
160 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16
161 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17
162
163 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18
164 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19
165 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A
166 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B
167 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C
168 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D
169 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E
170 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
171};
172
173static std::vector<u8> DecompressBLZ(const std::vector<u8>& in) {
174 const auto data_size = in.size() - 0xC;
175
176 u32 compressed_size{};
177 u32 init_index{};
178 u32 additional_size{};
179 std::memcpy(&compressed_size, in.data() + data_size, sizeof(u32));
180 std::memcpy(&init_index, in.data() + data_size + 0x4, sizeof(u32));
181 std::memcpy(&additional_size, in.data() + data_size + 0x8, sizeof(u32));
182
183 std::vector<u8> out(in.size() + additional_size);
184
185 if (compressed_size == in.size())
186 std::memcpy(out.data(), in.data() + in.size() - compressed_size, compressed_size);
187 else
188 std::memcpy(out.data(), in.data(), compressed_size);
189
190 auto index = in.size() - init_index;
191 auto out_index = out.size();
192
193 while (out_index > 0) {
194 --index;
195 auto control = in[index];
196 for (size_t i = 0; i < 8; ++i) {
197 if ((control & 0x80) > 0) {
198 ASSERT(index >= 2);
199 index -= 2;
200 u64 segment_offset = in[index] | in[index + 1] << 8;
201 u64 segment_size = ((segment_offset >> 12) & 0xF) + 3;
202 segment_offset &= 0xFFF;
203 segment_offset += 3;
204
205 if (out_index < segment_size)
206 segment_size = out_index;
207
208 ASSERT(out_index >= segment_size);
209
210 out_index -= segment_size;
211
212 for (size_t j = 0; j < segment_size; ++j) {
213 ASSERT(out_index + j + segment_offset < out.size());
214 out[out_index + j] = out[out_index + j + segment_offset];
215 }
216 } else {
217 ASSERT(out_index >= 1);
218 --out_index;
219 --index;
220 out[out_index] = in[index];
221 }
222
223 control <<= 1;
224 if (out_index == 0)
225 return out;
226 }
227 }
228
229 return out;
230}
231
232static u8 CalculateMaxKeyblobSourceHash() {
233 for (s8 i = 0x1F; i >= 0; --i) {
234 if (keyblob_source_hashes[i] != SHA256Hash{})
235 return static_cast<u8>(i + 1);
236 }
237
238 return 0;
239}
240
241const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = CalculateMaxKeyblobSourceHash();
242
243template <size_t key_size = 0x10>
244std::array<u8, key_size> FindKeyFromHex(const std::vector<u8>& binary,
245 const std::array<u8, 0x20>& hash) {
246 if (binary.size() < key_size)
247 return {};
248
249 std::array<u8, 0x20> temp{};
250 for (size_t i = 0; i < binary.size() - key_size; ++i) {
251 mbedtls_sha256(binary.data() + i, key_size, temp.data(), 0);
252
253 if (temp != hash)
254 continue;
255
256 std::array<u8, key_size> out{};
257 std::memcpy(out.data(), binary.data() + i, key_size);
258 return out;
259 }
260
261 return {};
262}
263
264std::array<u8, 16> FindKeyFromHex16(const std::vector<u8>& binary, std::array<u8, 32> hash) {
265 return FindKeyFromHex<0x10>(binary, hash);
266}
267
268static std::array<Key128, 0x20> FindEncryptedMasterKeyFromHex(const std::vector<u8>& binary,
269 const Key128& key) {
270 if (binary.size() < 0x10)
271 return {};
272
273 SHA256Hash temp{};
274 Key128 dec_temp{};
275 std::array<Key128, 0x20> out{};
276 AESCipher<Key128> cipher(key, Mode::ECB);
277 for (size_t i = 0; i < binary.size() - 0x10; ++i) {
278 cipher.Transcode(binary.data() + i, dec_temp.size(), dec_temp.data(), Op::Decrypt);
279 mbedtls_sha256(dec_temp.data(), dec_temp.size(), temp.data(), 0);
280
281 for (size_t k = 0; k < out.size(); ++k) {
282 if (temp == master_key_hashes[k]) {
283 out[k] = dec_temp;
284 break;
285 }
286 }
287 }
288
289 return out;
290}
291
292FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
293 const std::string& name) {
294 auto upper = name;
295 std::transform(upper.begin(), upper.end(), upper.begin(), [](u8 c) { return std::toupper(c); });
296 for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
297 if (dir->GetFile(fname) != nullptr)
298 return dir->GetFile(fname);
299 }
300
301 return nullptr;
302}
303
304PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_dir)
305 : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")),
306 fuses(FindFileInDirWithNames(sysdata_dir, "fuses")),
307 kfuses(FindFileInDirWithNames(sysdata_dir, "kfuses")),
308 package2({
309 FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"),
310 FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"),
311 FindFileInDirWithNames(sysdata_dir, "BCPKG2-3-SafeMode-Main"),
312 FindFileInDirWithNames(sysdata_dir, "BCPKG2-4-SafeMode-Sub"),
313 FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"),
314 FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"),
315 }),
316 prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")),
317 secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")),
318 package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")),
319 secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{}
320 : secure_monitor->ReadAllBytes()),
321 package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{}
322 : package1_decrypted->ReadAllBytes()) {
323}
324
325PartitionDataManager::~PartitionDataManager() = default;
326
327bool PartitionDataManager::HasBoot0() const {
328 return boot0 != nullptr;
329}
330
331FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const {
332 return boot0;
333}
334
335PartitionDataManager::EncryptedKeyBlob PartitionDataManager::GetEncryptedKeyblob(
336 std::size_t index) const {
337 if (HasBoot0() && index < NUM_ENCRYPTED_KEYBLOBS)
338 return GetEncryptedKeyblobs()[index];
339 return {};
340}
341
342PartitionDataManager::EncryptedKeyBlobs PartitionDataManager::GetEncryptedKeyblobs() const {
343 if (!HasBoot0())
344 return {};
345
346 EncryptedKeyBlobs out{};
347 for (size_t i = 0; i < out.size(); ++i)
348 boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200);
349 return out;
350}
351
352std::vector<u8> PartitionDataManager::GetSecureMonitor() const {
353 return secure_monitor_bytes;
354}
355
356std::array<u8, 16> PartitionDataManager::GetPackage2KeySource() const {
357 return FindKeyFromHex(secure_monitor_bytes, source_hashes[2]);
358}
359
360std::array<u8, 16> PartitionDataManager::GetAESKekGenerationSource() const {
361 return FindKeyFromHex(secure_monitor_bytes, source_hashes[3]);
362}
363
364std::array<u8, 16> PartitionDataManager::GetTitlekekSource() const {
365 return FindKeyFromHex(secure_monitor_bytes, source_hashes[5]);
366}
367
368std::array<std::array<u8, 16>, 32> PartitionDataManager::GetTZMasterKeys(
369 std::array<u8, 0x10> master_key) const {
370 return FindEncryptedMasterKeyFromHex(secure_monitor_bytes, master_key);
371}
372
373std::array<u8, 16> PartitionDataManager::GetRSAKekSeed3() const {
374 return FindKeyFromHex(secure_monitor_bytes, source_hashes[14]);
375}
376
377std::array<u8, 16> PartitionDataManager::GetRSAKekMask0() const {
378 return FindKeyFromHex(secure_monitor_bytes, source_hashes[15]);
379}
380
381std::vector<u8> PartitionDataManager::GetPackage1Decrypted() const {
382 return package1_decrypted_bytes;
383}
384
385std::array<u8, 16> PartitionDataManager::GetMasterKeySource() const {
386 return FindKeyFromHex(package1_decrypted_bytes, source_hashes[1]);
387}
388
389std::array<u8, 16> PartitionDataManager::GetKeyblobMACKeySource() const {
390 return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]);
391}
392
393std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(std::size_t revision) const {
394 if (keyblob_source_hashes[revision] == SHA256Hash{}) {
395 LOG_WARNING(Crypto,
396 "No keyblob source hash for crypto revision {:02X}! Cannot derive keys...",
397 revision);
398 }
399 return FindKeyFromHex(package1_decrypted_bytes, keyblob_source_hashes[revision]);
400}
401
402bool PartitionDataManager::HasFuses() const {
403 return fuses != nullptr;
404}
405
406FileSys::VirtualFile PartitionDataManager::GetFusesRaw() const {
407 return fuses;
408}
409
410std::array<u8, 16> PartitionDataManager::GetSecureBootKey() const {
411 if (!HasFuses())
412 return {};
413 Key128 out{};
414 fuses->Read(out.data(), out.size(), 0xA4);
415 return out;
416}
417
418bool PartitionDataManager::HasKFuses() const {
419 return kfuses != nullptr;
420}
421
422FileSys::VirtualFile PartitionDataManager::GetKFusesRaw() const {
423 return kfuses;
424}
425
426bool PartitionDataManager::HasPackage2(Package2Type type) const {
427 return package2.at(static_cast<size_t>(type)) != nullptr;
428}
429
430FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) const {
431 return package2.at(static_cast<size_t>(type));
432}
433
434bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
435
436 const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end());
437 Package2Header temp = header;
438 AESCipher<Key128> cipher(key, Mode::CTR);
439 cipher.SetIV(iv);
440 cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr,
441 Op::Decrypt);
442 if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
443 header = temp;
444 return true;
445 }
446
447 return false;
448}
449
450void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& package2_keys,
451 Package2Type type) {
452 FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>(
453 package2[static_cast<size_t>(type)],
454 package2[static_cast<size_t>(type)]->GetSize() - 0x4000, 0x4000);
455
456 Package2Header header{};
457 if (file->ReadObject(&header) != sizeof(Package2Header))
458 return;
459
460 std::size_t revision = 0xFF;
461 if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) {
462 for (std::size_t i = 0; i < package2_keys.size(); ++i) {
463 if (AttemptDecrypt(package2_keys[i], header)) {
464 revision = i;
465 }
466 }
467 }
468
469 if (header.magic != Common::MakeMagic('P', 'K', '2', '1'))
470 return;
471
472 const auto a = std::make_shared<FileSys::OffsetVfsFile>(
473 file, header.section_size[1], header.section_size[0] + sizeof(Package2Header));
474
475 auto c = a->ReadAllBytes();
476
477 AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR);
478 cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
479 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
480
481 INIHeader ini;
482 std::memcpy(&ini, c.data(), sizeof(INIHeader));
483 if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
484 return;
485
486 u64 offset = sizeof(INIHeader);
487 for (size_t i = 0; i < ini.process_count; ++i) {
488 KIPHeader kip;
489 std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
490 if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
491 return;
492
493 const auto name =
494 Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
495
496 if (name != "FS" && name != "spl") {
497 offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
498 kip.sections[1].size_compressed + kip.sections[2].size_compressed;
499 continue;
500 }
501
502 const u64 initial_offset = sizeof(KIPHeader) + offset;
503 const auto text_begin = c.cbegin() + initial_offset;
504 const auto text_end = text_begin + kip.sections[0].size_compressed;
505 const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
506
507 const auto rodata_end = text_end + kip.sections[1].size_compressed;
508 const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
509
510 const auto data_end = rodata_end + kip.sections[2].size_compressed;
511 const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
512
513 std::vector<u8> out;
514 out.reserve(text.size() + rodata.size() + data.size());
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());
518
519 offset += sizeof(KIPHeader) + out.size();
520
521 if (name == "FS")
522 package2_fs[static_cast<size_t>(type)] = std::move(out);
523 else if (name == "spl")
524 package2_spl[static_cast<size_t>(type)] = std::move(out);
525 }
526}
527
528const std::vector<u8>& PartitionDataManager::GetPackage2FSDecompressed(Package2Type type) const {
529 return package2_fs.at(static_cast<size_t>(type));
530}
531
532std::array<u8, 16> PartitionDataManager::GetKeyAreaKeyApplicationSource(Package2Type type) const {
533 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[6]);
534}
535
536std::array<u8, 16> PartitionDataManager::GetKeyAreaKeyOceanSource(Package2Type type) const {
537 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[7]);
538}
539
540std::array<u8, 16> PartitionDataManager::GetKeyAreaKeySystemSource(Package2Type type) const {
541 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[8]);
542}
543
544std::array<u8, 16> PartitionDataManager::GetSDKekSource(Package2Type type) const {
545 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[9]);
546}
547
548std::array<u8, 32> PartitionDataManager::GetSDSaveKeySource(Package2Type type) const {
549 return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[10]);
550}
551
552std::array<u8, 32> PartitionDataManager::GetSDNCAKeySource(Package2Type type) const {
553 return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[11]);
554}
555
556std::array<u8, 16> PartitionDataManager::GetHeaderKekSource(Package2Type type) const {
557 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[12]);
558}
559
560std::array<u8, 32> PartitionDataManager::GetHeaderKeySource(Package2Type type) const {
561 return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[13]);
562}
563
564const std::vector<u8>& PartitionDataManager::GetPackage2SPLDecompressed(Package2Type type) const {
565 return package2_spl.at(static_cast<size_t>(type));
566}
567
568std::array<u8, 16> PartitionDataManager::GetAESKeyGenerationSource(Package2Type type) const {
569 return FindKeyFromHex(package2_spl.at(static_cast<size_t>(type)), source_hashes[4]);
570}
571
572bool PartitionDataManager::HasProdInfo() const {
573 return prodinfo != nullptr;
574}
575
576FileSys::VirtualFile PartitionDataManager::GetProdInfoRaw() const {
577 return prodinfo;
578}
579
580void PartitionDataManager::DecryptProdInfo(std::array<u8, 0x20> bis_key) {
581 if (prodinfo == nullptr)
582 return;
583
584 prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key);
585}
586
587std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const {
588 std::array<u8, 0x240> out{};
589 if (prodinfo_decrypted != nullptr)
590 prodinfo_decrypted->Read(out.data(), out.size(), 0x3890);
591 return out;
592}
593} // namespace Core::Crypto
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h
new file mode 100644
index 000000000..0ad007c72
--- /dev/null
+++ b/src/core/crypto/partition_data_manager.h
@@ -0,0 +1,109 @@
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 <vector>
8#include "common/common_types.h"
9#include "core/file_sys/vfs_types.h"
10
11namespace Core::Crypto {
12
13enum class Package2Type {
14 NormalMain,
15 NormalSub,
16 SafeModeMain,
17 SafeModeSub,
18 RepairMain,
19 RepairSub,
20};
21
22class PartitionDataManager {
23public:
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
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);
32 ~PartitionDataManager();
33
34 // BOOT0
35 bool HasBoot0() const;
36 FileSys::VirtualFile GetBoot0Raw() const;
37 EncryptedKeyBlob GetEncryptedKeyblob(std::size_t index) const;
38 EncryptedKeyBlobs GetEncryptedKeyblobs() const;
39 std::vector<u8> GetSecureMonitor() const;
40 std::array<u8, 0x10> GetPackage2KeySource() const;
41 std::array<u8, 0x10> GetAESKekGenerationSource() const;
42 std::array<u8, 0x10> GetTitlekekSource() const;
43 std::array<std::array<u8, 0x10>, 0x20> GetTZMasterKeys(std::array<u8, 0x10> master_key) const;
44 std::array<u8, 0x10> GetRSAKekSeed3() const;
45 std::array<u8, 0x10> GetRSAKekMask0() const;
46 std::vector<u8> GetPackage1Decrypted() const;
47 std::array<u8, 0x10> GetMasterKeySource() const;
48 std::array<u8, 0x10> GetKeyblobMACKeySource() const;
49 std::array<u8, 0x10> GetKeyblobKeySource(std::size_t revision) const;
50
51 // Fuses
52 bool HasFuses() const;
53 FileSys::VirtualFile GetFusesRaw() const;
54 std::array<u8, 0x10> GetSecureBootKey() const;
55
56 // K-Fuses
57 bool HasKFuses() const;
58 FileSys::VirtualFile GetKFusesRaw() const;
59
60 // Package2
61 bool HasPackage2(Package2Type type = Package2Type::NormalMain) const;
62 FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const;
63 void DecryptPackage2(const std::array<std::array<u8, 16>, 0x20>& package2_keys,
64 Package2Type type);
65 const std::vector<u8>& GetPackage2FSDecompressed(
66 Package2Type type = Package2Type::NormalMain) const;
67 std::array<u8, 0x10> GetKeyAreaKeyApplicationSource(
68 Package2Type type = Package2Type::NormalMain) const;
69 std::array<u8, 0x10> GetKeyAreaKeyOceanSource(
70 Package2Type type = Package2Type::NormalMain) const;
71 std::array<u8, 0x10> GetKeyAreaKeySystemSource(
72 Package2Type type = Package2Type::NormalMain) const;
73 std::array<u8, 0x10> GetSDKekSource(Package2Type type = Package2Type::NormalMain) const;
74 std::array<u8, 0x20> GetSDSaveKeySource(Package2Type type = Package2Type::NormalMain) const;
75 std::array<u8, 0x20> GetSDNCAKeySource(Package2Type type = Package2Type::NormalMain) const;
76 std::array<u8, 0x10> GetHeaderKekSource(Package2Type type = Package2Type::NormalMain) const;
77 std::array<u8, 0x20> GetHeaderKeySource(Package2Type type = Package2Type::NormalMain) const;
78 const std::vector<u8>& GetPackage2SPLDecompressed(
79 Package2Type type = Package2Type::NormalMain) const;
80 std::array<u8, 0x10> GetAESKeyGenerationSource(
81 Package2Type type = Package2Type::NormalMain) const;
82
83 // PRODINFO
84 bool HasProdInfo() const;
85 FileSys::VirtualFile GetProdInfoRaw() const;
86 void DecryptProdInfo(std::array<u8, 0x20> bis_key);
87 std::array<u8, 0x240> GetETicketExtendedKek() const;
88
89private:
90 FileSys::VirtualFile boot0;
91 FileSys::VirtualFile fuses;
92 FileSys::VirtualFile kfuses;
93 std::array<FileSys::VirtualFile, 6> package2;
94 FileSys::VirtualFile prodinfo;
95 FileSys::VirtualFile secure_monitor;
96 FileSys::VirtualFile package1_decrypted;
97
98 // Processed
99 std::array<FileSys::VirtualFile, 6> package2_decrypted;
100 FileSys::VirtualFile prodinfo_decrypted;
101 std::vector<u8> secure_monitor_bytes;
102 std::vector<u8> package1_decrypted_bytes;
103 std::array<std::vector<u8>, 6> package2_fs;
104 std::array<std::vector<u8>, 6> package2_spl;
105};
106
107std::array<u8, 0x10> FindKeyFromHex16(const std::vector<u8>& binary, std::array<u8, 0x20> hash);
108
109} // namespace Core::Crypto
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/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index aa1b3c17d..6dcec7816 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -133,7 +133,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
133 static_cast<u8>(type)); 133 static_cast<u8>(type));
134 u128 out_128{}; 134 u128 out_128{};
135 memcpy(out_128.data(), out.data(), 16); 135 memcpy(out_128.data(), out.data(), 16);
136 LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}", 136 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]); 137 master_key_id, header.key_index, out_128[1], out_128[0]);
138 138
139 return out; 139 return out;
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/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 6c072d0a3..554eae9bc 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -107,12 +107,12 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
107 return nullptr; 107 return nullptr;
108 108
109 if (real_offset + rle_size > in_data.size()) 109 if (real_offset + rle_size > in_data.size())
110 rle_size = in_data.size() - real_offset; 110 rle_size = static_cast<u16>(in_data.size() - real_offset);
111 std::memset(in_data.data() + real_offset, data.get(), rle_size); 111 std::memset(in_data.data() + real_offset, data.get(), rle_size);
112 } else { // Standard Patch 112 } else { // Standard Patch
113 auto read = data_size; 113 auto read = data_size;
114 if (real_offset + read > in_data.size()) 114 if (real_offset + read > in_data.size())
115 read = in_data.size() - real_offset; 115 read = static_cast<u16>(in_data.size() - real_offset);
116 if (ips->Read(in_data.data() + real_offset, read, offset) != data_size) 116 if (ips->Read(in_data.data() + real_offset, read, offset) != data_size)
117 return nullptr; 117 return nullptr;
118 offset += data_size; 118 offset += data_size;
@@ -265,7 +265,7 @@ void IPSwitchCompiler::Parse() {
265 if (patch_line.length() < 11) 265 if (patch_line.length() < 11)
266 break; 266 break;
267 auto offset = std::stoul(patch_line.substr(0, 8), nullptr, 16); 267 auto offset = std::stoul(patch_line.substr(0, 8), nullptr, 16);
268 offset += offset_shift; 268 offset += static_cast<unsigned long>(offset_shift);
269 269
270 std::vector<u8> replace; 270 std::vector<u8> replace;
271 // 9 - first char of replacement val 271 // 9 - first char of replacement val
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index b14d7cb0a..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;
@@ -345,23 +351,22 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
345 return out; 351 return out;
346} 352}
347 353
348std::pair<std::shared_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)
353 return {}; 359 return {};
354 360
355 return ParseControlNCA(base_control_nca); 361 return ParseControlNCA(*base_control_nca);
356} 362}
357 363
358std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA( 364std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const {
359 const std::shared_ptr<NCA>& nca) const { 365 const auto base_romfs = nca.GetRomFS();
360 const auto base_romfs = nca->GetRomFS();
361 if (base_romfs == nullptr) 366 if (base_romfs == nullptr)
362 return {}; 367 return {};
363 368
364 const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control); 369 const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control);
365 if (romfs == nullptr) 370 if (romfs == nullptr)
366 return {}; 371 return {};
367 372
@@ -373,7 +378,7 @@ std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
373 if (nacp_file == nullptr) 378 if (nacp_file == nullptr)
374 nacp_file = extracted->GetFile("Control.nacp"); 379 nacp_file = extracted->GetFile("Control.nacp");
375 380
376 const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared<NACP>(nacp_file); 381 auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file);
377 382
378 VirtualFile icon_file; 383 VirtualFile icon_file;
379 for (const auto& language : FileSys::LANGUAGE_NAMES) { 384 for (const auto& language : FileSys::LANGUAGE_NAMES) {
@@ -382,6 +387,6 @@ std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
382 break; 387 break;
383 } 388 }
384 389
385 return {nacp, icon_file}; 390 return {std::move(nacp), icon_file};
386} 391}
387} // namespace FileSys 392} // namespace FileSys
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index eb6fc4607..7d168837f 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -57,11 +57,10 @@ public:
57 57
58 // Given title_id of the program, attempts to get the control data of the update and parse it, 58 // Given title_id of the program, attempts to get the control data of the update and parse it,
59 // falling back to the base control data. 59 // falling back to the base control data.
60 std::pair<std::shared_ptr<NACP>, VirtualFile> GetControlMetadata() const; 60 std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const;
61 61
62 // Version of GetControlMetadata that takes an arbitrary NCA 62 // Version of GetControlMetadata that takes an arbitrary NCA
63 std::pair<std::shared_ptr<NACP>, VirtualFile> ParseControlNCA( 63 std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const;
64 const std::shared_ptr<NCA>& nca) const;
65 64
66private: 65private:
67 u64 title_id; 66 u64 title_id;
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/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 270291631..7f0d520ca 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -12,20 +12,12 @@
12#include <vector> 12#include <vector>
13#include <boost/optional.hpp> 13#include <boost/optional.hpp>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "core/file_sys/vfs_types.h"
15 16
16namespace FileSys { 17namespace FileSys {
17 18
18class VfsDirectory;
19class VfsFile;
20class VfsFilesystem;
21
22enum class Mode : u32; 19enum class Mode : u32;
23 20
24// Convenience typedefs to use Vfs* interfaces
25using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
26using VirtualDir = std::shared_ptr<VfsDirectory>;
27using VirtualFile = std::shared_ptr<VfsFile>;
28
29// An enumeration representing what can be at the end of a path in a VfsFilesystem 21// An enumeration representing what can be at the end of a path in a VfsFilesystem
30enum class VfsEntryType { 22enum class VfsEntryType {
31 None, 23 None,
diff --git a/src/core/file_sys/vfs_types.h b/src/core/file_sys/vfs_types.h
new file mode 100644
index 000000000..6215ed7af
--- /dev/null
+++ b/src/core/file_sys/vfs_types.h
@@ -0,0 +1,21 @@
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 <memory>
8
9namespace FileSys {
10
11class VfsDirectory;
12class VfsFile;
13class VfsFilesystem;
14
15// Declarations for Vfs* pointer types
16
17using VirtualDir = std::shared_ptr<VfsDirectory>;
18using VirtualFile = std::shared_ptr<VfsFile>;
19using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
20
21} // 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/errors.h b/src/core/hle/kernel/errors.h
index e5fa67ae8..885259618 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -22,6 +22,7 @@ enum {
22 HandleTableFull = 105, 22 HandleTableFull = 105,
23 InvalidMemoryState = 106, 23 InvalidMemoryState = 106,
24 InvalidMemoryPermissions = 108, 24 InvalidMemoryPermissions = 108,
25 InvalidMemoryRange = 110,
25 InvalidThreadPriority = 112, 26 InvalidThreadPriority = 112,
26 InvalidProcessorId = 113, 27 InvalidProcessorId = 113,
27 InvalidHandle = 114, 28 InvalidHandle = 114,
@@ -56,6 +57,7 @@ constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidA
56constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); 57constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
57constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, 58constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
58 ErrCodes::InvalidMemoryPermissions); 59 ErrCodes::InvalidMemoryPermissions);
60constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange);
59constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); 61constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
60constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); 62constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
61constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); 63constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 98eb74298..bd680adfe 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -116,7 +116,7 @@ struct KernelCore::Impl {
116 next_thread_id = 1; 116 next_thread_id = 1;
117 117
118 process_list.clear(); 118 process_list.clear();
119 current_process.reset(); 119 current_process = nullptr;
120 120
121 handle_table.Clear(); 121 handle_table.Clear();
122 resource_limits.fill(nullptr); 122 resource_limits.fill(nullptr);
@@ -207,7 +207,7 @@ struct KernelCore::Impl {
207 207
208 // Lists all processes that exist in the current session. 208 // Lists all processes that exist in the current session.
209 std::vector<SharedPtr<Process>> process_list; 209 std::vector<SharedPtr<Process>> process_list;
210 SharedPtr<Process> current_process; 210 Process* current_process = nullptr;
211 211
212 Kernel::HandleTable handle_table; 212 Kernel::HandleTable handle_table;
213 std::array<SharedPtr<ResourceLimit>, 4> resource_limits; 213 std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
@@ -266,15 +266,15 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
266 impl->process_list.push_back(std::move(process)); 266 impl->process_list.push_back(std::move(process));
267} 267}
268 268
269void KernelCore::MakeCurrentProcess(SharedPtr<Process> process) { 269void KernelCore::MakeCurrentProcess(Process* process) {
270 impl->current_process = std::move(process); 270 impl->current_process = process;
271} 271}
272 272
273SharedPtr<Process>& KernelCore::CurrentProcess() { 273Process* KernelCore::CurrentProcess() {
274 return impl->current_process; 274 return impl->current_process;
275} 275}
276 276
277const SharedPtr<Process>& KernelCore::CurrentProcess() const { 277const Process* KernelCore::CurrentProcess() const {
278 return impl->current_process; 278 return impl->current_process;
279} 279}
280 280
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index c0771ecf0..41554821f 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -66,13 +66,13 @@ public:
66 void AppendNewProcess(SharedPtr<Process> process); 66 void AppendNewProcess(SharedPtr<Process> process);
67 67
68 /// Makes the given process the new current process. 68 /// Makes the given process the new current process.
69 void MakeCurrentProcess(SharedPtr<Process> process); 69 void MakeCurrentProcess(Process* process);
70 70
71 /// Retrieves a reference to the current process. 71 /// Retrieves a pointer to the current process.
72 SharedPtr<Process>& CurrentProcess(); 72 Process* CurrentProcess();
73 73
74 /// Retrieves a const reference to the current process. 74 /// Retrieves a const pointer to the current process.
75 const SharedPtr<Process>& CurrentProcess() const; 75 const Process* CurrentProcess() const;
76 76
77 /// Adds a port to the named port table 77 /// Adds a port to the named port table
78 void AddNamedPort(std::string name, SharedPtr<ClientPort> port); 78 void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
index d51562d92..d87a62bb9 100644
--- a/src/core/hle/kernel/object.cpp
+++ b/src/core/hle/kernel/object.cpp
@@ -25,7 +25,6 @@ bool Object::IsWaitable() const {
25 case HandleType::Process: 25 case HandleType::Process:
26 case HandleType::AddressArbiter: 26 case HandleType::AddressArbiter:
27 case HandleType::ResourceLimit: 27 case HandleType::ResourceLimit:
28 case HandleType::CodeSet:
29 case HandleType::ClientPort: 28 case HandleType::ClientPort:
30 case HandleType::ClientSession: 29 case HandleType::ClientSession:
31 return false; 30 return false;
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
index 9eb72315c..c9f4d0bb3 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -26,7 +26,6 @@ enum class HandleType : u32 {
26 AddressArbiter, 26 AddressArbiter,
27 Timer, 27 Timer,
28 ResourceLimit, 28 ResourceLimit,
29 CodeSet,
30 ClientPort, 29 ClientPort,
31 ServerPort, 30 ServerPort,
32 ClientSession, 31 ClientSession,
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index fb0027a71..073dd5a7d 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -20,13 +20,7 @@
20 20
21namespace Kernel { 21namespace Kernel {
22 22
23SharedPtr<CodeSet> CodeSet::Create(KernelCore& kernel, std::string name) { 23CodeSet::CodeSet() = default;
24 SharedPtr<CodeSet> codeset(new CodeSet(kernel));
25 codeset->name = std::move(name);
26 return codeset;
27}
28
29CodeSet::CodeSet(KernelCore& kernel) : Object{kernel} {}
30CodeSet::~CodeSet() = default; 24CodeSet::~CodeSet() = default;
31 25
32SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { 26SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
@@ -159,11 +153,11 @@ void Process::PrepareForTermination() {
159 } 153 }
160 }; 154 };
161 155
162 auto& system = Core::System::GetInstance(); 156 const auto& system = Core::System::GetInstance();
163 stop_threads(system.Scheduler(0)->GetThreadList()); 157 stop_threads(system.Scheduler(0).GetThreadList());
164 stop_threads(system.Scheduler(1)->GetThreadList()); 158 stop_threads(system.Scheduler(1).GetThreadList());
165 stop_threads(system.Scheduler(2)->GetThreadList()); 159 stop_threads(system.Scheduler(2).GetThreadList());
166 stop_threads(system.Scheduler(3)->GetThreadList()); 160 stop_threads(system.Scheduler(3).GetThreadList());
167} 161}
168 162
169/** 163/**
@@ -224,20 +218,20 @@ void Process::FreeTLSSlot(VAddr tls_address) {
224 tls_slots[tls_page].reset(tls_slot); 218 tls_slots[tls_page].reset(tls_slot);
225} 219}
226 220
227void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { 221void Process::LoadModule(CodeSet module_, VAddr base_addr) {
228 const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, 222 const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
229 MemoryState memory_state) { 223 MemoryState memory_state) {
230 auto vma = vm_manager 224 const auto vma = vm_manager
231 .MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset, 225 .MapMemoryBlock(segment.addr + base_addr, module_.memory,
232 segment.size, memory_state) 226 segment.offset, segment.size, memory_state)
233 .Unwrap(); 227 .Unwrap();
234 vm_manager.Reprotect(vma, permissions); 228 vm_manager.Reprotect(vma, permissions);
235 }; 229 };
236 230
237 // Map CodeSet segments 231 // Map CodeSet segments
238 MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); 232 MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
239 MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); 233 MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
240 MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); 234 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
241} 235}
242 236
243ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { 237ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 590e0c73d..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,30 +58,33 @@ 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 : public Object { 79struct CodeSet final {
65 struct Segment { 80 struct Segment {
66 std::size_t offset = 0; 81 std::size_t offset = 0;
67 VAddr addr = 0; 82 VAddr addr = 0;
68 u32 size = 0; 83 u32 size = 0;
69 }; 84 };
70 85
71 static SharedPtr<CodeSet> Create(KernelCore& kernel, std::string name); 86 explicit CodeSet();
72 87 ~CodeSet();
73 std::string GetTypeName() const override {
74 return "CodeSet";
75 }
76 std::string GetName() const override {
77 return name;
78 }
79
80 static const HandleType HANDLE_TYPE = HandleType::CodeSet;
81 HandleType GetHandleType() const override {
82 return HANDLE_TYPE;
83 }
84 88
85 Segment& CodeSegment() { 89 Segment& CodeSegment() {
86 return segments[0]; 90 return segments[0];
@@ -109,14 +113,7 @@ struct CodeSet final : public Object {
109 std::shared_ptr<std::vector<u8>> memory; 113 std::shared_ptr<std::vector<u8>> memory;
110 114
111 std::array<Segment, 3> segments; 115 std::array<Segment, 3> segments;
112 VAddr entrypoint; 116 VAddr entrypoint = 0;
113
114 /// Name of the process
115 std::string name;
116
117private:
118 explicit CodeSet(KernelCore& kernel);
119 ~CodeSet() override;
120}; 117};
121 118
122class Process final : public Object { 119class Process final : public Object {
@@ -219,7 +216,7 @@ public:
219 */ 216 */
220 void PrepareForTermination(); 217 void PrepareForTermination();
221 218
222 void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr); 219 void LoadModule(CodeSet module_, VAddr base_addr);
223 220
224 /////////////////////////////////////////////////////////////////////////////////////////////// 221 ///////////////////////////////////////////////////////////////////////////////////////////////
225 // Memory Management 222 // Memory Management
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index cfd6e1bad..1342c597e 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -9,7 +9,7 @@
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/arm/arm_interface.h" 10#include "core/arm/arm_interface.h"
11#include "core/core.h" 11#include "core/core.h"
12#include "core/core_timing.h" 12#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/scheduler.h" 14#include "core/hle/kernel/scheduler.h"
15 15
@@ -78,16 +78,16 @@ void Scheduler::SwitchContext(Thread* new_thread) {
78 // Cancel any outstanding wakeup events for this thread 78 // Cancel any outstanding wakeup events for this thread
79 new_thread->CancelWakeupTimer(); 79 new_thread->CancelWakeupTimer();
80 80
81 auto previous_process = Core::CurrentProcess(); 81 auto* const previous_process = Core::CurrentProcess();
82 82
83 current_thread = new_thread; 83 current_thread = new_thread;
84 84
85 ready_queue.remove(new_thread->GetPriority(), new_thread); 85 ready_queue.remove(new_thread->GetPriority(), new_thread);
86 new_thread->SetStatus(ThreadStatus::Running); 86 new_thread->SetStatus(ThreadStatus::Running);
87 87
88 const auto thread_owner_process = current_thread->GetOwnerProcess(); 88 auto* const thread_owner_process = current_thread->GetOwnerProcess();
89 if (previous_process != thread_owner_process) { 89 if (previous_process != thread_owner_process) {
90 Core::CurrentProcess() = thread_owner_process; 90 Core::System::GetInstance().Kernel().MakeCurrentProcess(thread_owner_process);
91 SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table); 91 SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table);
92 } 92 }
93 93
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index b488b508d..d08b84bde 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -39,6 +39,73 @@ namespace {
39constexpr bool Is4KBAligned(VAddr address) { 39constexpr bool Is4KBAligned(VAddr address) {
40 return (address & 0xFFF) == 0; 40 return (address & 0xFFF) == 0;
41} 41}
42
43// 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
45// or if the given size is zero.
46constexpr bool IsValidAddressRange(VAddr address, u64 size) {
47 return address + size > address;
48}
49
50// Checks if a given address range lies within a larger address range.
51constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
52 VAddr address_range_end) {
53 const VAddr end_address = address + size - 1;
54 return address_range_begin <= address && end_address <= address_range_end - 1;
55}
56
57bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) {
58 return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(),
59 vm.GetAddressSpaceEndAddress());
60}
61
62bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
63 return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(),
64 vm.GetNewMapRegionEndAddress());
65}
66
67// Helper function that performs the common sanity checks for svcMapMemory
68// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
69// in the same order.
70ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr,
71 u64 size) {
72 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
73 return ERR_INVALID_ADDRESS;
74 }
75
76 if (size == 0 || !Is4KBAligned(size)) {
77 return ERR_INVALID_SIZE;
78 }
79
80 if (!IsValidAddressRange(dst_addr, size)) {
81 return ERR_INVALID_ADDRESS_STATE;
82 }
83
84 if (!IsValidAddressRange(src_addr, size)) {
85 return ERR_INVALID_ADDRESS_STATE;
86 }
87
88 if (!IsInsideAddressSpace(vm_manager, src_addr, size)) {
89 return ERR_INVALID_ADDRESS_STATE;
90 }
91
92 if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) {
93 return ERR_INVALID_MEMORY_RANGE;
94 }
95
96 const VAddr dst_end_address = dst_addr + size;
97 if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() &&
98 vm_manager.GetHeapRegionEndAddress() > dst_addr) {
99 return ERR_INVALID_MEMORY_RANGE;
100 }
101
102 if (dst_end_address > vm_manager.GetMapRegionBaseAddress() &&
103 vm_manager.GetMapRegionEndAddress() > dst_addr) {
104 return ERR_INVALID_MEMORY_RANGE;
105 }
106
107 return RESULT_SUCCESS;
108}
42} // Anonymous namespace 109} // Anonymous namespace
43 110
44/// Set the process heap to a given Size. It can both extend and shrink the heap. 111/// Set the process heap to a given Size. It can both extend and shrink the heap.
@@ -69,15 +136,15 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
69 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 136 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
70 src_addr, size); 137 src_addr, size);
71 138
72 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { 139 auto* const current_process = Core::CurrentProcess();
73 return ERR_INVALID_ADDRESS; 140 const auto& vm_manager = current_process->VMManager();
74 }
75 141
76 if (size == 0 || !Is4KBAligned(size)) { 142 const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
77 return ERR_INVALID_SIZE; 143 if (result != RESULT_SUCCESS) {
144 return result;
78 } 145 }
79 146
80 return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size); 147 return current_process->MirrorMemory(dst_addr, src_addr, size);
81} 148}
82 149
83/// Unmaps a region that was previously mapped with svcMapMemory 150/// Unmaps a region that was previously mapped with svcMapMemory
@@ -85,15 +152,15 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
85 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 152 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
86 src_addr, size); 153 src_addr, size);
87 154
88 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { 155 auto* const current_process = Core::CurrentProcess();
89 return ERR_INVALID_ADDRESS; 156 const auto& vm_manager = current_process->VMManager();
90 }
91 157
92 if (size == 0 || !Is4KBAligned(size)) { 158 const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
93 return ERR_INVALID_SIZE; 159 if (result != RESULT_SUCCESS) {
160 return result;
94 } 161 }
95 162
96 return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size); 163 return current_process->UnmapMemory(dst_addr, src_addr, size);
97} 164}
98 165
99/// Connect to an OS service given the port name, returns the handle to the port to out 166/// Connect to an OS service given the port name, returns the handle to the port to out
@@ -303,15 +370,15 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
303 370
304struct BreakReason { 371struct BreakReason {
305 union { 372 union {
306 u64 raw; 373 u32 raw;
307 BitField<31, 1, u64> dont_kill_application; 374 BitField<31, 1, u32> signal_debugger;
308 }; 375 };
309}; 376};
310 377
311/// Break program execution 378/// Break program execution
312static void Break(u64 reason, u64 info1, u64 info2) { 379static void Break(u32 reason, u64 info1, u64 info2) {
313 BreakReason break_reason{reason}; 380 BreakReason break_reason{reason};
314 if (break_reason.dont_kill_application) { 381 if (break_reason.signal_debugger) {
315 LOG_ERROR( 382 LOG_ERROR(
316 Debug_Emulated, 383 Debug_Emulated,
317 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 384 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
@@ -322,6 +389,12 @@ static void Break(u64 reason, u64 info1, u64 info2) {
322 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 389 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
323 reason, info1, info2); 390 reason, info1, info2);
324 ASSERT(false); 391 ASSERT(false);
392
393 Core::CurrentProcess()->PrepareForTermination();
394
395 // Kill the current thread
396 GetCurrentThread()->Stop();
397 Core::System::GetInstance().PrepareReschedule();
325 } 398 }
326} 399}
327 400
@@ -341,7 +414,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
341 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, 414 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
342 info_sub_id, handle); 415 info_sub_id, handle);
343 416
344 const auto& current_process = Core::CurrentProcess(); 417 const auto* current_process = Core::CurrentProcess();
345 const auto& vm_manager = current_process->VMManager(); 418 const auto& vm_manager = current_process->VMManager();
346 419
347 switch (static_cast<GetInfoType>(info_id)) { 420 switch (static_cast<GetInfoType>(info_id)) {
@@ -375,25 +448,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
375 case GetInfoType::RandomEntropy: 448 case GetInfoType::RandomEntropy:
376 *result = 0; 449 *result = 0;
377 break; 450 break;
378 case GetInfoType::AddressSpaceBaseAddr: 451 case GetInfoType::ASLRRegionBaseAddr:
379 *result = vm_manager.GetCodeRegionBaseAddress(); 452 *result = vm_manager.GetASLRRegionBaseAddress();
380 break; 453 break;
381 case GetInfoType::AddressSpaceSize: { 454 case GetInfoType::ASLRRegionSize:
382 const u64 width = vm_manager.GetAddressSpaceWidth(); 455 *result = vm_manager.GetASLRRegionSize();
383
384 switch (width) {
385 case 32:
386 *result = 0xFFE00000;
387 break;
388 case 36:
389 *result = 0xFF8000000;
390 break;
391 case 39:
392 *result = 0x7FF8000000;
393 break;
394 }
395 break; 456 break;
396 }
397 case GetInfoType::NewMapRegionBaseAddr: 457 case GetInfoType::NewMapRegionBaseAddr:
398 *result = vm_manager.GetNewMapRegionBaseAddress(); 458 *result = vm_manager.GetNewMapRegionBaseAddress();
399 break; 459 break;
@@ -439,7 +499,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
439 return ERR_INVALID_HANDLE; 499 return ERR_INVALID_HANDLE;
440 } 500 }
441 501
442 const auto current_process = Core::CurrentProcess(); 502 const auto* current_process = Core::CurrentProcess();
443 if (thread->GetOwnerProcess() != current_process) { 503 if (thread->GetOwnerProcess() != current_process) {
444 return ERR_INVALID_HANDLE; 504 return ERR_INVALID_HANDLE;
445 } 505 }
@@ -531,7 +591,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
531 return ERR_INVALID_HANDLE; 591 return ERR_INVALID_HANDLE;
532 } 592 }
533 593
534 return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type, 594 return shared_memory->Map(Core::CurrentProcess(), addr, permissions_type,
535 MemoryPermission::DontCare); 595 MemoryPermission::DontCare);
536} 596}
537 597
@@ -550,7 +610,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
550 auto& kernel = Core::System::GetInstance().Kernel(); 610 auto& kernel = Core::System::GetInstance().Kernel();
551 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 611 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
552 612
553 return shared_memory->Unmap(Core::CurrentProcess().get(), addr); 613 return shared_memory->Unmap(Core::CurrentProcess(), addr);
554} 614}
555 615
556/// Query process memory 616/// Query process memory
@@ -588,7 +648,7 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd
588 648
589/// Exits the current process 649/// Exits the current process
590static void ExitProcess() { 650static void ExitProcess() {
591 auto& current_process = Core::CurrentProcess(); 651 auto* current_process = Core::CurrentProcess();
592 652
593 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); 653 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
594 ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, 654 ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
@@ -636,7 +696,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
636 auto& kernel = Core::System::GetInstance().Kernel(); 696 auto& kernel = Core::System::GetInstance().Kernel();
637 CASCADE_RESULT(SharedPtr<Thread> thread, 697 CASCADE_RESULT(SharedPtr<Thread> thread,
638 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, 698 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
639 Core::CurrentProcess())); 699 *Core::CurrentProcess()));
640 const auto new_guest_handle = kernel.HandleTable().Create(thread); 700 const auto new_guest_handle = kernel.HandleTable().Create(thread);
641 if (new_guest_handle.Failed()) { 701 if (new_guest_handle.Failed()) {
642 return new_guest_handle.Code(); 702 return new_guest_handle.Code();
@@ -736,7 +796,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
736 std::vector<SharedPtr<Thread>>& waiting_threads, 796 std::vector<SharedPtr<Thread>>& waiting_threads,
737 VAddr condvar_addr) { 797 VAddr condvar_addr) {
738 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 798 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
739 const auto& thread_list = scheduler->GetThreadList(); 799 const auto& thread_list = scheduler.GetThreadList();
740 800
741 for (const auto& thread : thread_list) { 801 for (const auto& thread : thread_list) {
742 if (thread->GetCondVarWaitAddress() == condvar_addr) 802 if (thread->GetCondVarWaitAddress() == condvar_addr)
@@ -1025,6 +1085,29 @@ static ResultCode ClearEvent(Handle handle) {
1025 return RESULT_SUCCESS; 1085 return RESULT_SUCCESS;
1026} 1086}
1027 1087
1088static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
1089 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
1090
1091 // This function currently only allows retrieving a process' status.
1092 enum class InfoType {
1093 Status,
1094 };
1095
1096 const auto& kernel = Core::System::GetInstance().Kernel();
1097 const auto process = kernel.HandleTable().Get<Process>(process_handle);
1098 if (!process) {
1099 return ERR_INVALID_HANDLE;
1100 }
1101
1102 const auto info_type = static_cast<InfoType>(type);
1103 if (info_type != InfoType::Status) {
1104 return ERR_INVALID_ENUM_VALUE;
1105 }
1106
1107 *out = static_cast<u64>(process->GetStatus());
1108 return RESULT_SUCCESS;
1109}
1110
1028namespace { 1111namespace {
1029struct FunctionDef { 1112struct FunctionDef {
1030 using Func = void(); 1113 using Func = void();
@@ -1160,7 +1243,7 @@ static const FunctionDef SVC_Table[] = {
1160 {0x79, nullptr, "CreateProcess"}, 1243 {0x79, nullptr, "CreateProcess"},
1161 {0x7A, nullptr, "StartProcess"}, 1244 {0x7A, nullptr, "StartProcess"},
1162 {0x7B, nullptr, "TerminateProcess"}, 1245 {0x7B, nullptr, "TerminateProcess"},
1163 {0x7C, nullptr, "GetProcessInfo"}, 1246 {0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"},
1164 {0x7D, nullptr, "CreateResourceLimit"}, 1247 {0x7D, nullptr, "CreateResourceLimit"},
1165 {0x7E, nullptr, "SetResourceLimitLimitValue"}, 1248 {0x7E, nullptr, "SetResourceLimitLimitValue"},
1166 {0x7F, nullptr, "CallSecureMonitor"}, 1249 {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 22712e64f..b09753c80 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -35,18 +35,18 @@ void SvcWrap() {
35 35
36template <ResultCode func(u32)> 36template <ResultCode func(u32)>
37void SvcWrap() { 37void SvcWrap() {
38 FuncReturn(func((u32)Param(0)).raw); 38 FuncReturn(func(static_cast<u32>(Param(0))).raw);
39} 39}
40 40
41template <ResultCode func(u32, u32)> 41template <ResultCode func(u32, u32)>
42void SvcWrap() { 42void SvcWrap() {
43 FuncReturn(func((u32)Param(0), (u32)Param(1)).raw); 43 FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw);
44} 44}
45 45
46template <ResultCode func(u32*, u32)> 46template <ResultCode func(u32*, u32)>
47void SvcWrap() { 47void SvcWrap() {
48 u32 param_1 = 0; 48 u32 param_1 = 0;
49 u32 retval = func(&param_1, (u32)Param(1)).raw; 49 u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
50 Core::CurrentArmInterface().SetReg(1, param_1); 50 Core::CurrentArmInterface().SetReg(1, param_1);
51 FuncReturn(retval); 51 FuncReturn(retval);
52} 52}
@@ -61,7 +61,7 @@ void SvcWrap() {
61 61
62template <ResultCode func(u64, s32)> 62template <ResultCode func(u64, s32)>
63void SvcWrap() { 63void SvcWrap() {
64 FuncReturn(func(Param(0), (s32)Param(1)).raw); 64 FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw);
65} 65}
66 66
67template <ResultCode func(u64, u32)> 67template <ResultCode func(u64, u32)>
@@ -77,21 +77,29 @@ 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((u32)(Param(0) & 0xFFFFFFFF), Param(1)).raw); 90 FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
83} 91}
84 92
85template <ResultCode func(u32, u32, u64)> 93template <ResultCode func(u32, u32, u64)>
86void SvcWrap() { 94void SvcWrap() {
87 FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), (u32)(Param(1) & 0xFFFFFFFF), Param(2)).raw); 95 FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw);
88} 96}
89 97
90template <ResultCode func(u32, u32*, u64*)> 98template <ResultCode func(u32, u32*, u64*)>
91void SvcWrap() { 99void SvcWrap() {
92 u32 param_1 = 0; 100 u32 param_1 = 0;
93 u64 param_2 = 0; 101 u64 param_2 = 0;
94 ResultCode retval = func((u32)(Param(2) & 0xFFFFFFFF), &param_1, &param_2); 102 ResultCode retval = func(static_cast<u32>(Param(2)), &param_1, &param_2);
95 Core::CurrentArmInterface().SetReg(1, param_1); 103 Core::CurrentArmInterface().SetReg(1, param_1);
96 Core::CurrentArmInterface().SetReg(2, param_2); 104 Core::CurrentArmInterface().SetReg(2, param_2);
97 FuncReturn(retval.raw); 105 FuncReturn(retval.raw);
@@ -100,12 +108,12 @@ void SvcWrap() {
100template <ResultCode func(u64, u64, u32, u32)> 108template <ResultCode func(u64, u64, u32, u32)>
101void SvcWrap() { 109void SvcWrap() {
102 FuncReturn( 110 FuncReturn(
103 func(Param(0), Param(1), (u32)(Param(3) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw); 111 func(Param(0), Param(1), static_cast<u32>(Param(3)), static_cast<u32>(Param(3))).raw);
104} 112}
105 113
106template <ResultCode func(u32, u64, u32)> 114template <ResultCode func(u32, u64, u32)>
107void SvcWrap() { 115void SvcWrap() {
108 FuncReturn(func((u32)Param(0), Param(1), (u32)Param(2)).raw); 116 FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw);
109} 117}
110 118
111template <ResultCode func(u64, u64, u64)> 119template <ResultCode func(u64, u64, u64)>
@@ -115,25 +123,28 @@ void SvcWrap() {
115 123
116template <ResultCode func(u32, u64, u64, u32)> 124template <ResultCode func(u32, u64, u64, u32)>
117void SvcWrap() { 125void SvcWrap() {
118 FuncReturn(func((u32)Param(0), Param(1), Param(2), (u32)Param(3)).raw); 126 FuncReturn(
127 func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw);
119} 128}
120 129
121template <ResultCode func(u32, u64, u64)> 130template <ResultCode func(u32, u64, u64)>
122void SvcWrap() { 131void SvcWrap() {
123 FuncReturn(func((u32)Param(0), Param(1), Param(2)).raw); 132 FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw);
124} 133}
125 134
126template <ResultCode func(u32*, u64, u64, s64)> 135template <ResultCode func(u32*, u64, u64, s64)>
127void SvcWrap() { 136void SvcWrap() {
128 u32 param_1 = 0; 137 u32 param_1 = 0;
129 ResultCode retval = func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)); 138 ResultCode retval =
139 func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3)));
130 Core::CurrentArmInterface().SetReg(1, param_1); 140 Core::CurrentArmInterface().SetReg(1, param_1);
131 FuncReturn(retval.raw); 141 FuncReturn(retval.raw);
132} 142}
133 143
134template <ResultCode func(u64, u64, u32, s64)> 144template <ResultCode func(u64, u64, u32, s64)>
135void SvcWrap() { 145void SvcWrap() {
136 FuncReturn(func(Param(0), Param(1), (u32)Param(2), (s64)Param(3)).raw); 146 FuncReturn(
147 func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw);
137} 148}
138 149
139template <ResultCode func(u64*, u64, u64, u64)> 150template <ResultCode func(u64*, u64, u64, u64)>
@@ -147,9 +158,9 @@ void SvcWrap() {
147template <ResultCode func(u32*, u64, u64, u64, u32, s32)> 158template <ResultCode func(u32*, u64, u64, u64, u32, s32)>
148void SvcWrap() { 159void SvcWrap() {
149 u32 param_1 = 0; 160 u32 param_1 = 0;
150 u32 retval = 161 u32 retval = func(&param_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)),
151 func(&param_1, Param(1), Param(2), Param(3), (u32)Param(4), (s32)(Param(5) & 0xFFFFFFFF)) 162 static_cast<s32>(Param(5)))
152 .raw; 163 .raw;
153 Core::CurrentArmInterface().SetReg(1, param_1); 164 Core::CurrentArmInterface().SetReg(1, param_1);
154 FuncReturn(retval); 165 FuncReturn(retval);
155} 166}
@@ -172,7 +183,7 @@ void SvcWrap() {
172template <ResultCode func(u32*, u64, u64, u32)> 183template <ResultCode func(u32*, u64, u64, u32)>
173void SvcWrap() { 184void SvcWrap() {
174 u32 param_1 = 0; 185 u32 param_1 = 0;
175 u32 retval = func(&param_1, Param(1), Param(2), (u32)(Param(3) & 0xFFFFFFFF)).raw; 186 u32 retval = func(&param_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw;
176 Core::CurrentArmInterface().SetReg(1, param_1); 187 Core::CurrentArmInterface().SetReg(1, param_1);
177 FuncReturn(retval); 188 FuncReturn(retval);
178} 189}
@@ -181,22 +192,22 @@ template <ResultCode func(Handle*, u64, u32, u32)>
181void SvcWrap() { 192void SvcWrap() {
182 u32 param_1 = 0; 193 u32 param_1 = 0;
183 u32 retval = 194 u32 retval =
184 func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw; 195 func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw;
185 Core::CurrentArmInterface().SetReg(1, param_1); 196 Core::CurrentArmInterface().SetReg(1, param_1);
186 FuncReturn(retval); 197 FuncReturn(retval);
187} 198}
188 199
189template <ResultCode func(u64, u32, s32, s64)> 200template <ResultCode func(u64, u32, s32, s64)>
190void SvcWrap() { 201void SvcWrap() {
191 FuncReturn( 202 FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
192 func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)) 203 static_cast<s64>(Param(3)))
193 .raw); 204 .raw);
194} 205}
195 206
196template <ResultCode func(u64, u32, s32, s32)> 207template <ResultCode func(u64, u32, s32, s32)>
197void SvcWrap() { 208void SvcWrap() {
198 FuncReturn(func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), 209 FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
199 (s32)(Param(3) & 0xFFFFFFFF)) 210 static_cast<s32>(Param(3)))
200 .raw); 211 .raw);
201} 212}
202 213
@@ -226,7 +237,7 @@ void SvcWrap() {
226 237
227template <void func(s64)> 238template <void func(s64)>
228void SvcWrap() { 239void SvcWrap() {
229 func((s64)Param(0)); 240 func(static_cast<s64>(Param(0)));
230} 241}
231 242
232template <void func(u64, u64 len)> 243template <void func(u64, u64 len)>
@@ -239,4 +250,9 @@ void SvcWrap() {
239 func(Param(0), Param(1), Param(2)); 250 func(Param(0), Param(1), Param(2));
240} 251}
241 252
253template <void func(u32, u64, u64)>
254void SvcWrap() {
255 func(static_cast<u32>(Param(0)), Param(1), Param(2));
256}
257
242} // namespace Kernel 258} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 8e514cf9a..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}
@@ -183,18 +183,15 @@ void Thread::ResumeFromWait() {
183 */ 183 */
184static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top, 184static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top,
185 VAddr entry_point, u64 arg) { 185 VAddr entry_point, u64 arg) {
186 memset(&context, 0, sizeof(Core::ARM_Interface::ThreadContext)); 186 context = {};
187
188 context.cpu_registers[0] = arg; 187 context.cpu_registers[0] = arg;
189 context.pc = entry_point; 188 context.pc = entry_point;
190 context.sp = stack_top; 189 context.sp = stack_top;
191 context.pstate = 0;
192 context.fpcr = 0;
193} 190}
194 191
195ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point, 192ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
196 u32 priority, u64 arg, s32 processor_id, 193 u32 priority, u64 arg, s32 processor_id,
197 VAddr stack_top, SharedPtr<Process> owner_process) { 194 VAddr stack_top, Process& owner_process) {
198 // Check if priority is in ranged. Lowest priority -> highest priority id. 195 // Check if priority is in ranged. Lowest priority -> highest priority id.
199 if (priority > THREADPRIO_LOWEST) { 196 if (priority > THREADPRIO_LOWEST) {
200 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); 197 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
@@ -208,7 +205,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
208 205
209 // TODO(yuriks): Other checks, returning 0xD9001BEA 206 // TODO(yuriks): Other checks, returning 0xD9001BEA
210 207
211 if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) { 208 if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) {
212 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); 209 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
213 // TODO (bunnei): Find the correct error code to use here 210 // TODO (bunnei): Find the correct error code to use here
214 return ResultCode(-1); 211 return ResultCode(-1);
@@ -232,8 +229,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
232 thread->wait_handle = 0; 229 thread->wait_handle = 0;
233 thread->name = std::move(name); 230 thread->name = std::move(name);
234 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); 231 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
235 thread->owner_process = owner_process; 232 thread->owner_process = &owner_process;
236 thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get(); 233 thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id);
237 thread->scheduler->AddThread(thread, priority); 234 thread->scheduler->AddThread(thread, priority);
238 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); 235 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
239 236
@@ -264,7 +261,7 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri
264 // Initialize new "main" thread 261 // Initialize new "main" thread
265 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); 262 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
266 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0, 263 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
267 stack_top, &owner_process); 264 stack_top, owner_process);
268 265
269 SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); 266 SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
270 267
@@ -378,14 +375,14 @@ void Thread::ChangeCore(u32 core, u64 mask) {
378 new_processor_id = processor_id; 375 new_processor_id = processor_id;
379 } 376 }
380 if (ideal_core != -1 && 377 if (ideal_core != -1 &&
381 Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { 378 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
382 new_processor_id = ideal_core; 379 new_processor_id = ideal_core;
383 } 380 }
384 381
385 ASSERT(*new_processor_id < 4); 382 ASSERT(*new_processor_id < 4);
386 383
387 // Add thread to new core's scheduler 384 // Add thread to new core's scheduler
388 auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id); 385 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
389 386
390 if (*new_processor_id != processor_id) { 387 if (*new_processor_id != processor_id) {
391 // Remove thread from previous core's scheduler 388 // Remove thread from previous core's scheduler
@@ -400,7 +397,7 @@ void Thread::ChangeCore(u32 core, u64 mask) {
400 next_scheduler->ScheduleThread(this, current_priority); 397 next_scheduler->ScheduleThread(this, current_priority);
401 398
402 // Change thread's scheduler 399 // Change thread's scheduler
403 scheduler = next_scheduler.get(); 400 scheduler = next_scheduler;
404 401
405 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); 402 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
406} 403}
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index c6ffbd28c..f4d7bd235 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -89,7 +89,7 @@ public:
89 static ResultVal<SharedPtr<Thread>> Create(KernelCore& kernel, std::string name, 89 static ResultVal<SharedPtr<Thread>> Create(KernelCore& kernel, std::string name,
90 VAddr entry_point, u32 priority, u64 arg, 90 VAddr entry_point, u32 priority, u64 arg,
91 s32 processor_id, VAddr stack_top, 91 s32 processor_id, VAddr stack_top,
92 SharedPtr<Process> owner_process); 92 Process& owner_process);
93 93
94 std::string GetName() const override { 94 std::string GetName() const override {
95 return name; 95 return name;
@@ -262,11 +262,11 @@ public:
262 return processor_id; 262 return processor_id;
263 } 263 }
264 264
265 SharedPtr<Process>& GetOwnerProcess() { 265 Process* GetOwnerProcess() {
266 return owner_process; 266 return owner_process;
267 } 267 }
268 268
269 const SharedPtr<Process>& GetOwnerProcess() const { 269 const Process* GetOwnerProcess() const {
270 return owner_process; 270 return owner_process;
271 } 271 }
272 272
@@ -386,7 +386,7 @@ private:
386 u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register. 386 u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register.
387 387
388 /// Process that owns this thread 388 /// Process that owns this thread
389 SharedPtr<Process> owner_process; 389 Process* owner_process;
390 390
391 /// Objects that the thread is waiting on, in the same order as they were 391 /// Objects that the thread is waiting on, in the same order as they were
392 /// passed to WaitSynchronization1/N. 392 /// passed to WaitSynchronization1/N.
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index e412309fd..1e28ccbda 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,18 @@ 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
493VAddr VMManager::GetCodeRegionBaseAddress() const { 510VAddr VMManager::GetCodeRegionBaseAddress() const {
494 return code_region_base; 511 return code_region_base;
495} 512}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 015559a64..4accde6b3 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -205,6 +205,15 @@ 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 /// Gets the size of the ASLR region
215 u64 GetASLRRegionSize() const;
216
208 /// Gets the base address of the code region. 217 /// Gets the base address of the code region.
209 VAddr GetCodeRegionBaseAddress() const; 218 VAddr GetCodeRegionBaseAddress() const;
210 219
@@ -306,6 +315,9 @@ private:
306 VAddr address_space_base = 0; 315 VAddr address_space_base = 0;
307 VAddr address_space_end = 0; 316 VAddr address_space_end = 0;
308 317
318 VAddr aslr_region_base = 0;
319 VAddr aslr_region_end = 0;
320
309 VAddr code_region_base = 0; 321 VAddr code_region_base = 0;
310 VAddr code_region_end = 0; 322 VAddr code_region_end = 0;
311 323
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 0ecfb5af1..518161bf7 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -7,8 +7,10 @@
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"
14#include "core/hle/kernel/process.h" 16#include "core/hle/kernel/process.h"
@@ -19,7 +21,7 @@
19namespace Service::AOC { 21namespace Service::AOC {
20 22
21constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; 23constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
22constexpr u64 DLC_BASE_TO_AOC_ID_MASK = 0x1000; 24constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000;
23 25
24static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { 26static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) {
25 return (aoc & DLC_BASE_TITLE_ID_MASK) == base; 27 return (aoc & DLC_BASE_TITLE_ID_MASK) == base;
@@ -97,14 +99,24 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
97 99
98 ctx.WriteBuffer(out); 100 ctx.WriteBuffer(out);
99 101
100 IPC::ResponseBuilder rb{ctx, 2}; 102 IPC::ResponseBuilder rb{ctx, 3};
101 rb.Push(RESULT_SUCCESS); 103 rb.Push(RESULT_SUCCESS);
104 rb.Push(count);
102} 105}
103 106
104void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { 107void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
105 IPC::ResponseBuilder rb{ctx, 4}; 108 IPC::ResponseBuilder rb{ctx, 4};
106 rb.Push(RESULT_SUCCESS); 109 rb.Push(RESULT_SUCCESS);
107 rb.Push(Core::System::GetInstance().CurrentProcess()->GetTitleID() | DLC_BASE_TO_AOC_ID_MASK); 110 const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
111 FileSys::PatchManager pm{title_id};
112
113 const auto res = pm.GetControlMetadata();
114 if (res.first == nullptr) {
115 rb.Push(title_id + DLC_BASE_TO_AOC_ID);
116 return;
117 }
118
119 rb.Push(res.first->GetDLCBaseTitleId());
108} 120}
109 121
110void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) { 122void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index fc6067e59..7168c6a10 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -2,8 +2,10 @@
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 <chrono>
5#include <cstring> 6#include <cstring>
6#include <memory> 7#include <memory>
8#include <optional>
7#include <vector> 9#include <vector>
8 10
9#include <opus.h> 11#include <opus.h>
@@ -33,7 +35,8 @@ public:
33 {1, nullptr, "SetContext"}, 35 {1, nullptr, "SetContext"},
34 {2, nullptr, "DecodeInterleavedForMultiStream"}, 36 {2, nullptr, "DecodeInterleavedForMultiStream"},
35 {3, nullptr, "SetContextForMultiStream"}, 37 {3, nullptr, "SetContextForMultiStream"},
36 {4, nullptr, "Unknown4"}, 38 {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerformance,
39 "DecodeInterleavedWithPerformance"},
37 {5, nullptr, "Unknown5"}, 40 {5, nullptr, "Unknown5"},
38 {6, nullptr, "Unknown6"}, 41 {6, nullptr, "Unknown6"},
39 {7, nullptr, "Unknown7"}, 42 {7, nullptr, "Unknown7"},
@@ -59,8 +62,31 @@ private:
59 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); 62 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
60 } 63 }
61 64
62 bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, 65 void DecodeInterleavedWithPerformance(Kernel::HLERequestContext& ctx) {
63 std::vector<opus_int16>& output) { 66 u32 consumed = 0;
67 u32 sample_count = 0;
68 u64 performance = 0;
69 std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
70 if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples,
71 performance)) {
72 IPC::ResponseBuilder rb{ctx, 2};
73 // TODO(ogniK): Use correct error code
74 rb.Push(ResultCode(-1));
75 return;
76 }
77 IPC::ResponseBuilder rb{ctx, 6};
78 rb.Push(RESULT_SUCCESS);
79 rb.Push<u32>(consumed);
80 rb.Push<u64>(performance);
81 rb.Push<u32>(sample_count);
82 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
83 }
84
85 bool Decoder_DecodeInterleaved(
86 u32& consumed, u32& sample_count, const std::vector<u8>& input,
87 std::vector<opus_int16>& output,
88 std::optional<std::reference_wrapper<u64>> performance_time = std::nullopt) {
89 const auto start_time = std::chrono::high_resolution_clock::now();
64 std::size_t raw_output_sz = output.size() * sizeof(opus_int16); 90 std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
65 if (sizeof(OpusHeader) > input.size()) 91 if (sizeof(OpusHeader) > input.size())
66 return false; 92 return false;
@@ -80,8 +106,13 @@ private:
80 (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0); 106 (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0);
81 if (out_sample_count < 0) 107 if (out_sample_count < 0)
82 return false; 108 return false;
109 const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
83 sample_count = out_sample_count; 110 sample_count = out_sample_count;
84 consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz); 111 consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
112 if (performance_time.has_value()) {
113 performance_time->get() =
114 std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count();
115 }
85 return true; 116 return true;
86 } 117 }
87 118
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/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/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 7555bbe7d..c41ef7058 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -15,6 +15,11 @@
15#include "video_core/renderer_base.h" 15#include "video_core/renderer_base.h"
16 16
17namespace Service::Nvidia::Devices { 17namespace Service::Nvidia::Devices {
18namespace NvErrCodes {
19enum {
20 InvalidNmapHandle = -22,
21};
22}
18 23
19nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} 24nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
20nvhost_as_gpu::~nvhost_as_gpu() = default; 25nvhost_as_gpu::~nvhost_as_gpu() = default;
@@ -79,14 +84,16 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
79 std::memcpy(entries.data(), input.data(), input.size()); 84 std::memcpy(entries.data(), input.data(), input.size());
80 85
81 auto& gpu = Core::System::GetInstance().GPU(); 86 auto& gpu = Core::System::GetInstance().GPU();
82
83 for (const auto& entry : entries) { 87 for (const auto& entry : entries) {
84 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", 88 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
85 entry.offset, entry.nvmap_handle, entry.pages); 89 entry.offset, entry.nvmap_handle, entry.pages);
86 Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10; 90 Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10;
87
88 auto object = nvmap_dev->GetObject(entry.nvmap_handle); 91 auto object = nvmap_dev->GetObject(entry.nvmap_handle);
89 ASSERT(object); 92 if (!object) {
93 LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle);
94 std::memcpy(output.data(), entries.data(), output.size());
95 return static_cast<u32>(NvErrCodes::InvalidNmapHandle);
96 }
90 97
91 ASSERT(object->status == nvmap::Object::Status::Allocated); 98 ASSERT(object->status == nvmap::Object::Status::Allocated);
92 99
@@ -167,10 +174,11 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
167 auto& system_instance = Core::System::GetInstance(); 174 auto& system_instance = Core::System::GetInstance();
168 175
169 // Remove this memory region from the rasterizer cache. 176 // Remove this memory region from the rasterizer cache.
170 system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(params.offset,
171 itr->second.size);
172
173 auto& gpu = system_instance.GPU(); 177 auto& gpu = system_instance.GPU();
178 auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
179 ASSERT(cpu_addr);
180 system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(*cpu_addr, itr->second.size);
181
174 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size); 182 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
175 183
176 buffer_mappings.erase(itr->second.offset); 184 buffer_mappings.erase(itr->second.offset);
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index a2287cc1b..43651d8a6 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -11,6 +11,13 @@
11 11
12namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
13 13
14namespace NvErrCodes {
15enum {
16 OperationNotPermitted = -1,
17 InvalidValue = -22,
18};
19}
20
14nvmap::nvmap() = default; 21nvmap::nvmap() = default;
15nvmap::~nvmap() = default; 22nvmap::~nvmap() = default;
16 23
@@ -44,7 +51,11 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& o
44u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { 51u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
45 IocCreateParams params; 52 IocCreateParams params;
46 std::memcpy(&params, input.data(), sizeof(params)); 53 std::memcpy(&params, input.data(), sizeof(params));
54 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
47 55
56 if (!params.size) {
57 return static_cast<u32>(NvErrCodes::InvalidValue);
58 }
48 // Create a new nvmap object and obtain a handle to it. 59 // Create a new nvmap object and obtain a handle to it.
49 auto object = std::make_shared<Object>(); 60 auto object = std::make_shared<Object>();
50 object->id = next_id++; 61 object->id = next_id++;
@@ -55,8 +66,6 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
55 u32 handle = next_handle++; 66 u32 handle = next_handle++;
56 handles[handle] = std::move(object); 67 handles[handle] = std::move(object);
57 68
58 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
59
60 params.handle = handle; 69 params.handle = handle;
61 70
62 std::memcpy(output.data(), &params, sizeof(params)); 71 std::memcpy(output.data(), &params, sizeof(params));
@@ -66,9 +75,29 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
66u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { 75u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
67 IocAllocParams params; 76 IocAllocParams params;
68 std::memcpy(&params, input.data(), sizeof(params)); 77 std::memcpy(&params, input.data(), sizeof(params));
78 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
79
80 if (!params.handle) {
81 return static_cast<u32>(NvErrCodes::InvalidValue);
82 }
83
84 if ((params.align - 1) & params.align) {
85 return static_cast<u32>(NvErrCodes::InvalidValue);
86 }
87
88 const u32 min_alignment = 0x1000;
89 if (params.align < min_alignment) {
90 params.align = min_alignment;
91 }
69 92
70 auto object = GetObject(params.handle); 93 auto object = GetObject(params.handle);
71 ASSERT(object); 94 if (!object) {
95 return static_cast<u32>(NvErrCodes::InvalidValue);
96 }
97
98 if (object->status == Object::Status::Allocated) {
99 return static_cast<u32>(NvErrCodes::OperationNotPermitted);
100 }
72 101
73 object->flags = params.flags; 102 object->flags = params.flags;
74 object->align = params.align; 103 object->align = params.align;
@@ -76,8 +105,6 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
76 object->addr = params.addr; 105 object->addr = params.addr;
77 object->status = Object::Status::Allocated; 106 object->status = Object::Status::Allocated;
78 107
79 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
80
81 std::memcpy(output.data(), &params, sizeof(params)); 108 std::memcpy(output.data(), &params, sizeof(params));
82 return 0; 109 return 0;
83} 110}
@@ -88,8 +115,14 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
88 115
89 LOG_WARNING(Service_NVDRV, "called"); 116 LOG_WARNING(Service_NVDRV, "called");
90 117
118 if (!params.handle) {
119 return static_cast<u32>(NvErrCodes::InvalidValue);
120 }
121
91 auto object = GetObject(params.handle); 122 auto object = GetObject(params.handle);
92 ASSERT(object); 123 if (!object) {
124 return static_cast<u32>(NvErrCodes::OperationNotPermitted);
125 }
93 126
94 params.id = object->id; 127 params.id = object->id;
95 128
@@ -105,7 +138,14 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
105 138
106 auto itr = std::find_if(handles.begin(), handles.end(), 139 auto itr = std::find_if(handles.begin(), handles.end(),
107 [&](const auto& entry) { return entry.second->id == params.id; }); 140 [&](const auto& entry) { return entry.second->id == params.id; });
108 ASSERT(itr != handles.end()); 141 if (itr == handles.end()) {
142 return static_cast<u32>(NvErrCodes::InvalidValue);
143 }
144
145 auto& object = itr->second;
146 if (object->status != Object::Status::Allocated) {
147 return static_cast<u32>(NvErrCodes::InvalidValue);
148 }
109 149
110 itr->second->refcount++; 150 itr->second->refcount++;
111 151
@@ -125,8 +165,13 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
125 LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param); 165 LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param);
126 166
127 auto object = GetObject(params.handle); 167 auto object = GetObject(params.handle);
128 ASSERT(object); 168 if (!object) {
129 ASSERT(object->status == Object::Status::Allocated); 169 return static_cast<u32>(NvErrCodes::InvalidValue);
170 }
171
172 if (object->status != Object::Status::Allocated) {
173 return static_cast<u32>(NvErrCodes::OperationNotPermitted);
174 }
130 175
131 switch (static_cast<ParamTypes>(params.param)) { 176 switch (static_cast<ParamTypes>(params.param)) {
132 case ParamTypes::Size: 177 case ParamTypes::Size:
@@ -163,9 +208,12 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
163 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 208 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
164 209
165 auto itr = handles.find(params.handle); 210 auto itr = handles.find(params.handle);
166 ASSERT(itr != handles.end()); 211 if (itr == handles.end()) {
167 212 return static_cast<u32>(NvErrCodes::InvalidValue);
168 ASSERT(itr->second->refcount > 0); 213 }
214 if (!itr->second->refcount) {
215 return static_cast<u32>(NvErrCodes::InvalidValue);
216 }
169 217
170 itr->second->refcount--; 218 itr->second->refcount--;
171 219
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/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/elf.cpp b/src/core/loader/elf.cpp
index e67b49fc9..6057c7f26 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -9,16 +9,11 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/file_util.h" 10#include "common/file_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "core/core.h"
13#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/vm_manager.h" 13#include "core/hle/kernel/vm_manager.h"
16#include "core/loader/elf.h" 14#include "core/loader/elf.h"
17#include "core/memory.h" 15#include "core/memory.h"
18 16
19using Kernel::CodeSet;
20using Kernel::SharedPtr;
21
22//////////////////////////////////////////////////////////////////////////////////////////////////// 17////////////////////////////////////////////////////////////////////////////////////////////////////
23// ELF Header Constants 18// ELF Header Constants
24 19
@@ -211,7 +206,7 @@ public:
211 u32 GetFlags() const { 206 u32 GetFlags() const {
212 return (u32)(header->e_flags); 207 return (u32)(header->e_flags);
213 } 208 }
214 SharedPtr<CodeSet> LoadInto(VAddr vaddr); 209 Kernel::CodeSet LoadInto(VAddr vaddr);
215 210
216 int GetNumSegments() const { 211 int GetNumSegments() const {
217 return (int)(header->e_phnum); 212 return (int)(header->e_phnum);
@@ -274,7 +269,7 @@ const char* ElfReader::GetSectionName(int section) const {
274 return nullptr; 269 return nullptr;
275} 270}
276 271
277SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) { 272Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
278 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); 273 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
279 274
280 // Should we relocate? 275 // Should we relocate?
@@ -302,8 +297,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
302 std::vector<u8> program_image(total_image_size); 297 std::vector<u8> program_image(total_image_size);
303 std::size_t current_image_position = 0; 298 std::size_t current_image_position = 0;
304 299
305 auto& kernel = Core::System::GetInstance().Kernel(); 300 Kernel::CodeSet codeset;
306 SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, "");
307 301
308 for (unsigned int i = 0; i < header->e_phnum; ++i) { 302 for (unsigned int i = 0; i < header->e_phnum; ++i) {
309 const Elf32_Phdr* p = &segments[i]; 303 const Elf32_Phdr* p = &segments[i];
@@ -311,14 +305,14 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
311 p->p_vaddr, p->p_filesz, p->p_memsz); 305 p->p_vaddr, p->p_filesz, p->p_memsz);
312 306
313 if (p->p_type == PT_LOAD) { 307 if (p->p_type == PT_LOAD) {
314 CodeSet::Segment* codeset_segment; 308 Kernel::CodeSet::Segment* codeset_segment;
315 u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); 309 u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X);
316 if (permission_flags == (PF_R | PF_X)) { 310 if (permission_flags == (PF_R | PF_X)) {
317 codeset_segment = &codeset->CodeSegment(); 311 codeset_segment = &codeset.CodeSegment();
318 } else if (permission_flags == (PF_R)) { 312 } else if (permission_flags == (PF_R)) {
319 codeset_segment = &codeset->RODataSegment(); 313 codeset_segment = &codeset.RODataSegment();
320 } else if (permission_flags == (PF_R | PF_W)) { 314 } else if (permission_flags == (PF_R | PF_W)) {
321 codeset_segment = &codeset->DataSegment(); 315 codeset_segment = &codeset.DataSegment();
322 } else { 316 } else {
323 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, 317 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
324 p->p_flags); 318 p->p_flags);
@@ -345,8 +339,8 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
345 } 339 }
346 } 340 }
347 341
348 codeset->entrypoint = base_addr + header->e_entry; 342 codeset.entrypoint = base_addr + header->e_entry;
349 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 343 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
350 344
351 LOG_DEBUG(Loader, "Done loading."); 345 LOG_DEBUG(Loader, "Done loading.");
352 346
@@ -397,11 +391,11 @@ ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
397 391
398 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 392 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
399 ElfReader elf_reader(&buffer[0]); 393 ElfReader elf_reader(&buffer[0]);
400 SharedPtr<CodeSet> codeset = elf_reader.LoadInto(base_address); 394 Kernel::CodeSet codeset = elf_reader.LoadInto(base_address);
401 codeset->name = file->GetName(); 395 const VAddr entry_point = codeset.entrypoint;
402 396
403 process.LoadModule(codeset, codeset->entrypoint); 397 process.LoadModule(std::move(codeset), entry_point);
404 process.Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE); 398 process.Run(entry_point, 48, Memory::DEFAULT_STACK_SIZE);
405 399
406 is_loaded = true; 400 is_loaded = true;
407 return ResultStatus::Success; 401 return ResultStatus::Success;
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 25dd3f04e..243b499f2 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -14,7 +14,6 @@
14#include "core/file_sys/control_metadata.h" 14#include "core/file_sys/control_metadata.h"
15#include "core/file_sys/vfs_offset.h" 15#include "core/file_sys/vfs_offset.h"
16#include "core/gdbstub/gdbstub.h" 16#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/kernel.h"
18#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
19#include "core/hle/kernel/vm_manager.h" 18#include "core/hle/kernel/vm_manager.h"
20#include "core/loader/nro.h" 19#include "core/loader/nro.h"
@@ -128,10 +127,10 @@ static constexpr u32 PageAlignSize(u32 size) {
128 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 127 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
129} 128}
130 129
131bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { 130bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
132 // Read NSO header 131 // Read NSO header
133 NroHeader nro_header{}; 132 NroHeader nro_header{};
134 if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { 133 if (sizeof(NroHeader) != file.ReadObject(&nro_header)) {
135 return {}; 134 return {};
136 } 135 }
137 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { 136 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
@@ -139,22 +138,21 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
139 } 138 }
140 139
141 // Build program image 140 // Build program image
142 auto& kernel = Core::System::GetInstance().Kernel(); 141 std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size));
143 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, "");
144 std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size));
145 if (program_image.size() != PageAlignSize(nro_header.file_size)) { 142 if (program_image.size() != PageAlignSize(nro_header.file_size)) {
146 return {}; 143 return {};
147 } 144 }
148 145
146 Kernel::CodeSet codeset;
149 for (std::size_t i = 0; i < nro_header.segments.size(); ++i) { 147 for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
150 codeset->segments[i].addr = nro_header.segments[i].offset; 148 codeset.segments[i].addr = nro_header.segments[i].offset;
151 codeset->segments[i].offset = nro_header.segments[i].offset; 149 codeset.segments[i].offset = nro_header.segments[i].offset;
152 codeset->segments[i].size = PageAlignSize(nro_header.segments[i].size); 150 codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
153 } 151 }
154 152
155 if (!Settings::values.program_args.empty()) { 153 if (!Settings::values.program_args.empty()) {
156 const auto arg_data = Settings::values.program_args; 154 const auto arg_data = Settings::values.program_args;
157 codeset->DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; 155 codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
158 NSOArgumentHeader args_header{ 156 NSOArgumentHeader args_header{
159 NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; 157 NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
160 const auto end_offset = program_image.size(); 158 const auto end_offset = program_image.size();
@@ -176,16 +174,15 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
176 // Resize program image to include .bss section and page align each section 174 // Resize program image to include .bss section and page align each section
177 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); 175 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
178 } 176 }
179 codeset->DataSegment().size += bss_size; 177 codeset.DataSegment().size += bss_size;
180 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 178 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
181 179
182 // Load codeset for current process 180 // Load codeset for current process
183 codeset->name = file->GetName(); 181 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
184 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 182 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
185 Core::CurrentProcess()->LoadModule(codeset, load_base);
186 183
187 // Register module with GDBStub 184 // Register module with GDBStub
188 GDBStub::RegisterModule(codeset->name, load_base, load_base); 185 GDBStub::RegisterModule(file.GetName(), load_base, load_base);
189 186
190 return true; 187 return true;
191} 188}
@@ -198,7 +195,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
198 // Load NRO 195 // Load NRO
199 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 196 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
200 197
201 if (!LoadNro(file, base_address)) { 198 if (!LoadNro(*file, base_address)) {
202 return ResultStatus::ErrorLoadingNRO; 199 return ResultStatus::ErrorLoadingNRO;
203 } 200 }
204 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 28c6dd9b7..68efca5c0 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -12,7 +12,6 @@
12#include "core/core.h" 12#include "core/core.h"
13#include "core/file_sys/patch_manager.h" 13#include "core/file_sys/patch_manager.h"
14#include "core/gdbstub/gdbstub.h" 14#include "core/gdbstub/gdbstub.h"
15#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
17#include "core/hle/kernel/vm_manager.h" 16#include "core/hle/kernel/vm_manager.h"
18#include "core/loader/nso.h" 17#include "core/loader/nso.h"
@@ -94,42 +93,38 @@ static constexpr u32 PageAlignSize(u32 size) {
94 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 93 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
95} 94}
96 95
97VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, 96std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAddr load_base,
98 bool should_pass_arguments, 97 bool should_pass_arguments,
99 boost::optional<FileSys::PatchManager> pm) { 98 std::optional<FileSys::PatchManager> pm) {
100 if (file == nullptr) 99 if (file.GetSize() < sizeof(NsoHeader))
101 return {};
102
103 if (file->GetSize() < sizeof(NsoHeader))
104 return {}; 100 return {};
105 101
106 NsoHeader nso_header{}; 102 NsoHeader nso_header{};
107 if (sizeof(NsoHeader) != file->ReadObject(&nso_header)) 103 if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
108 return {}; 104 return {};
109 105
110 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) 106 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
111 return {}; 107 return {};
112 108
113 // Build program image 109 // Build program image
114 auto& kernel = Core::System::GetInstance().Kernel(); 110 Kernel::CodeSet codeset;
115 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, "");
116 std::vector<u8> program_image; 111 std::vector<u8> program_image;
117 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) {
118 std::vector<u8> data = 113 std::vector<u8> data =
119 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);
120 if (nso_header.IsSegmentCompressed(i)) { 115 if (nso_header.IsSegmentCompressed(i)) {
121 data = DecompressSegment(data, nso_header.segments[i]); 116 data = DecompressSegment(data, nso_header.segments[i]);
122 } 117 }
123 program_image.resize(nso_header.segments[i].location); 118 program_image.resize(nso_header.segments[i].location);
124 program_image.insert(program_image.end(), data.begin(), data.end()); 119 program_image.insert(program_image.end(), data.begin(), data.end());
125 codeset->segments[i].addr = nso_header.segments[i].location; 120 codeset.segments[i].addr = nso_header.segments[i].location;
126 codeset->segments[i].offset = nso_header.segments[i].location; 121 codeset.segments[i].offset = nso_header.segments[i].location;
127 codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size())); 122 codeset.segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
128 } 123 }
129 124
130 if (should_pass_arguments && !Settings::values.program_args.empty()) { 125 if (should_pass_arguments && !Settings::values.program_args.empty()) {
131 const auto arg_data = Settings::values.program_args; 126 const auto arg_data = Settings::values.program_args;
132 codeset->DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; 127 codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
133 NSOArgumentHeader args_header{ 128 NSOArgumentHeader args_header{
134 NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; 129 NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
135 const auto end_offset = program_image.size(); 130 const auto end_offset = program_image.size();
@@ -154,12 +149,12 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
154 // Resize program image to include .bss section and page align each section 149 // Resize program image to include .bss section and page align each section
155 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); 150 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
156 } 151 }
157 codeset->DataSegment().size += bss_size; 152 codeset.DataSegment().size += bss_size;
158 const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; 153 const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
159 program_image.resize(image_size); 154 program_image.resize(image_size);
160 155
161 // Apply patches if necessary 156 // Apply patches if necessary
162 if (pm != boost::none && pm->HasNSOPatch(nso_header.build_id)) { 157 if (pm && pm->HasNSOPatch(nso_header.build_id)) {
163 std::vector<u8> pi_header(program_image.size() + 0x100); 158 std::vector<u8> pi_header(program_image.size() + 0x100);
164 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); 159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
165 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());
@@ -170,12 +165,11 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
170 } 165 }
171 166
172 // Load codeset for current process 167 // Load codeset for current process
173 codeset->name = file->GetName(); 168 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
174 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 169 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
175 Core::CurrentProcess()->LoadModule(codeset, load_base);
176 170
177 // Register module with GDBStub 171 // Register module with GDBStub
178 GDBStub::RegisterModule(codeset->name, load_base, load_base); 172 GDBStub::RegisterModule(file.GetName(), load_base, load_base);
179 173
180 return load_base + image_size; 174 return load_base + image_size;
181} 175}
@@ -187,7 +181,9 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
187 181
188 // Load module 182 // Load module
189 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 183 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
190 LoadModule(file, base_address, true); 184 if (!LoadModule(*file, base_address, true)) {
185 return ResultStatus::ErrorLoadingNSO;
186 }
191 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); 187 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
192 188
193 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/nsp.cpp b/src/core/loader/nsp.cpp
index 5534ce01c..13e57848d 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -35,7 +35,7 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
35 return; 35 return;
36 36
37 std::tie(nacp_file, icon_file) = 37 std::tie(nacp_file, icon_file) =
38 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(control_nca); 38 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
39} 39}
40 40
41AppLoader_NSP::~AppLoader_NSP() = default; 41AppLoader_NSP::~AppLoader_NSP() = default;
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index b006594a6..db91cd01e 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -49,7 +49,7 @@ private:
49 std::unique_ptr<AppLoader> secondary_loader; 49 std::unique_ptr<AppLoader> secondary_loader;
50 50
51 FileSys::VirtualFile icon_file; 51 FileSys::VirtualFile icon_file;
52 std::shared_ptr<FileSys::NACP> nacp_file; 52 std::unique_ptr<FileSys::NACP> nacp_file;
53 u64 title_id; 53 u64 title_id;
54}; 54};
55 55
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index ee5452eb9..7a619acb4 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -30,7 +30,7 @@ AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
30 return; 30 return;
31 31
32 std::tie(nacp_file, icon_file) = 32 std::tie(nacp_file, icon_file) =
33 FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(control_nca); 33 FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(*control_nca);
34} 34}
35 35
36AppLoader_XCI::~AppLoader_XCI() = default; 36AppLoader_XCI::~AppLoader_XCI() = default;
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 770ed1437..46f8dfc9e 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -49,7 +49,7 @@ private:
49 std::unique_ptr<AppLoader_NCA> nca_loader; 49 std::unique_ptr<AppLoader_NCA> nca_loader;
50 50
51 FileSys::VirtualFile icon_file; 51 FileSys::VirtualFile icon_file;
52 std::shared_ptr<FileSys::NACP> nacp_file; 52 std::unique_ptr<FileSys::NACP> nacp_file;
53}; 53};
54 54
55} // namespace Loader 55} // namespace Loader
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/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index c0a57e71f..37e15bad0 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -15,7 +15,8 @@ namespace ArmTests {
15TestEnvironment::TestEnvironment(bool mutable_memory_) 15TestEnvironment::TestEnvironment(bool mutable_memory_)
16 : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { 16 : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
17 17
18 Core::CurrentProcess() = Kernel::Process::Create(kernel, ""); 18 auto process = Kernel::Process::Create(kernel, "");
19 kernel.MakeCurrentProcess(process.get());
19 page_table = &Core::CurrentProcess()->VMManager().page_table; 20 page_table = &Core::CurrentProcess()->VMManager().page_table;
20 21
21 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); 22 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 912e785b9..597b279b9 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -62,14 +62,16 @@ void Fermi2D::HandleSurfaceCopy() {
62 u8* dst_buffer = Memory::GetPointer(dest_cpu); 62 u8* dst_buffer = Memory::GetPointer(dest_cpu);
63 if (!regs.src.linear && regs.dst.linear) { 63 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. 64 // 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, 65 Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
66 dst_bytes_per_pixel, src_buffer, dst_buffer, true, 66 src_bytes_per_pixel, dst_bytes_per_pixel, src_buffer,
67 regs.src.BlockHeight()); 67 dst_buffer, true, regs.src.BlockHeight(),
68 regs.src.BlockDepth());
68 } else { 69 } else {
69 // If the input is linear and the output is tiled, swizzle the input and copy it over. 70 // 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, 71 Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
71 dst_bytes_per_pixel, dst_buffer, src_buffer, false, 72 src_bytes_per_pixel, dst_bytes_per_pixel, dst_buffer,
72 regs.dst.BlockHeight()); 73 src_buffer, false, regs.dst.BlockHeight(),
74 regs.dst.BlockDepth());
73 } 75 }
74 } 76 }
75} 77}
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 81d15c62a..2a6e8bbbb 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -36,9 +36,9 @@ public:
36 RenderTargetFormat format; 36 RenderTargetFormat format;
37 BitField<0, 1, u32> linear; 37 BitField<0, 1, u32> linear;
38 union { 38 union {
39 BitField<0, 4, u32> block_depth; 39 BitField<0, 4, u32> block_width;
40 BitField<4, 4, u32> block_height; 40 BitField<4, 4, u32> block_height;
41 BitField<8, 4, u32> block_width; 41 BitField<8, 4, u32> block_depth;
42 }; 42 };
43 u32 depth; 43 u32 depth;
44 u32 layer; 44 u32 layer;
@@ -53,10 +53,20 @@ public:
53 address_low); 53 address_low);
54 } 54 }
55 55
56 u32 BlockWidth() const {
57 // The block width is stored in log2 format.
58 return 1 << block_width;
59 }
60
56 u32 BlockHeight() const { 61 u32 BlockHeight() const {
57 // The block height is stored in log2 format. 62 // The block height is stored in log2 format.
58 return 1 << block_height; 63 return 1 << block_height;
59 } 64 }
65
66 u32 BlockDepth() const {
67 // The block depth is stored in log2 format.
68 return 1 << block_depth;
69 }
60 }; 70 };
61 static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size"); 71 static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size");
62 72
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 4290da33f..c8d1b6478 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -347,6 +347,16 @@ public:
347 DecrWrap = 8, 347 DecrWrap = 8,
348 }; 348 };
349 349
350 enum class MemoryLayout : u32 {
351 Linear = 0,
352 BlockLinear = 1,
353 };
354
355 enum class InvMemoryLayout : u32 {
356 BlockLinear = 0,
357 Linear = 1,
358 };
359
350 struct Cull { 360 struct Cull {
351 enum class FrontFace : u32 { 361 enum class FrontFace : u32 {
352 ClockWise = 0x0900, 362 ClockWise = 0x0900,
@@ -432,7 +442,12 @@ public:
432 u32 width; 442 u32 width;
433 u32 height; 443 u32 height;
434 Tegra::RenderTargetFormat format; 444 Tegra::RenderTargetFormat format;
435 u32 block_dimensions; 445 union {
446 BitField<0, 3, u32> block_width;
447 BitField<4, 3, u32> block_height;
448 BitField<8, 3, u32> block_depth;
449 BitField<12, 1, InvMemoryLayout> type;
450 } memory_layout;
436 u32 array_mode; 451 u32 array_mode;
437 u32 layer_stride; 452 u32 layer_stride;
438 u32 base_layer; 453 u32 base_layer;
@@ -532,7 +547,21 @@ public:
532 INSERT_PADDING_WORDS(0x3); 547 INSERT_PADDING_WORDS(0x3);
533 s32 clear_stencil; 548 s32 clear_stencil;
534 549
535 INSERT_PADDING_WORDS(0x6C); 550 INSERT_PADDING_WORDS(0x17);
551
552 struct {
553 u32 enable;
554 union {
555 BitField<0, 16, u32> min_x;
556 BitField<16, 16, u32> max_x;
557 };
558 union {
559 BitField<0, 16, u32> min_y;
560 BitField<16, 16, u32> max_y;
561 };
562 } scissor_test;
563
564 INSERT_PADDING_WORDS(0x52);
536 565
537 s32 stencil_back_func_ref; 566 s32 stencil_back_func_ref;
538 u32 stencil_back_mask; 567 u32 stencil_back_mask;
@@ -548,7 +577,12 @@ public:
548 u32 address_high; 577 u32 address_high;
549 u32 address_low; 578 u32 address_low;
550 Tegra::DepthFormat format; 579 Tegra::DepthFormat format;
551 u32 block_dimensions; 580 union {
581 BitField<0, 4, u32> block_width;
582 BitField<4, 4, u32> block_height;
583 BitField<8, 4, u32> block_depth;
584 BitField<20, 1, InvMemoryLayout> type;
585 } memory_layout;
552 u32 layer_stride; 586 u32 layer_stride;
553 587
554 GPUVAddr Address() const { 588 GPUVAddr Address() const {
@@ -1002,6 +1036,7 @@ ASSERT_REG_POSITION(vertex_buffer, 0x35D);
1002ASSERT_REG_POSITION(clear_color[0], 0x360); 1036ASSERT_REG_POSITION(clear_color[0], 0x360);
1003ASSERT_REG_POSITION(clear_depth, 0x364); 1037ASSERT_REG_POSITION(clear_depth, 0x364);
1004ASSERT_REG_POSITION(clear_stencil, 0x368); 1038ASSERT_REG_POSITION(clear_stencil, 0x368);
1039ASSERT_REG_POSITION(scissor_test, 0x380);
1005ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); 1040ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
1006ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); 1041ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
1007ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); 1042ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index aa7481b8c..bf2a21bb6 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -68,12 +68,14 @@ void MaxwellDMA::HandleCopy() {
68 68
69 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { 69 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
70 // If the input is tiled and the output is linear, deswizzle the input and copy it over. 70 // 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, 71 Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y,
72 dst_buffer, true, regs.src_params.BlockHeight()); 72 regs.src_params.size_z, 1, 1, src_buffer, dst_buffer, true,
73 regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
73 } else { 74 } else {
74 // If the input is linear and the output is tiled, swizzle the input and copy it over. 75 // 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, 76 Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y,
76 src_buffer, false, regs.dst_params.BlockHeight()); 77 regs.dst_params.size_z, 1, 1, dst_buffer, src_buffer, false,
78 regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
77 } 79 }
78} 80}
79 81
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 311ccb616..df19e02e2 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -43,6 +43,10 @@ public:
43 u32 BlockHeight() const { 43 u32 BlockHeight() const {
44 return 1 << block_height; 44 return 1 << block_height;
45 } 45 }
46
47 u32 BlockDepth() const {
48 return 1 << block_depth;
49 }
46 }; 50 };
47 51
48 static_assert(sizeof(Parameters) == 24, "Parameters has wrong size"); 52 static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index b1f137b9c..f356f9a03 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -214,6 +214,18 @@ enum class IMinMaxExchange : u64 {
214 XHi = 3, 214 XHi = 3,
215}; 215};
216 216
217enum class VmadType : u64 {
218 Size16_Low = 0,
219 Size16_High = 1,
220 Size32 = 2,
221 Invalid = 3,
222};
223
224enum class VmadShr : u64 {
225 Shr7 = 1,
226 Shr15 = 2,
227};
228
217enum class XmadMode : u64 { 229enum class XmadMode : u64 {
218 None = 0, 230 None = 0,
219 CLo = 1, 231 CLo = 1,
@@ -255,7 +267,7 @@ enum class ControlCode : u64 {
255 GTU = 12, 267 GTU = 12,
256 NEU = 13, 268 NEU = 13,
257 GEU = 14, 269 GEU = 14,
258 // 270 T = 15,
259 OFF = 16, 271 OFF = 16,
260 LO = 17, 272 LO = 17,
261 SFF = 18, 273 SFF = 18,
@@ -314,6 +326,15 @@ enum class TextureMiscMode : u64 {
314 PTP, 326 PTP,
315}; 327};
316 328
329enum class IsberdMode : u64 {
330 None = 0,
331 Patch = 1,
332 Prim = 2,
333 Attr = 3,
334};
335
336enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 };
337
317enum class IpaInterpMode : u64 { 338enum class IpaInterpMode : u64 {
318 Linear = 0, 339 Linear = 0,
319 Perspective = 1, 340 Perspective = 1,
@@ -340,6 +361,87 @@ struct IpaMode {
340 } 361 }
341}; 362};
342 363
364enum class SystemVariable : u64 {
365 LaneId = 0x00,
366 VirtCfg = 0x02,
367 VirtId = 0x03,
368 Pm0 = 0x04,
369 Pm1 = 0x05,
370 Pm2 = 0x06,
371 Pm3 = 0x07,
372 Pm4 = 0x08,
373 Pm5 = 0x09,
374 Pm6 = 0x0a,
375 Pm7 = 0x0b,
376 OrderingTicket = 0x0f,
377 PrimType = 0x10,
378 InvocationId = 0x11,
379 Ydirection = 0x12,
380 ThreadKill = 0x13,
381 ShaderType = 0x14,
382 DirectBeWriteAddressLow = 0x15,
383 DirectBeWriteAddressHigh = 0x16,
384 DirectBeWriteEnabled = 0x17,
385 MachineId0 = 0x18,
386 MachineId1 = 0x19,
387 MachineId2 = 0x1a,
388 MachineId3 = 0x1b,
389 Affinity = 0x1c,
390 InvocationInfo = 0x1d,
391 WscaleFactorXY = 0x1e,
392 WscaleFactorZ = 0x1f,
393 Tid = 0x20,
394 TidX = 0x21,
395 TidY = 0x22,
396 TidZ = 0x23,
397 CtaParam = 0x24,
398 CtaIdX = 0x25,
399 CtaIdY = 0x26,
400 CtaIdZ = 0x27,
401 NtId = 0x28,
402 CirQueueIncrMinusOne = 0x29,
403 Nlatc = 0x2a,
404 SmSpaVersion = 0x2c,
405 MultiPassShaderInfo = 0x2d,
406 LwinHi = 0x2e,
407 SwinHi = 0x2f,
408 SwinLo = 0x30,
409 SwinSz = 0x31,
410 SmemSz = 0x32,
411 SmemBanks = 0x33,
412 LwinLo = 0x34,
413 LwinSz = 0x35,
414 LmemLosz = 0x36,
415 LmemHioff = 0x37,
416 EqMask = 0x38,
417 LtMask = 0x39,
418 LeMask = 0x3a,
419 GtMask = 0x3b,
420 GeMask = 0x3c,
421 RegAlloc = 0x3d,
422 CtxAddr = 0x3e, // .fmask = F_SM50
423 BarrierAlloc = 0x3e, // .fmask = F_SM60
424 GlobalErrorStatus = 0x40,
425 WarpErrorStatus = 0x42,
426 WarpErrorStatusClear = 0x43,
427 PmHi0 = 0x48,
428 PmHi1 = 0x49,
429 PmHi2 = 0x4a,
430 PmHi3 = 0x4b,
431 PmHi4 = 0x4c,
432 PmHi5 = 0x4d,
433 PmHi6 = 0x4e,
434 PmHi7 = 0x4f,
435 ClockLo = 0x50,
436 ClockHi = 0x51,
437 GlobalTimerLo = 0x52,
438 GlobalTimerHi = 0x53,
439 HwTaskId = 0x60,
440 CircularQueueEntryIndex = 0x61,
441 CircularQueueEntryAddressLow = 0x62,
442 CircularQueueEntryAddressHigh = 0x63,
443};
444
343union Instruction { 445union Instruction {
344 Instruction& operator=(const Instruction& instr) { 446 Instruction& operator=(const Instruction& instr) {
345 value = instr.value; 447 value = instr.value;
@@ -362,6 +464,7 @@ union Instruction {
362 BitField<48, 16, u64> opcode; 464 BitField<48, 16, u64> opcode;
363 465
364 union { 466 union {
467 BitField<20, 16, u64> imm20_16;
365 BitField<20, 19, u64> imm20_19; 468 BitField<20, 19, u64> imm20_19;
366 BitField<20, 32, s64> imm20_32; 469 BitField<20, 32, s64> imm20_32;
367 BitField<45, 1, u64> negate_b; 470 BitField<45, 1, u64> negate_b;
@@ -403,6 +506,10 @@ union Instruction {
403 } 506 }
404 } lop3; 507 } lop3;
405 508
509 u16 GetImm20_16() const {
510 return static_cast<u16>(imm20_16);
511 }
512
406 u32 GetImm20_19() const { 513 u32 GetImm20_19() const {
407 u32 imm{static_cast<u32>(imm20_19)}; 514 u32 imm{static_cast<u32>(imm20_19)};
408 imm <<= 12; 515 imm <<= 12;
@@ -915,6 +1022,35 @@ union Instruction {
915 } bra; 1022 } bra;
916 1023
917 union { 1024 union {
1025 BitField<39, 1, u64> emit; // EmitVertex
1026 BitField<40, 1, u64> cut; // EndPrimitive
1027 } out;
1028
1029 union {
1030 BitField<31, 1, u64> skew;
1031 BitField<32, 1, u64> o;
1032 BitField<33, 2, IsberdMode> mode;
1033 BitField<47, 2, IsberdShift> shift;
1034 } isberd;
1035
1036 union {
1037 BitField<48, 1, u64> signed_a;
1038 BitField<38, 1, u64> is_byte_chunk_a;
1039 BitField<36, 2, VmadType> type_a;
1040 BitField<36, 2, u64> byte_height_a;
1041
1042 BitField<49, 1, u64> signed_b;
1043 BitField<50, 1, u64> use_register_b;
1044 BitField<30, 1, u64> is_byte_chunk_b;
1045 BitField<28, 2, VmadType> type_b;
1046 BitField<28, 2, u64> byte_height_b;
1047
1048 BitField<51, 2, VmadShr> shr;
1049 BitField<55, 1, u64> saturate; // Saturates the result (a * b + c)
1050 BitField<47, 1, u64> cc;
1051 } vmad;
1052
1053 union {
918 BitField<20, 16, u64> imm20_16; 1054 BitField<20, 16, u64> imm20_16;
919 BitField<36, 1, u64> product_shift_left; 1055 BitField<36, 1, u64> product_shift_left;
920 BitField<37, 1, u64> merge_37; 1056 BitField<37, 1, u64> merge_37;
@@ -936,6 +1072,10 @@ union Instruction {
936 BitField<36, 5, u64> index; 1072 BitField<36, 5, u64> index;
937 } cbuf36; 1073 } cbuf36;
938 1074
1075 // Unsure about the size of this one.
1076 // It's always used with a gpr0, so any size should be fine.
1077 BitField<20, 8, SystemVariable> sys20;
1078
939 BitField<47, 1, u64> generates_cc; 1079 BitField<47, 1, u64> generates_cc;
940 BitField<61, 1, u64> is_b_imm; 1080 BitField<61, 1, u64> is_b_imm;
941 BitField<60, 1, u64> is_b_gpr; 1081 BitField<60, 1, u64> is_b_gpr;
@@ -975,6 +1115,9 @@ public:
975 TMML, // Texture Mip Map Level 1115 TMML, // Texture Mip Map Level
976 EXIT, 1116 EXIT,
977 IPA, 1117 IPA,
1118 OUT_R, // Emit vertex/primitive
1119 ISBERD,
1120 VMAD,
978 FFMA_IMM, // Fused Multiply and Add 1121 FFMA_IMM, // Fused Multiply and Add
979 FFMA_CR, 1122 FFMA_CR,
980 FFMA_RC, 1123 FFMA_RC,
@@ -1034,6 +1177,7 @@ public:
1034 MOV_C, 1177 MOV_C,
1035 MOV_R, 1178 MOV_R,
1036 MOV_IMM, 1179 MOV_IMM,
1180 MOV_SYS,
1037 MOV32_IMM, 1181 MOV32_IMM,
1038 SHL_C, 1182 SHL_C,
1039 SHL_R, 1183 SHL_R,
@@ -1209,6 +1353,9 @@ private:
1209 INST("1101111101011---", Id::TMML, Type::Memory, "TMML"), 1353 INST("1101111101011---", Id::TMML, Type::Memory, "TMML"),
1210 INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), 1354 INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
1211 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), 1355 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
1356 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
1357 INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
1358 INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"),
1212 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), 1359 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
1213 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), 1360 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
1214 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), 1361 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
@@ -1255,6 +1402,7 @@ private:
1255 INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), 1402 INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"),
1256 INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), 1403 INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"),
1257 INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), 1404 INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"),
1405 INST("1111000011001---", Id::MOV_SYS, Type::Trivial, "MOV_SYS"),
1258 INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"), 1406 INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"),
1259 INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"), 1407 INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"),
1260 INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"), 1408 INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"),
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 209bdf181..468253033 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -255,7 +255,7 @@ DrawParameters RasterizerOpenGL::SetupDraw() {
255 return params; 255 return params;
256} 256}
257 257
258void RasterizerOpenGL::SetupShaders() { 258void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
259 MICROPROFILE_SCOPE(OpenGL_Shader); 259 MICROPROFILE_SCOPE(OpenGL_Shader);
260 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 260 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
261 261
@@ -270,6 +270,11 @@ void RasterizerOpenGL::SetupShaders() {
270 270
271 // Skip stages that are not enabled 271 // Skip stages that are not enabled
272 if (!gpu.regs.IsShaderConfigEnabled(index)) { 272 if (!gpu.regs.IsShaderConfigEnabled(index)) {
273 switch (program) {
274 case Maxwell::ShaderProgram::Geometry:
275 shader_program_manager->UseTrivialGeometryShader();
276 break;
277 }
273 continue; 278 continue;
274 } 279 }
275 280
@@ -281,18 +286,26 @@ void RasterizerOpenGL::SetupShaders() {
281 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); 286 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
282 287
283 // Bind the buffer 288 // Bind the buffer
284 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)));
285 291
286 Shader shader{shader_cache.GetStageProgram(program)}; 292 Shader shader{shader_cache.GetStageProgram(program)};
287 293
288 switch (program) { 294 switch (program) {
289 case Maxwell::ShaderProgram::VertexA: 295 case Maxwell::ShaderProgram::VertexA:
290 case Maxwell::ShaderProgram::VertexB: { 296 case Maxwell::ShaderProgram::VertexB: {
291 shader_program_manager->UseProgrammableVertexShader(shader->GetProgramHandle()); 297 shader_program_manager->UseProgrammableVertexShader(
298 shader->GetProgramHandle(primitive_mode));
299 break;
300 }
301 case Maxwell::ShaderProgram::Geometry: {
302 shader_program_manager->UseProgrammableGeometryShader(
303 shader->GetProgramHandle(primitive_mode));
292 break; 304 break;
293 } 305 }
294 case Maxwell::ShaderProgram::Fragment: { 306 case Maxwell::ShaderProgram::Fragment: {
295 shader_program_manager->UseProgrammableFragmentShader(shader->GetProgramHandle()); 307 shader_program_manager->UseProgrammableFragmentShader(
308 shader->GetProgramHandle(primitive_mode));
296 break; 309 break;
297 } 310 }
298 default: 311 default:
@@ -302,12 +315,13 @@ void RasterizerOpenGL::SetupShaders() {
302 } 315 }
303 316
304 // Configure the const buffers for this shader stage. 317 // Configure the const buffers for this shader stage.
305 current_constbuffer_bindpoint = SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), 318 current_constbuffer_bindpoint =
306 shader, current_constbuffer_bindpoint); 319 SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), shader, primitive_mode,
320 current_constbuffer_bindpoint);
307 321
308 // Configure the textures for this shader stage. 322 // Configure the textures for this shader stage.
309 current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader, 323 current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader,
310 current_texture_bindpoint); 324 primitive_mode, current_texture_bindpoint);
311 325
312 // When VertexA is enabled, we have dual vertex shaders 326 // When VertexA is enabled, we have dual vertex shaders
313 if (program == Maxwell::ShaderProgram::VertexA) { 327 if (program == Maxwell::ShaderProgram::VertexA) {
@@ -317,8 +331,6 @@ void RasterizerOpenGL::SetupShaders() {
317 } 331 }
318 332
319 state.Apply(); 333 state.Apply();
320
321 shader_program_manager->UseTrivialGeometryShader();
322} 334}
323 335
324std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { 336std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -412,6 +424,13 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
412 // 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
413 Surface color_surface = 425 Surface color_surface =
414 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
415 glFramebufferTexture2D( 434 glFramebufferTexture2D(
416 GL_DRAW_FRAMEBUFFER, 435 GL_DRAW_FRAMEBUFFER,
417 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,
@@ -422,6 +441,13 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
422 std::array<GLenum, Maxwell::NumRenderTargets> buffers; 441 std::array<GLenum, Maxwell::NumRenderTargets> buffers;
423 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { 442 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
424 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
425 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); 451 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
426 glFramebufferTexture2D( 452 glFramebufferTexture2D(
427 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), 453 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index),
@@ -441,6 +467,10 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
441 } 467 }
442 468
443 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
444 if (regs.stencil_enable) { 474 if (regs.stencil_enable) {
445 // Attach both depth and stencil 475 // Attach both depth and stencil
446 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 476 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
@@ -541,6 +571,7 @@ void RasterizerOpenGL::DrawArrays() {
541 SyncLogicOpState(); 571 SyncLogicOpState();
542 SyncCullMode(); 572 SyncCullMode();
543 SyncAlphaTest(); 573 SyncAlphaTest();
574 SyncScissorTest();
544 SyncTransformFeedback(); 575 SyncTransformFeedback();
545 SyncPointState(); 576 SyncPointState();
546 577
@@ -580,7 +611,7 @@ void RasterizerOpenGL::DrawArrays() {
580 611
581 SetupVertexArrays(); 612 SetupVertexArrays();
582 DrawParameters params = SetupDraw(); 613 DrawParameters params = SetupDraw();
583 SetupShaders(); 614 SetupShaders(params.primitive_mode);
584 615
585 buffer_cache.Unmap(); 616 buffer_cache.Unmap();
586 617
@@ -604,7 +635,14 @@ void RasterizerOpenGL::DrawArrays() {
604 635
605void RasterizerOpenGL::FlushAll() {} 636void RasterizerOpenGL::FlushAll() {}
606 637
607void 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}
608 646
609void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 647void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
610 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 648 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
@@ -614,6 +652,7 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
614} 652}
615 653
616void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { 654void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
655 FlushRegion(addr, size);
617 InvalidateRegion(addr, size); 656 InvalidateRegion(addr, size);
618} 657}
619 658
@@ -719,7 +758,7 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
719} 758}
720 759
721u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader, 760u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
722 u32 current_bindpoint) { 761 GLenum primitive_mode, u32 current_bindpoint) {
723 MICROPROFILE_SCOPE(OpenGL_UBO); 762 MICROPROFILE_SCOPE(OpenGL_UBO);
724 const auto& gpu = Core::System::GetInstance().GPU(); 763 const auto& gpu = Core::System::GetInstance().GPU();
725 const auto& maxwell3d = gpu.Maxwell3D(); 764 const auto& maxwell3d = gpu.Maxwell3D();
@@ -771,7 +810,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad
771 buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); 810 buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment));
772 811
773 // Now configure the bindpoint of the buffer inside the shader 812 // Now configure the bindpoint of the buffer inside the shader
774 glUniformBlockBinding(shader->GetProgramHandle(), 813 glUniformBlockBinding(shader->GetProgramHandle(primitive_mode),
775 shader->GetProgramResourceIndex(used_buffer), 814 shader->GetProgramResourceIndex(used_buffer),
776 current_bindpoint + bindpoint); 815 current_bindpoint + bindpoint);
777 816
@@ -787,7 +826,8 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad
787 return current_bindpoint + static_cast<u32>(entries.size()); 826 return current_bindpoint + static_cast<u32>(entries.size());
788} 827}
789 828
790u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, u32 current_unit) { 829u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
830 GLenum primitive_mode, u32 current_unit) {
791 MICROPROFILE_SCOPE(OpenGL_Texture); 831 MICROPROFILE_SCOPE(OpenGL_Texture);
792 const auto& gpu = Core::System::GetInstance().GPU(); 832 const auto& gpu = Core::System::GetInstance().GPU();
793 const auto& maxwell3d = gpu.Maxwell3D(); 833 const auto& maxwell3d = gpu.Maxwell3D();
@@ -802,8 +842,8 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
802 842
803 // Bind the uniform to the sampler. 843 // Bind the uniform to the sampler.
804 844
805 glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry), 845 glProgramUniform1i(shader->GetProgramHandle(primitive_mode),
806 current_bindpoint); 846 shader->GetUniformLocation(entry), current_bindpoint);
807 847
808 const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); 848 const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
809 849
@@ -972,6 +1012,22 @@ void RasterizerOpenGL::SyncAlphaTest() {
972 } 1012 }
973} 1013}
974 1014
1015void RasterizerOpenGL::SyncScissorTest() {
1016 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1017
1018 state.scissor.enabled = (regs.scissor_test.enable != 0);
1019 // TODO(Blinkhawk): Figure if the hardware supports scissor testing per viewport and how it's
1020 // implemented.
1021 if (regs.scissor_test.enable != 0) {
1022 const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x;
1023 const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y;
1024 state.scissor.x = regs.scissor_test.min_x;
1025 state.scissor.y = regs.scissor_test.min_y;
1026 state.scissor.width = width;
1027 state.scissor.height = height;
1028 }
1029}
1030
975void RasterizerOpenGL::SyncTransformFeedback() { 1031void RasterizerOpenGL::SyncTransformFeedback() {
976 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1032 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
977 1033
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 0dab2018b..b1f7ccc7e 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -120,7 +120,7 @@ private:
120 * @returns The next available bindpoint for use in the next shader stage. 120 * @returns The next available bindpoint for use in the next shader stage.
121 */ 121 */
122 u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader, 122 u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
123 u32 current_bindpoint); 123 GLenum primitive_mode, u32 current_bindpoint);
124 124
125 /* 125 /*
126 * Configures the current textures to use for the draw command. 126 * Configures the current textures to use for the draw command.
@@ -130,7 +130,7 @@ private:
130 * @returns The next available bindpoint for use in the next shader stage. 130 * @returns The next available bindpoint for use in the next shader stage.
131 */ 131 */
132 u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader, 132 u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
133 u32 current_unit); 133 GLenum primitive_mode, u32 current_unit);
134 134
135 /// Syncs the viewport to match the guest state 135 /// Syncs the viewport to match the guest state
136 void SyncViewport(); 136 void SyncViewport();
@@ -165,6 +165,9 @@ private:
165 /// Syncs the alpha test state to match the guest state 165 /// Syncs the alpha test state to match the guest state
166 void SyncAlphaTest(); 166 void SyncAlphaTest();
167 167
168 /// Syncs the scissor test state to match the guest state
169 void SyncScissorTest();
170
168 /// Syncs the transform feedback state to match the guest state 171 /// Syncs the transform feedback state to match the guest state
169 void SyncTransformFeedback(); 172 void SyncTransformFeedback();
170 173
@@ -207,7 +210,7 @@ private:
207 210
208 DrawParameters SetupDraw(); 211 DrawParameters SetupDraw();
209 212
210 void SetupShaders(); 213 void SetupShaders(GLenum primitive_mode);
211 214
212 enum class AccelDraw { Disabled, Arrays, Indexed }; 215 enum class AccelDraw { Disabled, Arrays, Indexed };
213 AccelDraw accelerate_draw = AccelDraw::Disabled; 216 AccelDraw accelerate_draw = AccelDraw::Disabled;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 56ff83eff..1cb77aaf2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -34,18 +34,57 @@ 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();
85 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
48 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, 86 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
87 params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0,
49 params.pixel_format = 88 params.pixel_format =
50 PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value()); 89 PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value());
51 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); 90 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
@@ -85,20 +124,23 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
85 break; 124 break;
86 } 125 }
87 126
88 params.size_in_bytes_total = params.SizeInBytesTotal();
89 params.size_in_bytes_2d = params.SizeInBytes2D();
90 params.max_mip_level = config.tic.max_mip_level + 1; 127 params.max_mip_level = config.tic.max_mip_level + 1;
91 params.rt = {}; 128 params.rt = {};
92 129
130 params.InitCacheParameters(config.tic.Address());
131
93 return params; 132 return params;
94} 133}
95 134
96/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) { 135/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) {
97 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]}; 136 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]};
98 SurfaceParams params{}; 137 SurfaceParams params{};
99 params.addr = TryGetCpuAddr(config.Address()); 138
100 params.is_tiled = true; 139 params.is_tiled =
101 params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; 140 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
141 params.block_width = 1 << config.memory_layout.block_width;
142 params.block_height = 1 << config.memory_layout.block_height;
143 params.block_depth = 1 << config.memory_layout.block_depth;
102 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); 144 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
103 params.component_type = ComponentTypeFromRenderTarget(config.format); 145 params.component_type = ComponentTypeFromRenderTarget(config.format);
104 params.type = GetFormatType(params.pixel_format); 146 params.type = GetFormatType(params.pixel_format);
@@ -107,8 +149,6 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
107 params.unaligned_height = config.height; 149 params.unaligned_height = config.height;
108 params.target = SurfaceTarget::Texture2D; 150 params.target = SurfaceTarget::Texture2D;
109 params.depth = 1; 151 params.depth = 1;
110 params.size_in_bytes_total = params.SizeInBytesTotal();
111 params.size_in_bytes_2d = params.SizeInBytes2D();
112 params.max_mip_level = 0; 152 params.max_mip_level = 0;
113 153
114 // Render target specific parameters, not used for caching 154 // Render target specific parameters, not used for caching
@@ -117,16 +157,21 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
117 params.rt.layer_stride = config.layer_stride; 157 params.rt.layer_stride = config.layer_stride;
118 params.rt.base_layer = config.base_layer; 158 params.rt.base_layer = config.base_layer;
119 159
160 params.InitCacheParameters(config.Address());
161
120 return params; 162 return params;
121} 163}
122 164
123/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, 165/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(
124 Tegra::GPUVAddr zeta_address, 166 u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format,
125 Tegra::DepthFormat format) { 167 u32 block_width, u32 block_height, u32 block_depth,
168 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
126 SurfaceParams params{}; 169 SurfaceParams params{};
127 params.addr = TryGetCpuAddr(zeta_address); 170
128 params.is_tiled = true; 171 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
129 params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; 172 params.block_width = 1 << std::min(block_width, 5U);
173 params.block_height = 1 << std::min(block_height, 5U);
174 params.block_depth = 1 << std::min(block_depth, 5U);
130 params.pixel_format = PixelFormatFromDepthFormat(format); 175 params.pixel_format = PixelFormatFromDepthFormat(format);
131 params.component_type = ComponentTypeFromDepthFormat(format); 176 params.component_type = ComponentTypeFromDepthFormat(format);
132 params.type = GetFormatType(params.pixel_format); 177 params.type = GetFormatType(params.pixel_format);
@@ -135,20 +180,22 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
135 params.unaligned_height = zeta_height; 180 params.unaligned_height = zeta_height;
136 params.target = SurfaceTarget::Texture2D; 181 params.target = SurfaceTarget::Texture2D;
137 params.depth = 1; 182 params.depth = 1;
138 params.size_in_bytes_total = params.SizeInBytesTotal();
139 params.size_in_bytes_2d = params.SizeInBytes2D();
140 params.max_mip_level = 0; 183 params.max_mip_level = 0;
141 params.rt = {}; 184 params.rt = {};
142 185
186 params.InitCacheParameters(zeta_address);
187
143 return params; 188 return params;
144} 189}
145 190
146/*static*/ SurfaceParams SurfaceParams::CreateForFermiCopySurface( 191/*static*/ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
147 const Tegra::Engines::Fermi2D::Regs::Surface& config) { 192 const Tegra::Engines::Fermi2D::Regs::Surface& config) {
148 SurfaceParams params{}; 193 SurfaceParams params{};
149 params.addr = TryGetCpuAddr(config.Address()); 194
150 params.is_tiled = !config.linear; 195 params.is_tiled = !config.linear;
151 params.block_height = params.is_tiled ? config.BlockHeight() : 0, 196 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
197 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
198 params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0,
152 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); 199 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
153 params.component_type = ComponentTypeFromRenderTarget(config.format); 200 params.component_type = ComponentTypeFromRenderTarget(config.format);
154 params.type = GetFormatType(params.pixel_format); 201 params.type = GetFormatType(params.pixel_format);
@@ -157,11 +204,11 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
157 params.unaligned_height = config.height; 204 params.unaligned_height = config.height;
158 params.target = SurfaceTarget::Texture2D; 205 params.target = SurfaceTarget::Texture2D;
159 params.depth = 1; 206 params.depth = 1;
160 params.size_in_bytes_total = params.SizeInBytesTotal();
161 params.size_in_bytes_2d = params.SizeInBytes2D();
162 params.max_mip_level = 0; 207 params.max_mip_level = 0;
163 params.rt = {}; 208 params.rt = {};
164 209
210 params.InitCacheParameters(config.Address());
211
165 return params; 212 return params;
166} 213}
167 214
@@ -221,6 +268,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
221 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI 268 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI
222 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI 269 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI
223 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 270 {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_8X5
272 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4
224 273
225 // Depth formats 274 // Depth formats
226 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F 275 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -264,28 +313,6 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType
264 return format; 313 return format;
265} 314}
266 315
267static bool IsPixelFormatASTC(PixelFormat format) {
268 switch (format) {
269 case PixelFormat::ASTC_2D_4X4:
270 case PixelFormat::ASTC_2D_8X8:
271 return true;
272 default:
273 return false;
274 }
275}
276
277static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
278 switch (format) {
279 case PixelFormat::ASTC_2D_4X4:
280 return {4, 4};
281 case PixelFormat::ASTC_2D_8X8:
282 return {8, 8};
283 default:
284 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
285 UNREACHABLE();
286 }
287}
288
289MathUtil::Rectangle<u32> SurfaceParams::GetRect() const { 316MathUtil::Rectangle<u32> SurfaceParams::GetRect() const {
290 u32 actual_height{unaligned_height}; 317 u32 actual_height{unaligned_height};
291 if (IsPixelFormatASTC(pixel_format)) { 318 if (IsPixelFormatASTC(pixel_format)) {
@@ -313,29 +340,27 @@ static bool IsFormatBCn(PixelFormat format) {
313} 340}
314 341
315template <bool morton_to_gl, PixelFormat format> 342template <bool morton_to_gl, PixelFormat format>
316void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::size_t gl_buffer_size, 343void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer,
317 VAddr addr) { 344 std::size_t gl_buffer_size, VAddr addr) {
318 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; 345 constexpr u32 bytes_per_pixel = SurfaceParams::GetBytesPerPixel(format);
319 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); 346
347 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
348 // pixel values.
349 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
320 350
321 if (morton_to_gl) { 351 if (morton_to_gl) {
322 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
323 // pixel values.
324 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
325 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( 352 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
326 addr, tile_size, bytes_per_pixel, stride, height, block_height); 353 addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth);
327 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())}; 354 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
328 memcpy(gl_buffer, data.data(), size_to_copy); 355 memcpy(gl_buffer, data.data(), size_to_copy);
329 } else { 356 } else {
330 // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should 357 Tegra::Texture::CopySwizzledData(stride / tile_size, height / tile_size, depth,
331 // check the configuration for this and perform more generic un/swizzle 358 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr),
332 LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); 359 gl_buffer, false, block_height, block_depth);
333 VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel,
334 Memory::GetPointer(addr), gl_buffer, morton_to_gl);
335 } 360 }
336} 361}
337 362
338static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), 363static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
339 SurfaceParams::MaxPixelFormat> 364 SurfaceParams::MaxPixelFormat>
340 morton_to_gl_fns = { 365 morton_to_gl_fns = {
341 // clang-format off 366 // clang-format off
@@ -385,6 +410,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
385 MortonCopy<true, PixelFormat::RG32UI>, 410 MortonCopy<true, PixelFormat::RG32UI>,
386 MortonCopy<true, PixelFormat::R32UI>, 411 MortonCopy<true, PixelFormat::R32UI>,
387 MortonCopy<true, PixelFormat::ASTC_2D_8X8>, 412 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
413 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
414 MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
388 MortonCopy<true, PixelFormat::Z32F>, 415 MortonCopy<true, PixelFormat::Z32F>,
389 MortonCopy<true, PixelFormat::Z16>, 416 MortonCopy<true, PixelFormat::Z16>,
390 MortonCopy<true, PixelFormat::Z24S8>, 417 MortonCopy<true, PixelFormat::Z24S8>,
@@ -393,7 +420,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
393 // clang-format on 420 // clang-format on
394}; 421};
395 422
396static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), 423static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
397 SurfaceParams::MaxPixelFormat> 424 SurfaceParams::MaxPixelFormat>
398 gl_to_morton_fns = { 425 gl_to_morton_fns = {
399 // clang-format off 426 // clang-format off
@@ -410,17 +437,16 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
410 MortonCopy<false, PixelFormat::RGBA16UI>, 437 MortonCopy<false, PixelFormat::RGBA16UI>,
411 MortonCopy<false, PixelFormat::R11FG11FB10F>, 438 MortonCopy<false, PixelFormat::R11FG11FB10F>,
412 MortonCopy<false, PixelFormat::RGBA32UI>, 439 MortonCopy<false, PixelFormat::RGBA32UI>,
413 // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/BC6H_UF16/BC6H_SF16/ASTC_2D_4X4 440 MortonCopy<false, PixelFormat::DXT1>,
414 // formats are not supported 441 MortonCopy<false, PixelFormat::DXT23>,
415 nullptr, 442 MortonCopy<false, PixelFormat::DXT45>,
416 nullptr, 443 MortonCopy<false, PixelFormat::DXN1>,
417 nullptr, 444 MortonCopy<false, PixelFormat::DXN2UNORM>,
418 nullptr, 445 MortonCopy<false, PixelFormat::DXN2SNORM>,
419 nullptr, 446 MortonCopy<false, PixelFormat::BC7U>,
420 nullptr, 447 MortonCopy<false, PixelFormat::BC6H_UF16>,
421 nullptr, 448 MortonCopy<false, PixelFormat::BC6H_SF16>,
422 nullptr, 449 // TODO(Subv): Swizzling ASTC formats are not supported
423 nullptr,
424 nullptr, 450 nullptr,
425 MortonCopy<false, PixelFormat::G8R8U>, 451 MortonCopy<false, PixelFormat::G8R8U>,
426 MortonCopy<false, PixelFormat::G8R8S>, 452 MortonCopy<false, PixelFormat::G8R8S>,
@@ -445,6 +471,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
445 MortonCopy<false, PixelFormat::RG32UI>, 471 MortonCopy<false, PixelFormat::RG32UI>,
446 MortonCopy<false, PixelFormat::R32UI>, 472 MortonCopy<false, PixelFormat::R32UI>,
447 nullptr, 473 nullptr,
474 nullptr,
475 nullptr,
448 MortonCopy<false, PixelFormat::Z32F>, 476 MortonCopy<false, PixelFormat::Z32F>,
449 MortonCopy<false, PixelFormat::Z16>, 477 MortonCopy<false, PixelFormat::Z16>,
450 MortonCopy<false, PixelFormat::Z24S8>, 478 MortonCopy<false, PixelFormat::Z24S8>,
@@ -604,22 +632,21 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
604 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type); 632 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
605 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type); 633 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
606 634
607 std::size_t buffer_size = 635 std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
608 std::max(src_params.size_in_bytes_total, dst_params.size_in_bytes_total);
609 636
610 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); 637 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
611 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); 638 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
612 if (source_format.compressed) { 639 if (source_format.compressed) {
613 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment, 640 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment,
614 static_cast<GLsizei>(src_params.size_in_bytes_total), nullptr); 641 static_cast<GLsizei>(src_params.size_in_bytes), nullptr);
615 } else { 642 } else {
616 glGetTextureImage(src_surface->Texture().handle, src_attachment, source_format.format, 643 glGetTextureImage(src_surface->Texture().handle, src_attachment, source_format.format,
617 source_format.type, static_cast<GLsizei>(src_params.size_in_bytes_total), 644 source_format.type, static_cast<GLsizei>(src_params.size_in_bytes),
618 nullptr); 645 nullptr);
619 } 646 }
620 // If the new texture is bigger than the previous one, we need to fill in the rest with data 647 // If the new texture is bigger than the previous one, we need to fill in the rest with data
621 // from the CPU. 648 // from the CPU.
622 if (src_params.size_in_bytes_total < dst_params.size_in_bytes_total) { 649 if (src_params.size_in_bytes < dst_params.size_in_bytes) {
623 // Upload the rest of the memory. 650 // Upload the rest of the memory.
624 if (dst_params.is_tiled) { 651 if (dst_params.is_tiled) {
625 // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest 652 // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest
@@ -629,12 +656,12 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
629 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " 656 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
630 "reinterpretation but the texture is tiled."); 657 "reinterpretation but the texture is tiled.");
631 } 658 }
632 std::size_t remaining_size = 659 std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
633 dst_params.size_in_bytes_total - src_params.size_in_bytes_total;
634 std::vector<u8> data(remaining_size); 660 std::vector<u8> data(remaining_size);
635 Memory::ReadBlock(dst_params.addr + src_params.size_in_bytes_total, data.data(), 661 std::memcpy(data.data(), Memory::GetPointer(dst_params.addr + src_params.size_in_bytes),
636 data.size()); 662 data.size());
637 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes_total, remaining_size, 663
664 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
638 data.data()); 665 data.data());
639 } 666 }
640 667
@@ -680,7 +707,8 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
680} 707}
681 708
682CachedSurface::CachedSurface(const SurfaceParams& params) 709CachedSurface::CachedSurface(const SurfaceParams& params)
683 : params(params), gl_target(SurfaceTargetToGL(params.target)) { 710 : params(params), gl_target(SurfaceTargetToGL(params.target)),
711 cached_size_in_bytes(params.size_in_bytes) {
684 texture.Create(); 712 texture.Create();
685 const auto& rect{params.GetRect()}; 713 const auto& rect{params.GetRect()};
686 714
@@ -730,9 +758,21 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
730 758
731 VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, 759 VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr,
732 SurfaceParams::SurfaceTargetName(params.target)); 760 SurfaceParams::SurfaceTargetName(params.target));
761
762 // Clamp size to mapped GPU memory region
763 // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000
764 // R32F render buffer. We do not yet know if this is a game bug or something else, but this
765 // check is necessary to prevent flushing from overwriting unmapped memory.
766
767 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
768 const u64 max_size{memory_manager.GetRegionEnd(params.gpu_addr) - params.gpu_addr};
769 if (cached_size_in_bytes > max_size) {
770 LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size);
771 cached_size_in_bytes = max_size;
772 }
733} 773}
734 774
735static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { 775static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bool reverse) {
736 union S8Z24 { 776 union S8Z24 {
737 BitField<0, 24, u32> z24; 777 BitField<0, 24, u32> z24;
738 BitField<24, 8, u32> s8; 778 BitField<24, 8, u32> s8;
@@ -745,22 +785,29 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
745 }; 785 };
746 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size"); 786 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size");
747 787
748 S8Z24 input_pixel{}; 788 S8Z24 s8z24_pixel{};
749 Z24S8 output_pixel{}; 789 Z24S8 z24s8_pixel{};
750 constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)}; 790 constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::S8Z24)};
751 for (std::size_t y = 0; y < height; ++y) { 791 for (std::size_t y = 0; y < height; ++y) {
752 for (std::size_t x = 0; x < width; ++x) { 792 for (std::size_t x = 0; x < width; ++x) {
753 const std::size_t offset{bpp * (y * width + x)}; 793 const std::size_t offset{bpp * (y * width + x)};
754 std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24)); 794 if (reverse) {
755 output_pixel.s8.Assign(input_pixel.s8); 795 std::memcpy(&z24s8_pixel, &data[offset], sizeof(Z24S8));
756 output_pixel.z24.Assign(input_pixel.z24); 796 s8z24_pixel.s8.Assign(z24s8_pixel.s8);
757 std::memcpy(&data[offset], &output_pixel, sizeof(Z24S8)); 797 s8z24_pixel.z24.Assign(z24s8_pixel.z24);
798 std::memcpy(&data[offset], &s8z24_pixel, sizeof(S8Z24));
799 } else {
800 std::memcpy(&s8z24_pixel, &data[offset], sizeof(S8Z24));
801 z24s8_pixel.s8.Assign(s8z24_pixel.s8);
802 z24s8_pixel.z24.Assign(s8z24_pixel.z24);
803 std::memcpy(&data[offset], &z24s8_pixel, sizeof(Z24S8));
804 }
758 } 805 }
759 } 806 }
760} 807}
761 808
762static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { 809static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
763 constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)}; 810 constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::G8R8U)};
764 for (std::size_t y = 0; y < height; ++y) { 811 for (std::size_t y = 0; y < height; ++y) {
765 for (std::size_t x = 0; x < width; ++x) { 812 for (std::size_t x = 0; x < width; ++x) {
766 const std::size_t offset{bpp * (y * width + x)}; 813 const std::size_t offset{bpp * (y * width + x)};
@@ -780,7 +827,9 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
780 u32 width, u32 height) { 827 u32 width, u32 height) {
781 switch (pixel_format) { 828 switch (pixel_format) {
782 case PixelFormat::ASTC_2D_4X4: 829 case PixelFormat::ASTC_2D_4X4:
783 case PixelFormat::ASTC_2D_8X8: { 830 case PixelFormat::ASTC_2D_8X8:
831 case PixelFormat::ASTC_2D_8X5:
832 case PixelFormat::ASTC_2D_5X4: {
784 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. 833 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
785 u32 block_width{}; 834 u32 block_width{};
786 u32 block_height{}; 835 u32 block_height{};
@@ -790,7 +839,7 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
790 } 839 }
791 case PixelFormat::S8Z24: 840 case PixelFormat::S8Z24:
792 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24. 841 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
793 ConvertS8Z24ToZ24S8(data, width, height); 842 ConvertS8Z24ToZ24S8(data, width, height, false);
794 break; 843 break;
795 844
796 case PixelFormat::G8R8U: 845 case PixelFormat::G8R8U:
@@ -801,49 +850,54 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
801 } 850 }
802} 851}
803 852
853/**
854 * Helper function to perform software conversion (as needed) when flushing a buffer from OpenGL to
855 * Switch memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or
856 * with typical desktop GPUs.
857 */
858static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
859 u32 width, u32 height) {
860 switch (pixel_format) {
861 case PixelFormat::G8R8U:
862 case PixelFormat::G8R8S:
863 case PixelFormat::ASTC_2D_4X4:
864 case PixelFormat::ASTC_2D_8X8: {
865 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
866 static_cast<u32>(pixel_format));
867 UNREACHABLE();
868 break;
869 }
870 case PixelFormat::S8Z24:
871 // Convert the Z24S8 depth format to S8Z24, as OpenGL does not support S8Z24.
872 ConvertS8Z24ToZ24S8(data, width, height, true);
873 break;
874 }
875}
876
804MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); 877MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));
805void CachedSurface::LoadGLBuffer() { 878void CachedSurface::LoadGLBuffer() {
806 ASSERT(params.type != SurfaceType::Fill);
807
808 const u8* const texture_src_data = Memory::GetPointer(params.addr);
809
810 ASSERT(texture_src_data);
811
812 const u32 bytes_per_pixel = GetGLBytesPerPixel(params.pixel_format);
813 const u32 copy_size = params.width * params.height * bytes_per_pixel;
814 const std::size_t total_size = copy_size * params.depth;
815
816 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); 879 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
817 880
881 gl_buffer.resize(params.size_in_bytes_gl);
818 if (params.is_tiled) { 882 if (params.is_tiled) {
819 gl_buffer.resize(total_size); 883 u32 depth = params.depth;
884 u32 block_depth = params.block_depth;
820 885
821 // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do 886 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
822 // this for 3D textures, etc. 887 params.block_width, static_cast<u32>(params.target));
823 switch (params.target) { 888
824 case SurfaceParams::SurfaceTarget::Texture2D: 889 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
825 // Pass impl. to the fallback code below 890 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
826 break; 891 depth = 1U;
827 case SurfaceParams::SurfaceTarget::Texture2DArray: 892 block_depth = 1U;
828 case SurfaceParams::SurfaceTarget::TextureCubemap:
829 for (std::size_t index = 0; index < params.depth; ++index) {
830 const std::size_t offset{index * copy_size};
831 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
832 params.width, params.block_height, params.height, gl_buffer.data() + offset,
833 copy_size, params.addr + offset);
834 }
835 break;
836 default:
837 LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}",
838 static_cast<u32>(params.target));
839 UNREACHABLE();
840 } 893 }
841 894
842 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( 895 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
843 params.width, params.block_height, params.height, gl_buffer.data(), copy_size, 896 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
844 params.addr); 897 gl_buffer.size(), params.addr);
845 } else { 898 } else {
846 const u8* const texture_src_data_end{texture_src_data + total_size}; 899 const auto texture_src_data{Memory::GetPointer(params.addr)};
900 const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl};
847 gl_buffer.assign(texture_src_data, texture_src_data_end); 901 gl_buffer.assign(texture_src_data, texture_src_data_end);
848 } 902 }
849 903
@@ -852,7 +906,44 @@ void CachedSurface::LoadGLBuffer() {
852 906
853MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); 907MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
854void CachedSurface::FlushGLBuffer() { 908void CachedSurface::FlushGLBuffer() {
855 ASSERT_MSG(false, "Unimplemented"); 909 MICROPROFILE_SCOPE(OpenGL_SurfaceFlush);
910
911 ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented");
912
913 // OpenGL temporary buffer needs to be big enough to store raw texture size
914 gl_buffer.resize(GetSizeInBytes());
915
916 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
917 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
918 ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0);
919 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
920 ASSERT(!tuple.compressed);
921 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
922 glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, gl_buffer.size(),
923 gl_buffer.data());
924 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
925 ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width,
926 params.height);
927 ASSERT(params.type != SurfaceType::Fill);
928 const u8* const texture_src_data = Memory::GetPointer(params.addr);
929 ASSERT(texture_src_data);
930 if (params.is_tiled) {
931 u32 depth = params.depth;
932 u32 block_depth = params.block_depth;
933
934 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
935 params.block_width, static_cast<u32>(params.target));
936
937 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
938 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
939 depth = 1U;
940 }
941 gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
942 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
943 gl_buffer.size(), GetAddr());
944 } else {
945 std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes());
946 }
856} 947}
857 948
858MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); 949MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
@@ -862,9 +953,6 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
862 953
863 MICROPROFILE_SCOPE(OpenGL_TextureUL); 954 MICROPROFILE_SCOPE(OpenGL_TextureUL);
864 955
865 ASSERT(gl_buffer.size() == static_cast<std::size_t>(params.width) * params.height *
866 GetGLBytesPerPixel(params.pixel_format) * params.depth);
867
868 const auto& rect{params.GetRect()}; 956 const auto& rect{params.GetRect()};
869 957
870 // Load data from memory to the surface 958 // Load data from memory to the surface
@@ -873,7 +961,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
873 std::size_t buffer_offset = 961 std::size_t buffer_offset =
874 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width + 962 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width +
875 static_cast<std::size_t>(x0)) * 963 static_cast<std::size_t>(x0)) *
876 GetGLBytesPerPixel(params.pixel_format); 964 SurfaceParams::GetBytesPerPixel(params.pixel_format);
877 965
878 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); 966 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
879 const GLuint target_tex = texture.handle; 967 const GLuint target_tex = texture.handle;
@@ -889,7 +977,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
889 cur_state.Apply(); 977 cur_state.Apply();
890 978
891 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT 979 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
892 ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0); 980 ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0);
893 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width)); 981 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width));
894 982
895 glActiveTexture(GL_TEXTURE0); 983 glActiveTexture(GL_TEXTURE0);
@@ -899,7 +987,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
899 glCompressedTexImage2D( 987 glCompressedTexImage2D(
900 SurfaceTargetToGL(params.target), 0, tuple.internal_format, 988 SurfaceTargetToGL(params.target), 0, tuple.internal_format,
901 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0, 989 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0,
902 static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]); 990 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
903 break; 991 break;
904 case SurfaceParams::SurfaceTarget::Texture3D: 992 case SurfaceParams::SurfaceTarget::Texture3D:
905 case SurfaceParams::SurfaceTarget::Texture2DArray: 993 case SurfaceParams::SurfaceTarget::Texture2DArray:
@@ -907,16 +995,16 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
907 SurfaceTargetToGL(params.target), 0, tuple.internal_format, 995 SurfaceTargetToGL(params.target), 0, tuple.internal_format,
908 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 996 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height),
909 static_cast<GLsizei>(params.depth), 0, 997 static_cast<GLsizei>(params.depth), 0,
910 static_cast<GLsizei>(params.size_in_bytes_total), &gl_buffer[buffer_offset]); 998 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
911 break; 999 break;
912 case SurfaceParams::SurfaceTarget::TextureCubemap: 1000 case SurfaceParams::SurfaceTarget::TextureCubemap:
913 for (std::size_t face = 0; face < params.depth; ++face) { 1001 for (std::size_t face = 0; face < params.depth; ++face) {
914 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), 1002 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face),
915 0, tuple.internal_format, static_cast<GLsizei>(params.width), 1003 0, tuple.internal_format, static_cast<GLsizei>(params.width),
916 static_cast<GLsizei>(params.height), 0, 1004 static_cast<GLsizei>(params.height), 0,
917 static_cast<GLsizei>(params.size_in_bytes_2d), 1005 static_cast<GLsizei>(params.SizeInBytesCubeFaceGL()),
918 &gl_buffer[buffer_offset]); 1006 &gl_buffer[buffer_offset]);
919 buffer_offset += params.size_in_bytes_2d; 1007 buffer_offset += params.SizeInBytesCubeFace();
920 } 1008 }
921 break; 1009 break;
922 default: 1010 default:
@@ -926,7 +1014,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
926 glCompressedTexImage2D( 1014 glCompressedTexImage2D(
927 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), 1015 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width),
928 static_cast<GLsizei>(params.height), 0, 1016 static_cast<GLsizei>(params.height), 0,
929 static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]); 1017 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
930 } 1018 }
931 } else { 1019 } else {
932 1020
@@ -955,7 +1043,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
955 y0, static_cast<GLsizei>(rect.GetWidth()), 1043 y0, static_cast<GLsizei>(rect.GetWidth()),
956 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, 1044 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
957 &gl_buffer[buffer_offset]); 1045 &gl_buffer[buffer_offset]);
958 buffer_offset += params.size_in_bytes_2d; 1046 buffer_offset += params.SizeInBytesCubeFace();
959 } 1047 }
960 break; 1048 break;
961 default: 1049 default:
@@ -989,7 +1077,9 @@ Surface RasterizerCacheOpenGL::GetDepthBufferSurface(bool preserve_contents) {
989 } 1077 }
990 1078
991 SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer( 1079 SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer(
992 regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format)}; 1080 regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format,
1081 regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
1082 regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
993 1083
994 return GetSurface(depth_params, preserve_contents); 1084 return GetSurface(depth_params, preserve_contents);
995} 1085}
@@ -1015,10 +1105,7 @@ Surface RasterizerCacheOpenGL::GetColorBufferSurface(std::size_t index, bool pre
1015void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { 1105void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
1016 surface->LoadGLBuffer(); 1106 surface->LoadGLBuffer();
1017 surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle); 1107 surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
1018} 1108 surface->MarkAsModified(false, *this);
1019
1020void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) {
1021 surface->FlushGLBuffer();
1022} 1109}
1023 1110
1024Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) { 1111Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) {
@@ -1087,6 +1174,14 @@ void RasterizerCacheOpenGL::FermiCopySurface(
1087 FastCopySurface(GetSurface(src_params, true), GetSurface(dst_params, false)); 1174 FastCopySurface(GetSurface(src_params, true), GetSurface(dst_params, false));
1088} 1175}
1089 1176
1177void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
1178 const Surface& dst_surface) {
1179 const auto& src_params{src_surface->GetSurfaceParams()};
1180 const auto& dst_params{dst_surface->GetSurfaceParams()};
1181 FlushRegion(src_params.addr, dst_params.size_in_bytes);
1182 LoadSurface(dst_surface);
1183}
1184
1090Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, 1185Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1091 const SurfaceParams& new_params) { 1186 const SurfaceParams& new_params) {
1092 // Verify surface is compatible for blitting 1187 // Verify surface is compatible for blitting
@@ -1095,6 +1190,12 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1095 // Get a new surface with the new parameters, and blit the previous surface to it 1190 // Get a new surface with the new parameters, and blit the previous surface to it
1096 Surface new_surface{GetUncachedSurface(new_params)}; 1191 Surface new_surface{GetUncachedSurface(new_params)};
1097 1192
1193 // With use_accurate_gpu_emulation enabled, do an accurate surface copy
1194 if (Settings::values.use_accurate_gpu_emulation) {
1195 AccurateCopySurface(old_surface, new_surface);
1196 return new_surface;
1197 }
1198
1098 // For compatible surfaces, we can just do fast glCopyImageSubData based copy 1199 // For compatible surfaces, we can just do fast glCopyImageSubData based copy
1099 if (old_params.target == new_params.target && old_params.type == new_params.type && 1200 if (old_params.target == new_params.target && old_params.type == new_params.type &&
1100 old_params.depth == new_params.depth && old_params.depth == 1 && 1201 old_params.depth == new_params.depth && old_params.depth == 1 &&
@@ -1106,11 +1207,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1106 1207
1107 // If the format is the same, just do a framebuffer blit. This is significantly faster than 1208 // If the format is the same, just do a framebuffer blit. This is significantly faster than
1108 // using PBOs. The is also likely less accurate, as textures will be converted rather than 1209 // using PBOs. The is also likely less accurate, as textures will be converted rather than
1109 // reinterpreted. When use_accurate_framebuffers setting is enabled, perform a more accurate 1210 // reinterpreted. When use_accurate_gpu_emulation setting is enabled, perform a more accurate
1110 // surface copy, where pixels are reinterpreted as a new format (without conversion). This 1211 // surface copy, where pixels are reinterpreted as a new format (without conversion). This
1111 // code path uses OpenGL PBOs and is quite slow. 1212 // code path uses OpenGL PBOs and is quite slow.
1112 const bool is_blit{old_params.pixel_format == new_params.pixel_format || 1213 const bool is_blit{old_params.pixel_format == new_params.pixel_format};
1113 !Settings::values.use_accurate_framebuffers};
1114 1214
1115 switch (new_params.target) { 1215 switch (new_params.target) {
1116 case SurfaceParams::SurfaceTarget::Texture2D: 1216 case SurfaceParams::SurfaceTarget::Texture2D:
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 0b4940b3c..7c1cb72d0 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
@@ -220,6 +223,8 @@ struct SurfaceParams {
220 1, // RG32UI 223 1, // RG32UI
221 1, // R32UI 224 1, // R32UI
222 4, // ASTC_2D_8X8 225 4, // ASTC_2D_8X8
226 4, // ASTC_2D_8X5
227 4, // ASTC_2D_5X4
223 1, // Z32F 228 1, // Z32F
224 1, // Z16 229 1, // Z16
225 1, // Z24S8 230 1, // Z24S8
@@ -282,6 +287,8 @@ struct SurfaceParams {
282 64, // RG32UI 287 64, // RG32UI
283 32, // R32UI 288 32, // R32UI
284 16, // ASTC_2D_8X8 289 16, // ASTC_2D_8X8
290 32, // ASTC_2D_8X5
291 32, // ASTC_2D_5X4
285 32, // Z32F 292 32, // Z32F
286 16, // Z16 293 16, // Z16
287 32, // Z24S8 294 32, // Z24S8
@@ -553,8 +560,12 @@ struct SurfaceParams {
553 return PixelFormat::BC6H_SF16; 560 return PixelFormat::BC6H_SF16;
554 case Tegra::Texture::TextureFormat::ASTC_2D_4X4: 561 case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
555 return PixelFormat::ASTC_2D_4X4; 562 return PixelFormat::ASTC_2D_4X4;
563 case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
564 return PixelFormat::ASTC_2D_5X4;
556 case Tegra::Texture::TextureFormat::ASTC_2D_8X8: 565 case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
557 return PixelFormat::ASTC_2D_8X8; 566 return PixelFormat::ASTC_2D_8X8;
567 case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
568 return PixelFormat::ASTC_2D_8X5;
558 case Tegra::Texture::TextureFormat::R16_G16: 569 case Tegra::Texture::TextureFormat::R16_G16:
559 switch (component_type) { 570 switch (component_type) {
560 case Tegra::Texture::ComponentType::FLOAT: 571 case Tegra::Texture::ComponentType::FLOAT:
@@ -691,21 +702,42 @@ struct SurfaceParams {
691 return SurfaceType::Invalid; 702 return SurfaceType::Invalid;
692 } 703 }
693 704
705 /// Returns the sizer in bytes of the specified pixel format
706 static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) {
707 if (pixel_format == SurfaceParams::PixelFormat::Invalid) {
708 return 0;
709 }
710 return GetFormatBpp(pixel_format) / CHAR_BIT;
711 }
712
694 /// Returns the rectangle corresponding to this surface 713 /// Returns the rectangle corresponding to this surface
695 MathUtil::Rectangle<u32> GetRect() const; 714 MathUtil::Rectangle<u32> GetRect() const;
696 715
697 /// Returns the size of this surface as a 2D texture in bytes, adjusted for compression 716 /// Returns the total size of this surface in bytes, adjusted for compression
698 std::size_t SizeInBytes2D() const { 717 std::size_t SizeInBytesRaw(bool ignore_tiled = false) const {
699 const u32 compression_factor{GetCompressionFactor(pixel_format)}; 718 const u32 compression_factor{GetCompressionFactor(pixel_format)};
700 ASSERT(width % compression_factor == 0); 719 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
701 ASSERT(height % compression_factor == 0); 720 const size_t uncompressed_size{
702 return (width / compression_factor) * (height / compression_factor) * 721 Tegra::Texture::CalculateSize((ignore_tiled ? false : is_tiled), bytes_per_pixel, width,
703 GetFormatBpp(pixel_format) / CHAR_BIT; 722 height, depth, block_height, block_depth)};
723
724 // Divide by compression_factor^2, as height and width are factored by this
725 return uncompressed_size / (compression_factor * compression_factor);
704 } 726 }
705 727
706 /// Returns the total size of this surface in bytes, adjusted for compression 728 /// Returns the size of this surface as an OpenGL texture in bytes
707 std::size_t SizeInBytesTotal() const { 729 std::size_t SizeInBytesGL() const {
708 return SizeInBytes2D() * depth; 730 return SizeInBytesRaw(true);
731 }
732
733 /// Returns the size of this surface as a cube face in bytes
734 std::size_t SizeInBytesCubeFace() const {
735 return size_in_bytes / 6;
736 }
737
738 /// Returns the size of this surface as an OpenGL cube face in bytes
739 std::size_t SizeInBytesCubeFaceGL() const {
740 return size_in_bytes_gl / 6;
709 } 741 }
710 742
711 /// Creates SurfaceParams from a texture configuration 743 /// Creates SurfaceParams from a texture configuration
@@ -716,9 +748,10 @@ struct SurfaceParams {
716 static SurfaceParams CreateForFramebuffer(std::size_t index); 748 static SurfaceParams CreateForFramebuffer(std::size_t index);
717 749
718 /// Creates SurfaceParams for a depth buffer configuration 750 /// Creates SurfaceParams for a depth buffer configuration
719 static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, 751 static SurfaceParams CreateForDepthBuffer(
720 Tegra::GPUVAddr zeta_address, 752 u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format,
721 Tegra::DepthFormat format); 753 u32 block_width, u32 block_height, u32 block_depth,
754 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
722 755
723 /// Creates SurfaceParams for a Fermi2D surface copy 756 /// Creates SurfaceParams for a Fermi2D surface copy
724 static SurfaceParams CreateForFermiCopySurface( 757 static SurfaceParams CreateForFermiCopySurface(
@@ -731,9 +764,13 @@ struct SurfaceParams {
731 other.depth); 764 other.depth);
732 } 765 }
733 766
734 VAddr addr; 767 /// Initializes parameters for caching, should be called after everything has been initialized
768 void InitCacheParameters(Tegra::GPUVAddr gpu_addr);
769
735 bool is_tiled; 770 bool is_tiled;
771 u32 block_width;
736 u32 block_height; 772 u32 block_height;
773 u32 block_depth;
737 PixelFormat pixel_format; 774 PixelFormat pixel_format;
738 ComponentType component_type; 775 ComponentType component_type;
739 SurfaceType type; 776 SurfaceType type;
@@ -741,11 +778,15 @@ struct SurfaceParams {
741 u32 height; 778 u32 height;
742 u32 depth; 779 u32 depth;
743 u32 unaligned_height; 780 u32 unaligned_height;
744 std::size_t size_in_bytes_total;
745 std::size_t size_in_bytes_2d;
746 SurfaceTarget target; 781 SurfaceTarget target;
747 u32 max_mip_level; 782 u32 max_mip_level;
748 783
784 // Parameters used for caching
785 VAddr addr;
786 Tegra::GPUVAddr gpu_addr;
787 std::size_t size_in_bytes;
788 std::size_t size_in_bytes_gl;
789
749 // Render target specific parameters, not used in caching 790 // Render target specific parameters, not used in caching
750 struct { 791 struct {
751 u32 index; 792 u32 index;
@@ -762,7 +803,8 @@ struct SurfaceReserveKey : Common::HashableStruct<OpenGL::SurfaceParams> {
762 static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) { 803 static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) {
763 SurfaceReserveKey res; 804 SurfaceReserveKey res;
764 res.state = params; 805 res.state = params;
765 res.state.rt = {}; // Ignore rt config in caching 806 res.state.gpu_addr = {}; // Ignore GPU vaddr in caching
807 res.state.rt = {}; // Ignore rt config in caching
766 return res; 808 return res;
767 } 809 }
768}; 810};
@@ -777,16 +819,20 @@ struct hash<SurfaceReserveKey> {
777 819
778namespace OpenGL { 820namespace OpenGL {
779 821
780class CachedSurface final { 822class CachedSurface final : public RasterizerCacheObject {
781public: 823public:
782 CachedSurface(const SurfaceParams& params); 824 CachedSurface(const SurfaceParams& params);
783 825
784 VAddr GetAddr() const { 826 VAddr GetAddr() const override {
785 return params.addr; 827 return params.addr;
786 } 828 }
787 829
788 std::size_t GetSizeInBytes() const { 830 std::size_t GetSizeInBytes() const override {
789 return params.size_in_bytes_total; 831 return cached_size_in_bytes;
832 }
833
834 void Flush() override {
835 FlushGLBuffer();
790 } 836 }
791 837
792 const OGLTexture& Texture() const { 838 const OGLTexture& Texture() const {
@@ -797,13 +843,6 @@ public:
797 return gl_target; 843 return gl_target;
798 } 844 }
799 845
800 static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) {
801 if (format == SurfaceParams::PixelFormat::Invalid)
802 return 0;
803
804 return SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
805 }
806
807 const SurfaceParams& GetSurfaceParams() const { 846 const SurfaceParams& GetSurfaceParams() const {
808 return params; 847 return params;
809 } 848 }
@@ -820,6 +859,7 @@ private:
820 std::vector<u8> gl_buffer; 859 std::vector<u8> gl_buffer;
821 SurfaceParams params; 860 SurfaceParams params;
822 GLenum gl_target; 861 GLenum gl_target;
862 std::size_t cached_size_in_bytes;
823}; 863};
824 864
825class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { 865class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
@@ -836,9 +876,6 @@ public:
836 /// Get the color surface based on the framebuffer configuration and the specified render target 876 /// Get the color surface based on the framebuffer configuration and the specified render target
837 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents); 877 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents);
838 878
839 /// Flushes the surface to Switch memory
840 void FlushSurface(const Surface& surface);
841
842 /// Tries to find a framebuffer using on the provided CPU address 879 /// Tries to find a framebuffer using on the provided CPU address
843 Surface TryFindFramebufferSurface(VAddr addr) const; 880 Surface TryFindFramebufferSurface(VAddr addr) const;
844 881
@@ -862,6 +899,9 @@ private:
862 /// Tries to get a reserved surface for the specified parameters 899 /// Tries to get a reserved surface for the specified parameters
863 Surface TryGetReservedSurface(const SurfaceParams& params); 900 Surface TryGetReservedSurface(const SurfaceParams& params);
864 901
902 /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data
903 void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface);
904
865 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have 905 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
866 /// previously been used. This is to prevent surfaces from being constantly created and 906 /// previously been used. This is to prevent surfaces from being constantly created and
867 /// destroyed when used with different surface parameters. 907 /// destroyed when used with different surface parameters.
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 7cd8f91e4..1a03a677f 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -68,6 +68,10 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
68 program_result = GLShader::GenerateVertexShader(setup); 68 program_result = GLShader::GenerateVertexShader(setup);
69 gl_type = GL_VERTEX_SHADER; 69 gl_type = GL_VERTEX_SHADER;
70 break; 70 break;
71 case Maxwell::ShaderProgram::Geometry:
72 program_result = GLShader::GenerateGeometryShader(setup);
73 gl_type = GL_GEOMETRY_SHADER;
74 break;
71 case Maxwell::ShaderProgram::Fragment: 75 case Maxwell::ShaderProgram::Fragment:
72 program_result = GLShader::GenerateFragmentShader(setup); 76 program_result = GLShader::GenerateFragmentShader(setup);
73 gl_type = GL_FRAGMENT_SHADER; 77 gl_type = GL_FRAGMENT_SHADER;
@@ -80,11 +84,16 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
80 84
81 entries = program_result.second; 85 entries = program_result.second;
82 86
83 OGLShader shader; 87 if (program_type != Maxwell::ShaderProgram::Geometry) {
84 shader.Create(program_result.first.c_str(), gl_type); 88 OGLShader shader;
85 program.Create(true, shader.handle); 89 shader.Create(program_result.first.c_str(), gl_type);
86 SetShaderUniformBlockBindings(program.handle); 90 program.Create(true, shader.handle);
87 VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr); 91 SetShaderUniformBlockBindings(program.handle);
92 VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr);
93 } else {
94 // Store shader's code to lazily build it on draw
95 geometry_programs.code = program_result.first;
96 }
88} 97}
89 98
90GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) { 99GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) {
@@ -110,6 +119,21 @@ GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
110 return search->second; 119 return search->second;
111} 120}
112 121
122GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
123 const std::string& glsl_topology,
124 const std::string& debug_name) {
125 if (target_program.handle != 0) {
126 return target_program.handle;
127 }
128 const std::string source{geometry_programs.code + "layout (" + glsl_topology + ") in;\n"};
129 OGLShader shader;
130 shader.Create(source.c_str(), GL_GEOMETRY_SHADER);
131 target_program.Create(true, shader.handle);
132 SetShaderUniformBlockBindings(target_program.handle);
133 VideoCore::LabelGLObject(GL_PROGRAM, target_program.handle, addr, debug_name);
134 return target_program.handle;
135};
136
113Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { 137Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
114 const VAddr program_addr{GetShaderAddress(program)}; 138 const VAddr program_addr{GetShaderAddress(program)};
115 139
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 9bafe43a9..a210f1731 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -7,6 +7,7 @@
7#include <map> 7#include <map>
8#include <memory> 8#include <memory>
9 9
10#include "common/assert.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "video_core/rasterizer_cache.h" 12#include "video_core/rasterizer_cache.h"
12#include "video_core/renderer_opengl/gl_resource_manager.h" 13#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -18,28 +19,52 @@ class CachedShader;
18using Shader = std::shared_ptr<CachedShader>; 19using Shader = std::shared_ptr<CachedShader>;
19using Maxwell = Tegra::Engines::Maxwell3D::Regs; 20using Maxwell = Tegra::Engines::Maxwell3D::Regs;
20 21
21class CachedShader final { 22class CachedShader final : public RasterizerCacheObject {
22public: 23public:
23 CachedShader(VAddr addr, Maxwell::ShaderProgram program_type); 24 CachedShader(VAddr addr, Maxwell::ShaderProgram program_type);
24 25
25 /// Gets the address of the shader in guest memory, required for cache management 26 VAddr GetAddr() const override {
26 VAddr GetAddr() const {
27 return addr; 27 return addr;
28 } 28 }
29 29
30 /// Gets the size of the shader in guest memory, required for cache management 30 std::size_t GetSizeInBytes() const override {
31 std::size_t GetSizeInBytes() const {
32 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); 31 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64);
33 } 32 }
34 33
34 // We do not have to flush this cache as things in it are never modified by us.
35 void Flush() override {}
36
35 /// Gets the shader entries for the shader 37 /// Gets the shader entries for the shader
36 const GLShader::ShaderEntries& GetShaderEntries() const { 38 const GLShader::ShaderEntries& GetShaderEntries() const {
37 return entries; 39 return entries;
38 } 40 }
39 41
40 /// Gets the GL program handle for the shader 42 /// Gets the GL program handle for the shader
41 GLuint GetProgramHandle() const { 43 GLuint GetProgramHandle(GLenum primitive_mode) {
42 return program.handle; 44 if (program_type != Maxwell::ShaderProgram::Geometry) {
45 return program.handle;
46 }
47 switch (primitive_mode) {
48 case GL_POINTS:
49 return LazyGeometryProgram(geometry_programs.points, "points", "ShaderPoints");
50 case GL_LINES:
51 case GL_LINE_STRIP:
52 return LazyGeometryProgram(geometry_programs.lines, "lines", "ShaderLines");
53 case GL_LINES_ADJACENCY:
54 case GL_LINE_STRIP_ADJACENCY:
55 return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency",
56 "ShaderLinesAdjacency");
57 case GL_TRIANGLES:
58 case GL_TRIANGLE_STRIP:
59 case GL_TRIANGLE_FAN:
60 return LazyGeometryProgram(geometry_programs.triangles, "triangles", "ShaderTriangles");
61 case GL_TRIANGLES_ADJACENCY:
62 case GL_TRIANGLE_STRIP_ADJACENCY:
63 return LazyGeometryProgram(geometry_programs.triangles_adjacency, "triangles_adjacency",
64 "ShaderLines");
65 default:
66 UNREACHABLE_MSG("Unknown primitive mode.");
67 }
43 } 68 }
44 69
45 /// Gets the GL program resource location for the specified resource, caching as needed 70 /// Gets the GL program resource location for the specified resource, caching as needed
@@ -49,12 +74,30 @@ public:
49 GLint GetUniformLocation(const GLShader::SamplerEntry& sampler); 74 GLint GetUniformLocation(const GLShader::SamplerEntry& sampler);
50 75
51private: 76private:
77 /// Generates a geometry shader or returns one that already exists.
78 GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology,
79 const std::string& debug_name);
80
52 VAddr addr; 81 VAddr addr;
53 Maxwell::ShaderProgram program_type; 82 Maxwell::ShaderProgram program_type;
54 GLShader::ShaderSetup setup; 83 GLShader::ShaderSetup setup;
55 GLShader::ShaderEntries entries; 84 GLShader::ShaderEntries entries;
85
86 // Non-geometry program.
56 OGLProgram program; 87 OGLProgram program;
57 88
89 // Geometry programs. These are needed because GLSL needs an input topology but it's not
90 // declared by the hardware. Workaround this issue by generating a different shader per input
91 // topology class.
92 struct {
93 std::string code;
94 OGLProgram points;
95 OGLProgram lines;
96 OGLProgram lines_adjacency;
97 OGLProgram triangles;
98 OGLProgram triangles_adjacency;
99 } geometry_programs;
100
58 std::map<u32, GLuint> resource_cache; 101 std::map<u32, GLuint> resource_cache;
59 std::map<u32, GLint> uniform_cache; 102 std::map<u32, GLint> uniform_cache;
60}; 103};
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 85c668ca1..55c33c3a9 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -7,6 +7,7 @@
7#include <string> 7#include <string>
8#include <string_view> 8#include <string_view>
9 9
10#include <boost/optional.hpp>
10#include <fmt/format.h> 11#include <fmt/format.h>
11 12
12#include "common/assert.h" 13#include "common/assert.h"
@@ -29,11 +30,32 @@ using Tegra::Shader::SubOp;
29constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; 30constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
30constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); 31constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header);
31 32
33enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
34
35constexpr u32 MAX_GEOMETRY_BUFFERS = 6;
36constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested
37
32class DecompileFail : public std::runtime_error { 38class DecompileFail : public std::runtime_error {
33public: 39public:
34 using std::runtime_error::runtime_error; 40 using std::runtime_error::runtime_error;
35}; 41};
36 42
43/// Translate topology
44static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
45 switch (topology) {
46 case Tegra::Shader::OutputTopology::PointList:
47 return "points";
48 case Tegra::Shader::OutputTopology::LineStrip:
49 return "line_strip";
50 case Tegra::Shader::OutputTopology::TriangleStrip:
51 return "triangle_strip";
52 default:
53 LOG_CRITICAL(Render_OpenGL, "Unknown output topology {}", static_cast<u32>(topology));
54 UNREACHABLE();
55 return "points";
56 }
57}
58
37/// Describes the behaviour of code path of a given entry point and a return point. 59/// Describes the behaviour of code path of a given entry point and a return point.
38enum class ExitMethod { 60enum class ExitMethod {
39 Undetermined, ///< Internal value. Only occur when analyzing JMP loop. 61 Undetermined, ///< Internal value. Only occur when analyzing JMP loop.
@@ -253,8 +275,9 @@ enum class InternalFlag : u64 {
253class GLSLRegisterManager { 275class GLSLRegisterManager {
254public: 276public:
255 GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, 277 GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations,
256 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix) 278 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix,
257 : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix} { 279 const Tegra::Shader::Header& header)
280 : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header} {
258 BuildRegisterList(); 281 BuildRegisterList();
259 BuildInputList(); 282 BuildInputList();
260 } 283 }
@@ -358,11 +381,13 @@ public:
358 * @param reg The destination register to use. 381 * @param reg The destination register to use.
359 * @param elem The element to use for the operation. 382 * @param elem The element to use for the operation.
360 * @param attribute The input attribute to use as the source value. 383 * @param attribute The input attribute to use as the source value.
384 * @param vertex The register that decides which vertex to read from (used in GS).
361 */ 385 */
362 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, 386 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute,
363 const Tegra::Shader::IpaMode& input_mode) { 387 const Tegra::Shader::IpaMode& input_mode,
388 boost::optional<Register> vertex = {}) {
364 const std::string dest = GetRegisterAsFloat(reg); 389 const std::string dest = GetRegisterAsFloat(reg);
365 const std::string src = GetInputAttribute(attribute, input_mode) + GetSwizzle(elem); 390 const std::string src = GetInputAttribute(attribute, input_mode, vertex) + GetSwizzle(elem);
366 shader.AddLine(dest + " = " + src + ';'); 391 shader.AddLine(dest + " = " + src + ';');
367 } 392 }
368 393
@@ -391,16 +416,29 @@ public:
391 * are stored as floats, so this may require conversion. 416 * are stored as floats, so this may require conversion.
392 * @param attribute The destination output attribute. 417 * @param attribute The destination output attribute.
393 * @param elem The element to use for the operation. 418 * @param elem The element to use for the operation.
394 * @param reg The register to use as the source value. 419 * @param val_reg The register to use as the source value.
420 * @param buf_reg The register that tells which buffer to write to (used in geometry shaders).
395 */ 421 */
396 void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) { 422 void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& val_reg,
423 const Register& buf_reg) {
397 const std::string dest = GetOutputAttribute(attribute); 424 const std::string dest = GetOutputAttribute(attribute);
398 const std::string src = GetRegisterAsFloat(reg); 425 const std::string src = GetRegisterAsFloat(val_reg);
399 426
400 if (!dest.empty()) { 427 if (!dest.empty()) {
401 // Can happen with unknown/unimplemented output attributes, in which case we ignore the 428 // Can happen with unknown/unimplemented output attributes, in which case we ignore the
402 // instruction for now. 429 // instruction for now.
403 shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); 430 if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
431 // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry
432 // shader. These instructions use a dirty register as buffer index. To avoid some
433 // drivers from complaining for the out of boundary writes, guard them.
434 const std::string buf_index{"min(" + GetRegisterAsInteger(buf_reg) + ", " +
435 std::to_string(MAX_GEOMETRY_BUFFERS - 1) + ')'};
436 shader.AddLine("amem[" + buf_index + "][" +
437 std::to_string(static_cast<u32>(attribute)) + ']' +
438 GetSwizzle(elem) + " = " + src + ';');
439 } else {
440 shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';');
441 }
404 } 442 }
405 } 443 }
406 444
@@ -441,41 +479,123 @@ public:
441 } 479 }
442 } 480 }
443 481
444 /// Add declarations for registers 482 /// Add declarations.
445 void GenerateDeclarations(const std::string& suffix) { 483 void GenerateDeclarations(const std::string& suffix) {
484 GenerateRegisters(suffix);
485 GenerateInternalFlags();
486 GenerateInputAttrs();
487 GenerateOutputAttrs();
488 GenerateConstBuffers();
489 GenerateSamplers();
490 GenerateGeometry();
491 }
492
493 /// Returns a list of constant buffer declarations.
494 std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const {
495 std::vector<ConstBufferEntry> result;
496 std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(),
497 std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); });
498 return result;
499 }
500
501 /// Returns a list of samplers used in the shader.
502 const std::vector<SamplerEntry>& GetSamplers() const {
503 return used_samplers;
504 }
505
506 /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
507 /// necessary.
508 std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
509 bool is_array, bool is_shadow) {
510 const auto offset = static_cast<std::size_t>(sampler.index.Value());
511
512 // If this sampler has already been used, return the existing mapping.
513 const auto itr =
514 std::find_if(used_samplers.begin(), used_samplers.end(),
515 [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; });
516
517 if (itr != used_samplers.end()) {
518 ASSERT(itr->GetType() == type && itr->IsArray() == is_array &&
519 itr->IsShadow() == is_shadow);
520 return itr->GetName();
521 }
522
523 // Otherwise create a new mapping for this sampler
524 const std::size_t next_index = used_samplers.size();
525 const SamplerEntry entry{stage, offset, next_index, type, is_array, is_shadow};
526 used_samplers.emplace_back(entry);
527 return entry.GetName();
528 }
529
530private:
531 /// Generates declarations for registers.
532 void GenerateRegisters(const std::string& suffix) {
446 for (const auto& reg : regs) { 533 for (const auto& reg : regs) {
447 declarations.AddLine(GLSLRegister::GetTypeString() + ' ' + reg.GetPrefixString() + 534 declarations.AddLine(GLSLRegister::GetTypeString() + ' ' + reg.GetPrefixString() +
448 std::to_string(reg.GetIndex()) + '_' + suffix + " = 0;"); 535 std::to_string(reg.GetIndex()) + '_' + suffix + " = 0;");
449 } 536 }
450 declarations.AddNewLine(); 537 declarations.AddNewLine();
538 }
451 539
540 /// Generates declarations for internal flags.
541 void GenerateInternalFlags() {
452 for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { 542 for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) {
453 const InternalFlag code = static_cast<InternalFlag>(ii); 543 const InternalFlag code = static_cast<InternalFlag>(ii);
454 declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); 544 declarations.AddLine("bool " + GetInternalFlag(code) + " = false;");
455 } 545 }
456 declarations.AddNewLine(); 546 declarations.AddNewLine();
547 }
548
549 /// Generates declarations for input attributes.
550 void GenerateInputAttrs() {
551 if (stage != Maxwell3D::Regs::ShaderStage::Vertex) {
552 const std::string attr =
553 stage == Maxwell3D::Regs::ShaderStage::Geometry ? "gs_position[]" : "position";
554 declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) +
555 ") in vec4 " + attr + ';');
556 }
457 557
458 for (const auto element : declr_input_attribute) { 558 for (const auto element : declr_input_attribute) {
459 // TODO(bunnei): Use proper number of elements for these 559 // TODO(bunnei): Use proper number of elements for these
460 u32 idx = 560 u32 idx =
461 static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0); 561 static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0);
462 declarations.AddLine("layout(location = " + std::to_string(idx) + ")" + 562 if (stage != Maxwell3D::Regs::ShaderStage::Vertex) {
463 GetInputFlags(element.first) + "in vec4 " + 563 // If inputs are varyings, add an offset
464 GetInputAttribute(element.first, element.second) + ';'); 564 idx += GENERIC_VARYING_START_LOCATION;
565 }
566
567 std::string attr{GetInputAttribute(element.first, element.second)};
568 if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
569 attr = "gs_" + attr + "[]";
570 }
571 declarations.AddLine("layout (location = " + std::to_string(idx) + ") " +
572 GetInputFlags(element.first) + "in vec4 " + attr + ';');
465 } 573 }
574
466 declarations.AddNewLine(); 575 declarations.AddNewLine();
576 }
467 577
578 /// Generates declarations for output attributes.
579 void GenerateOutputAttrs() {
580 if (stage != Maxwell3D::Regs::ShaderStage::Fragment) {
581 declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) +
582 ") out vec4 position;");
583 }
468 for (const auto& index : declr_output_attribute) { 584 for (const auto& index : declr_output_attribute) {
469 // TODO(bunnei): Use proper number of elements for these 585 // TODO(bunnei): Use proper number of elements for these
470 declarations.AddLine("layout(location = " + 586 const u32 idx = static_cast<u32>(index) -
471 std::to_string(static_cast<u32>(index) - 587 static_cast<u32>(Attribute::Index::Attribute_0) +
472 static_cast<u32>(Attribute::Index::Attribute_0)) + 588 GENERIC_VARYING_START_LOCATION;
473 ") out vec4 " + GetOutputAttribute(index) + ';'); 589 declarations.AddLine("layout (location = " + std::to_string(idx) + ") out vec4 " +
590 GetOutputAttribute(index) + ';');
474 } 591 }
475 declarations.AddNewLine(); 592 declarations.AddNewLine();
593 }
476 594
595 /// Generates declarations for constant buffers.
596 void GenerateConstBuffers() {
477 for (const auto& entry : GetConstBuffersDeclarations()) { 597 for (const auto& entry : GetConstBuffersDeclarations()) {
478 declarations.AddLine("layout(std140) uniform " + entry.GetName()); 598 declarations.AddLine("layout (std140) uniform " + entry.GetName());
479 declarations.AddLine('{'); 599 declarations.AddLine('{');
480 declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) + 600 declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) +
481 "[MAX_CONSTBUFFER_ELEMENTS];"); 601 "[MAX_CONSTBUFFER_ELEMENTS];");
@@ -483,7 +603,10 @@ public:
483 declarations.AddNewLine(); 603 declarations.AddNewLine();
484 } 604 }
485 declarations.AddNewLine(); 605 declarations.AddNewLine();
606 }
486 607
608 /// Generates declarations for samplers.
609 void GenerateSamplers() {
487 const auto& samplers = GetSamplers(); 610 const auto& samplers = GetSamplers();
488 for (const auto& sampler : samplers) { 611 for (const auto& sampler : samplers) {
489 declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() + 612 declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() +
@@ -492,44 +615,42 @@ public:
492 declarations.AddNewLine(); 615 declarations.AddNewLine();
493 } 616 }
494 617
495 /// Returns a list of constant buffer declarations 618 /// Generates declarations used for geometry shaders.
496 std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const { 619 void GenerateGeometry() {
497 std::vector<ConstBufferEntry> result; 620 if (stage != Maxwell3D::Regs::ShaderStage::Geometry)
498 std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(), 621 return;
499 std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); });
500 return result;
501 }
502
503 /// Returns a list of samplers used in the shader
504 const std::vector<SamplerEntry>& GetSamplers() const {
505 return used_samplers;
506 }
507
508 /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
509 /// necessary.
510 std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
511 bool is_array, bool is_shadow) {
512 const std::size_t offset = static_cast<std::size_t>(sampler.index.Value());
513 622
514 // If this sampler has already been used, return the existing mapping. 623 declarations.AddLine(
515 const auto itr = 624 "layout (" + GetTopologyName(header.common3.output_topology) +
516 std::find_if(used_samplers.begin(), used_samplers.end(), 625 ", max_vertices = " + std::to_string(header.common4.max_output_vertices) + ") out;");
517 [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); 626 declarations.AddNewLine();
518 627
519 if (itr != used_samplers.end()) { 628 declarations.AddLine("vec4 amem[" + std::to_string(MAX_GEOMETRY_BUFFERS) + "][" +
520 ASSERT(itr->GetType() == type && itr->IsArray() == is_array && 629 std::to_string(MAX_ATTRIBUTES) + "];");
521 itr->IsShadow() == is_shadow); 630 declarations.AddNewLine();
522 return itr->GetName();
523 }
524 631
525 // Otherwise create a new mapping for this sampler 632 constexpr char buffer[] = "amem[output_buffer]";
526 const std::size_t next_index = used_samplers.size(); 633 declarations.AddLine("void emit_vertex(uint output_buffer) {");
527 const SamplerEntry entry{stage, offset, next_index, type, is_array, is_shadow}; 634 ++declarations.scope;
528 used_samplers.emplace_back(entry); 635 for (const auto element : declr_output_attribute) {
529 return entry.GetName(); 636 declarations.AddLine(GetOutputAttribute(element) + " = " + buffer + '[' +
637 std::to_string(static_cast<u32>(element)) + "];");
638 }
639
640 declarations.AddLine("position = " + std::string(buffer) + '[' +
641 std::to_string(static_cast<u32>(Attribute::Index::Position)) + "];");
642
643 // If a geometry shader is attached, it will always flip (it's the last stage before
644 // fragment). For more info about flipping, refer to gl_shader_gen.cpp.
645 declarations.AddLine("position.xy *= viewport_flip.xy;");
646 declarations.AddLine("gl_Position = position;");
647 declarations.AddLine("position.w = 1.0;");
648 declarations.AddLine("EmitVertex();");
649 --declarations.scope;
650 declarations.AddLine('}');
651 declarations.AddNewLine();
530 } 652 }
531 653
532private:
533 /// Generates code representing a temporary (GPR) register. 654 /// Generates code representing a temporary (GPR) register.
534 std::string GetRegister(const Register& reg, unsigned elem) { 655 std::string GetRegister(const Register& reg, unsigned elem) {
535 if (reg == Register::ZeroIndex) { 656 if (reg == Register::ZeroIndex) {
@@ -586,11 +707,19 @@ private:
586 707
587 /// Generates code representing an input attribute register. 708 /// Generates code representing an input attribute register.
588 std::string GetInputAttribute(Attribute::Index attribute, 709 std::string GetInputAttribute(Attribute::Index attribute,
589 const Tegra::Shader::IpaMode& input_mode) { 710 const Tegra::Shader::IpaMode& input_mode,
711 boost::optional<Register> vertex = {}) {
712 auto GeometryPass = [&](const std::string& name) {
713 if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) {
714 return "gs_" + name + '[' + GetRegisterAsInteger(vertex.value(), 0, false) + ']';
715 }
716 return name;
717 };
718
590 switch (attribute) { 719 switch (attribute) {
591 case Attribute::Index::Position: 720 case Attribute::Index::Position:
592 if (stage != Maxwell3D::Regs::ShaderStage::Fragment) { 721 if (stage != Maxwell3D::Regs::ShaderStage::Fragment) {
593 return "position"; 722 return GeometryPass("position");
594 } else { 723 } else {
595 return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)"; 724 return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)";
596 } 725 }
@@ -619,7 +748,7 @@ private:
619 UNREACHABLE(); 748 UNREACHABLE();
620 } 749 }
621 } 750 }
622 return "input_attribute_" + std::to_string(index); 751 return GeometryPass("input_attribute_" + std::to_string(index));
623 } 752 }
624 753
625 LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute)); 754 LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute));
@@ -672,7 +801,7 @@ private:
672 return out; 801 return out;
673 } 802 }
674 803
675 /// Generates code representing an output attribute register. 804 /// Generates code representing the declaration name of an output attribute register.
676 std::string GetOutputAttribute(Attribute::Index attribute) { 805 std::string GetOutputAttribute(Attribute::Index attribute) {
677 switch (attribute) { 806 switch (attribute) {
678 case Attribute::Index::Position: 807 case Attribute::Index::Position:
@@ -708,6 +837,7 @@ private:
708 std::vector<SamplerEntry> used_samplers; 837 std::vector<SamplerEntry> used_samplers;
709 const Maxwell3D::Regs::ShaderStage& stage; 838 const Maxwell3D::Regs::ShaderStage& stage;
710 const std::string& suffix; 839 const std::string& suffix;
840 const Tegra::Shader::Header& header;
711}; 841};
712 842
713class GLSLGenerator { 843class GLSLGenerator {
@@ -1103,8 +1233,8 @@ private:
1103 return offset + 1; 1233 return offset + 1;
1104 } 1234 }
1105 1235
1106 shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName() + " (" + 1236 shader.AddLine(
1107 std::to_string(instr.value) + ')'); 1237 fmt::format("// {}: {} (0x{:016x})", offset, opcode->GetName(), instr.value));
1108 1238
1109 using Tegra::Shader::Pred; 1239 using Tegra::Shader::Pred;
1110 ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, 1240 ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute,
@@ -1306,7 +1436,6 @@ private:
1306 1436
1307 break; 1437 break;
1308 } 1438 }
1309
1310 case OpCode::Type::Shift: { 1439 case OpCode::Type::Shift: {
1311 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); 1440 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);
1312 std::string op_b; 1441 std::string op_b;
@@ -1348,7 +1477,6 @@ private:
1348 } 1477 }
1349 break; 1478 break;
1350 } 1479 }
1351
1352 case OpCode::Type::ArithmeticIntegerImmediate: { 1480 case OpCode::Type::ArithmeticIntegerImmediate: {
1353 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); 1481 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
1354 std::string op_b = std::to_string(instr.alu.imm20_32.Value()); 1482 std::string op_b = std::to_string(instr.alu.imm20_32.Value());
@@ -1826,7 +1954,7 @@ private:
1826 const auto LoadNextElement = [&](u32 reg_offset) { 1954 const auto LoadNextElement = [&](u32 reg_offset) {
1827 regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element, 1955 regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element,
1828 static_cast<Attribute::Index>(next_index), 1956 static_cast<Attribute::Index>(next_index),
1829 input_mode); 1957 input_mode, instr.gpr39.Value());
1830 1958
1831 // Load the next attribute element into the following register. If the element 1959 // Load the next attribute element into the following register. If the element
1832 // to load goes beyond the vec4 size, load the first element of the next 1960 // to load goes beyond the vec4 size, load the first element of the next
@@ -1890,8 +2018,8 @@ private:
1890 2018
1891 const auto StoreNextElement = [&](u32 reg_offset) { 2019 const auto StoreNextElement = [&](u32 reg_offset) {
1892 regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index), 2020 regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index),
1893 next_element, 2021 next_element, instr.gpr0.Value() + reg_offset,
1894 instr.gpr0.Value() + reg_offset); 2022 instr.gpr39.Value());
1895 2023
1896 // Load the next attribute element into the following register. If the element 2024 // Load the next attribute element into the following register. If the element
1897 // to load goes beyond the vec4 size, load the first element of the next 2025 // to load goes beyond the vec4 size, load the first element of the next
@@ -1908,9 +2036,9 @@ private:
1908 break; 2036 break;
1909 } 2037 }
1910 case OpCode::Id::TEX: { 2038 case OpCode::Id::TEX: {
1911 ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented");
1912 Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; 2039 Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
1913 std::string coord; 2040 std::string coord;
2041 const bool is_array = instr.tex.array != 0;
1914 2042
1915 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2043 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
1916 "NODEP is not implemented"); 2044 "NODEP is not implemented");
@@ -1925,21 +2053,59 @@ private:
1925 2053
1926 switch (num_coordinates) { 2054 switch (num_coordinates) {
1927 case 1: { 2055 case 1: {
1928 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2056 if (is_array) {
1929 coord = "float coords = " + x + ';'; 2057 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2058 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2059 coord = "vec2 coords = vec2(" + x + ", " + index + ");";
2060 } else {
2061 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2062 coord = "float coords = " + x + ';';
2063 }
1930 break; 2064 break;
1931 } 2065 }
1932 case 2: { 2066 case 2: {
1933 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2067 if (is_array) {
1934 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2068 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
1935 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2069 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2070 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2071 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");";
2072 } else {
2073 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2074 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2075 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2076 }
1936 break; 2077 break;
1937 } 2078 }
1938 case 3: { 2079 case 3: {
1939 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2080 if (depth_compare) {
1940 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2081 if (is_array) {
1941 const std::string z = regs.GetRegisterAsFloat(instr.gpr20); 2082 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
1942 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; 2083 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2084 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2085 const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2086 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
2087 ");";
2088 } else {
2089 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2090 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2091 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2092 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2093 }
2094 } else {
2095 if (is_array) {
2096 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2097 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2098 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2099 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 3);
2100 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
2101 ");";
2102 } else {
2103 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2104 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2105 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2106 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2107 }
2108 }
1943 break; 2109 break;
1944 } 2110 }
1945 default: 2111 default:
@@ -1958,7 +2124,7 @@ private:
1958 std::string op_c; 2124 std::string op_c;
1959 2125
1960 const std::string sampler = 2126 const std::string sampler =
1961 GetSampler(instr.sampler, texture_type, false, depth_compare); 2127 GetSampler(instr.sampler, texture_type, is_array, depth_compare);
1962 // Add an extra scope and declare the texture coords inside to prevent 2128 // Add an extra scope and declare the texture coords inside to prevent
1963 // overwriting them in case they are used as outputs of the texs instruction. 2129 // overwriting them in case they are used as outputs of the texs instruction.
1964 2130
@@ -1978,10 +2144,13 @@ private:
1978 } 2144 }
1979 case Tegra::Shader::TextureProcessMode::LB: 2145 case Tegra::Shader::TextureProcessMode::LB:
1980 case Tegra::Shader::TextureProcessMode::LBA: { 2146 case Tegra::Shader::TextureProcessMode::LBA: {
1981 if (num_coordinates <= 2) { 2147 if (depth_compare) {
1982 op_c = regs.GetRegisterAsFloat(instr.gpr20); 2148 if (is_array)
2149 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 2);
2150 else
2151 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
1983 } else { 2152 } else {
1984 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); 2153 op_c = regs.GetRegisterAsFloat(instr.gpr20);
1985 } 2154 }
1986 // TODO: Figure if A suffix changes the equation at all. 2155 // TODO: Figure if A suffix changes the equation at all.
1987 texture = "texture(" + sampler + ", coords, " + op_c + ')'; 2156 texture = "texture(" + sampler + ", coords, " + op_c + ')';
@@ -2124,6 +2293,8 @@ private:
2124 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), 2293 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ),
2125 "MZ is not implemented"); 2294 "MZ is not implemented");
2126 2295
2296 u32 op_c_offset = 0;
2297
2127 switch (texture_type) { 2298 switch (texture_type) {
2128 case Tegra::Shader::TextureType::Texture1D: { 2299 case Tegra::Shader::TextureType::Texture1D: {
2129 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2300 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
@@ -2138,6 +2309,7 @@ private:
2138 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2309 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
2139 const std::string y = regs.GetRegisterAsInteger(instr.gpr20); 2310 const std::string y = regs.GetRegisterAsInteger(instr.gpr20);
2140 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");"; 2311 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");";
2312 op_c_offset = 1;
2141 } 2313 }
2142 break; 2314 break;
2143 } 2315 }
@@ -2149,13 +2321,14 @@ private:
2149 const std::string sampler = 2321 const std::string sampler =
2150 GetSampler(instr.sampler, texture_type, is_array, false); 2322 GetSampler(instr.sampler, texture_type, is_array, false);
2151 std::string texture = "texelFetch(" + sampler + ", coords, 0)"; 2323 std::string texture = "texelFetch(" + sampler + ", coords, 0)";
2152 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr20.Value() + 1);
2153 switch (instr.tlds.GetTextureProcessMode()) { 2324 switch (instr.tlds.GetTextureProcessMode()) {
2154 case Tegra::Shader::TextureProcessMode::LZ: { 2325 case Tegra::Shader::TextureProcessMode::LZ: {
2155 texture = "texelFetch(" + sampler + ", coords, 0)"; 2326 texture = "texelFetch(" + sampler + ", coords, 0)";
2156 break; 2327 break;
2157 } 2328 }
2158 case Tegra::Shader::TextureProcessMode::LL: { 2329 case Tegra::Shader::TextureProcessMode::LL: {
2330 const std::string op_c =
2331 regs.GetRegisterAsInteger(instr.gpr20.Value() + op_c_offset);
2159 texture = "texelFetch(" + sampler + ", coords, " + op_c + ')'; 2332 texture = "texelFetch(" + sampler + ", coords, " + op_c + ')';
2160 break; 2333 break;
2161 } 2334 }
@@ -2496,14 +2669,14 @@ private:
2496 const std::string pred = 2669 const std::string pred =
2497 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); 2670 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);
2498 const std::string combiner = GetPredicateCombiner(instr.csetp.op); 2671 const std::string combiner = GetPredicateCombiner(instr.csetp.op);
2499 const std::string controlCode = regs.GetControlCode(instr.csetp.cc); 2672 const std::string control_code = regs.GetControlCode(instr.csetp.cc);
2500 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { 2673 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {
2501 SetPredicate(instr.csetp.pred3, 2674 SetPredicate(instr.csetp.pred3,
2502 '(' + controlCode + ") " + combiner + " (" + pred + ')'); 2675 '(' + control_code + ") " + combiner + " (" + pred + ')');
2503 } 2676 }
2504 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { 2677 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
2505 SetPredicate(instr.csetp.pred0, 2678 SetPredicate(instr.csetp.pred0,
2506 "!(" + controlCode + ") " + combiner + " (" + pred + ')'); 2679 "!(" + control_code + ") " + combiner + " (" + pred + ')');
2507 } 2680 }
2508 break; 2681 break;
2509 } 2682 }
@@ -2734,6 +2907,52 @@ private:
2734 2907
2735 break; 2908 break;
2736 } 2909 }
2910 case OpCode::Id::OUT_R: {
2911 ASSERT(instr.gpr20.Value() == Register::ZeroIndex);
2912 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
2913 "OUT is expected to be used in a geometry shader.");
2914
2915 if (instr.out.emit) {
2916 // gpr0 is used to store the next address. Hardware returns a pointer but
2917 // we just return the next index with a cyclic cap.
2918 const std::string current{regs.GetRegisterAsInteger(instr.gpr8, 0, false)};
2919 const std::string next = "((" + current + " + 1" + ") % " +
2920 std::to_string(MAX_GEOMETRY_BUFFERS) + ')';
2921 shader.AddLine("emit_vertex(" + current + ");");
2922 regs.SetRegisterToInteger(instr.gpr0, false, 0, next, 1, 1);
2923 }
2924 if (instr.out.cut) {
2925 shader.AddLine("EndPrimitive();");
2926 }
2927
2928 break;
2929 }
2930 case OpCode::Id::MOV_SYS: {
2931 switch (instr.sys20) {
2932 case Tegra::Shader::SystemVariable::InvocationInfo: {
2933 LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete");
2934 regs.SetRegisterToInteger(instr.gpr0, false, 0, "0u", 1, 1);
2935 break;
2936 }
2937 default: {
2938 LOG_CRITICAL(HW_GPU, "Unhandled system move: {}",
2939 static_cast<u32>(instr.sys20.Value()));
2940 UNREACHABLE();
2941 }
2942 }
2943 break;
2944 }
2945 case OpCode::Id::ISBERD: {
2946 ASSERT(instr.isberd.o == 0);
2947 ASSERT(instr.isberd.skew == 0);
2948 ASSERT(instr.isberd.shift == Tegra::Shader::IsberdShift::None);
2949 ASSERT(instr.isberd.mode == Tegra::Shader::IsberdMode::None);
2950 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
2951 "ISBERD is expected to be used in a geometry shader.");
2952 LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete");
2953 regs.SetRegisterToFloat(instr.gpr0, 0, regs.GetRegisterAsFloat(instr.gpr8), 1, 1);
2954 break;
2955 }
2737 case OpCode::Id::BRA: { 2956 case OpCode::Id::BRA: {
2738 ASSERT_MSG(instr.bra.constant_buffer == 0, 2957 ASSERT_MSG(instr.bra.constant_buffer == 0,
2739 "BRA with constant buffers are not implemented"); 2958 "BRA with constant buffers are not implemented");
@@ -2777,6 +2996,88 @@ private:
2777 LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed"); 2996 LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed");
2778 break; 2997 break;
2779 } 2998 }
2999 case OpCode::Id::VMAD: {
3000 const bool signed_a = instr.vmad.signed_a == 1;
3001 const bool signed_b = instr.vmad.signed_b == 1;
3002 const bool result_signed = signed_a || signed_b;
3003 boost::optional<std::string> forced_result;
3004
3005 auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed,
3006 Tegra::Shader::VmadType type, u64 byte_height) {
3007 const std::string value = [&]() {
3008 if (!is_chunk) {
3009 const auto offset = static_cast<u32>(byte_height * 8);
3010 return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
3011 }
3012 const std::string zero = "0";
3013
3014 switch (type) {
3015 case Tegra::Shader::VmadType::Size16_Low:
3016 return '(' + op + " & 0xffff)";
3017 case Tegra::Shader::VmadType::Size16_High:
3018 return '(' + op + " >> 16)";
3019 case Tegra::Shader::VmadType::Size32:
3020 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
3021 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
3022 // explanation is found: assert.
3023 UNREACHABLE_MSG("Unimplemented");
3024 return zero;
3025 case Tegra::Shader::VmadType::Invalid:
3026 // Note(Rodrigo): This flag is invalid according to nvdisasm. From my
3027 // testing (even though it's invalid) this makes the whole instruction
3028 // assign zero to target register.
3029 forced_result = boost::make_optional(zero);
3030 return zero;
3031 default:
3032 UNREACHABLE();
3033 return zero;
3034 }
3035 }();
3036
3037 if (is_signed) {
3038 return "int(" + value + ')';
3039 }
3040 return value;
3041 };
3042
3043 const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
3044 instr.vmad.is_byte_chunk_a != 0, signed_a,
3045 instr.vmad.type_a, instr.vmad.byte_height_a);
3046
3047 std::string op_b;
3048 if (instr.vmad.use_register_b) {
3049 op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
3050 instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b,
3051 instr.vmad.byte_height_b);
3052 } else {
3053 op_b = '(' +
3054 std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
3055 : instr.alu.GetImm20_16()) +
3056 ')';
3057 }
3058
3059 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed);
3060
3061 std::string result;
3062 if (forced_result) {
3063 result = *forced_result;
3064 } else {
3065 result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
3066
3067 switch (instr.vmad.shr) {
3068 case Tegra::Shader::VmadShr::Shr7:
3069 result = '(' + result + " >> 7)";
3070 break;
3071 case Tegra::Shader::VmadShr::Shr15:
3072 result = '(' + result + " >> 15)";
3073 break;
3074 }
3075 }
3076 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
3077 instr.vmad.saturate == 1, 0, Register::Size::Word,
3078 instr.vmad.cc);
3079 break;
3080 }
2780 default: { 3081 default: {
2781 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); 3082 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
2782 UNREACHABLE(); 3083 UNREACHABLE();
@@ -2907,7 +3208,7 @@ private:
2907 3208
2908 ShaderWriter shader; 3209 ShaderWriter shader;
2909 ShaderWriter declarations; 3210 ShaderWriter declarations;
2910 GLSLRegisterManager regs{shader, declarations, stage, suffix}; 3211 GLSLRegisterManager regs{shader, declarations, stage, suffix, header};
2911 3212
2912 // Declarations 3213 // Declarations
2913 std::set<std::string> declr_predicates; 3214 std::set<std::string> declr_predicates;
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index b0466c18f..1e5eb32df 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -17,7 +17,18 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
17 std::string out = "#version 430 core\n"; 17 std::string out = "#version 430 core\n";
18 out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; 18 out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
19 out += Decompiler::GetCommonDeclarations(); 19 out += Decompiler::GetCommonDeclarations();
20 out += "bool exec_vertex();\n"; 20
21 out += R"(
22out gl_PerVertex {
23 vec4 gl_Position;
24};
25
26layout(std140) uniform vs_config {
27 vec4 viewport_flip;
28 uvec4 instance_id;
29 uvec4 flip_stage;
30};
31)";
21 32
22 if (setup.IsDualProgram()) { 33 if (setup.IsDualProgram()) {
23 out += "bool exec_vertex_b();\n"; 34 out += "bool exec_vertex_b();\n";
@@ -28,18 +39,17 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
28 Maxwell3D::Regs::ShaderStage::Vertex, "vertex") 39 Maxwell3D::Regs::ShaderStage::Vertex, "vertex")
29 .get_value_or({}); 40 .get_value_or({});
30 41
31 out += R"( 42 out += program.first;
32
33out gl_PerVertex {
34 vec4 gl_Position;
35};
36 43
37out vec4 position; 44 if (setup.IsDualProgram()) {
45 ProgramResult program_b =
46 Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET,
47 Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b")
48 .get_value_or({});
49 out += program_b.first;
50 }
38 51
39layout (std140) uniform vs_config { 52 out += R"(
40 vec4 viewport_flip;
41 uvec4 instance_id;
42};
43 53
44void main() { 54void main() {
45 position = vec4(0.0, 0.0, 0.0, 0.0); 55 position = vec4(0.0, 0.0, 0.0, 0.0);
@@ -52,27 +62,52 @@ void main() {
52 62
53 out += R"( 63 out += R"(
54 64
55 // Viewport can be flipped, which is unsupported by glViewport 65 // Check if the flip stage is VertexB
56 position.xy *= viewport_flip.xy; 66 if (flip_stage[0] == 1) {
67 // Viewport can be flipped, which is unsupported by glViewport
68 position.xy *= viewport_flip.xy;
69 }
57 gl_Position = position; 70 gl_Position = position;
58 71
59 // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0 72 // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0
60 // For now, this is here to bring order in lieu of proper emulation 73 // For now, this is here to bring order in lieu of proper emulation
61 position.w = 1.0; 74 if (flip_stage[0] == 1) {
75 position.w = 1.0;
76 }
62} 77}
63 78
64)"; 79)";
65 80
66 out += program.first; 81 return {out, program.second};
82}
67 83
68 if (setup.IsDualProgram()) { 84ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
69 ProgramResult program_b = 85 std::string out = "#version 430 core\n";
70 Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET, 86 out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
71 Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b") 87 out += Decompiler::GetCommonDeclarations();
72 .get_value_or({}); 88 out += "bool exec_geometry();\n";
73 out += program_b.first; 89
74 } 90 ProgramResult program =
91 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
92 Maxwell3D::Regs::ShaderStage::Geometry, "geometry")
93 .get_value_or({});
94 out += R"(
95out gl_PerVertex {
96 vec4 gl_Position;
97};
75 98
99layout (std140) uniform gs_config {
100 vec4 viewport_flip;
101 uvec4 instance_id;
102 uvec4 flip_stage;
103};
104
105void main() {
106 exec_geometry();
107}
108
109)";
110 out += program.first;
76 return {out, program.second}; 111 return {out, program.second};
77} 112}
78 113
@@ -87,7 +122,6 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup) {
87 Maxwell3D::Regs::ShaderStage::Fragment, "fragment") 122 Maxwell3D::Regs::ShaderStage::Fragment, "fragment")
88 .get_value_or({}); 123 .get_value_or({});
89 out += R"( 124 out += R"(
90in vec4 position;
91layout(location = 0) out vec4 FragColor0; 125layout(location = 0) out vec4 FragColor0;
92layout(location = 1) out vec4 FragColor1; 126layout(location = 1) out vec4 FragColor1;
93layout(location = 2) out vec4 FragColor2; 127layout(location = 2) out vec4 FragColor2;
@@ -100,6 +134,7 @@ layout(location = 7) out vec4 FragColor7;
100layout (std140) uniform fs_config { 134layout (std140) uniform fs_config {
101 vec4 viewport_flip; 135 vec4 viewport_flip;
102 uvec4 instance_id; 136 uvec4 instance_id;
137 uvec4 flip_stage;
103}; 138};
104 139
105void main() { 140void main() {
@@ -110,5 +145,4 @@ void main() {
110 out += program.first; 145 out += program.first;
111 return {out, program.second}; 146 return {out, program.second};
112} 147}
113 148} // namespace OpenGL::GLShader \ No newline at end of file
114} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index e56f39e78..79596087a 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -196,6 +196,12 @@ private:
196ProgramResult GenerateVertexShader(const ShaderSetup& setup); 196ProgramResult GenerateVertexShader(const ShaderSetup& setup);
197 197
198/** 198/**
199 * Generates the GLSL geometry shader program source code for the given GS program
200 * @returns String of the shader source code
201 */
202ProgramResult GenerateGeometryShader(const ShaderSetup& setup);
203
204/**
199 * Generates the GLSL fragment shader program source code for the given FS program 205 * Generates the GLSL fragment shader program source code for the given FS program
200 * @returns String of the shader source code 206 * @returns String of the shader source code
201 */ 207 */
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 022d32a86..010857ec6 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -18,6 +18,14 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
18 18
19 // We only assign the instance to the first component of the vector, the rest is just padding. 19 // We only assign the instance to the first component of the vector, the rest is just padding.
20 instance_id[0] = state.current_instance; 20 instance_id[0] = state.current_instance;
21
22 // Assign in which stage the position has to be flipped
23 // (the last stage before the fragment shader).
24 if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) {
25 flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
26 } else {
27 flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
28 }
21} 29}
22 30
23} // namespace OpenGL::GLShader 31} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 3de15ba9b..b3a191cf2 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -21,8 +21,9 @@ struct MaxwellUniformData {
21 void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage); 21 void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage);
22 alignas(16) GLvec4 viewport_flip; 22 alignas(16) GLvec4 viewport_flip;
23 alignas(16) GLuvec4 instance_id; 23 alignas(16) GLuvec4 instance_id;
24 alignas(16) GLuvec4 flip_stage;
24}; 25};
25static_assert(sizeof(MaxwellUniformData) == 32, "MaxwellUniformData structure size is incorrect"); 26static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect");
26static_assert(sizeof(MaxwellUniformData) < 16384, 27static_assert(sizeof(MaxwellUniformData) < 16384,
27 "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); 28 "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
28 29
@@ -36,6 +37,10 @@ public:
36 vs = program; 37 vs = program;
37 } 38 }
38 39
40 void UseProgrammableGeometryShader(GLuint program) {
41 gs = program;
42 }
43
39 void UseProgrammableFragmentShader(GLuint program) { 44 void UseProgrammableFragmentShader(GLuint program) {
40 fs = program; 45 fs = program;
41 } 46 }
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 3d5476e5d..18ab723f7 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -4,6 +4,7 @@
4 4
5#include <cmath> 5#include <cmath>
6#include <cstring> 6#include <cstring>
7#include "common/alignment.h"
7#include "common/assert.h" 8#include "common/assert.h"
8#include "core/memory.h" 9#include "core/memory.h"
9#include "video_core/gpu.h" 10#include "video_core/gpu.h"
@@ -39,72 +40,146 @@ struct alignas(64) SwizzleTable {
39constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>(); 40constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>();
40constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>(); 41constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>();
41 42
42static void LegacySwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 43/**
43 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, 44 * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
44 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) {
45 std::array<u8*, 2> data_ptrs; 53 std::array<u8*, 2> data_ptrs;
46 const std::size_t stride = width * bytes_per_pixel; 54 u32 z_address = tile_offset;
47 const std::size_t gobs_in_x = 64; 55 const u32 gob_size_x = 64;
48 const std::size_t gobs_in_y = 8; 56 const u32 gob_size_y = 8;
49 const std::size_t gobs_size = gobs_in_x * gobs_in_y; 57 const u32 gob_size_z = 1;
50 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;
51 for (std::size_t y = 0; y < height; ++y) { 59 for (u32 z = z_start; z < z_end; z++) {
52 const std::size_t gob_y_address = 60 u32 y_address = z_address;
53 (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;
54 (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; 62 for (u32 y = y_start; y < y_end; y++) {
55 const auto& table = legacy_swizzle_table[y % gobs_in_y]; 63 const auto& table = legacy_swizzle_table[y % gob_size_y];
56 for (std::size_t x = 0; x < width; ++x) { 64 for (u32 x = x_start; x < x_end; x++) {
57 const std::size_t gob_address = 65 const u32 swizzle_offset{y_address + table[x * bytes_per_pixel % gob_size_x]};
58 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};
59 const std::size_t x2 = x * bytes_per_pixel; 67 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
60 const std::size_t swizzle_offset = gob_address + table[x2 % gobs_in_x]; 68 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
61 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);
62 70 }
63 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 71 pixel_base += stride_x;
64 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 72 if ((y + 1) % gob_size_y == 0)
65 73 y_address += gob_size;
66 std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
67 } 74 }
75 z_address += xy_block_size;
68 } 76 }
69} 77}
70 78
71static void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 79/**
72 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, 80 * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
73 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) {
74 std::array<u8*, 2> data_ptrs; 89 std::array<u8*, 2> data_ptrs;
75 const std::size_t stride{width * bytes_per_pixel}; 90 u32 z_address = tile_offset;
76 const std::size_t gobs_in_x = 64; 91 const u32 x_startb = x_start * bytes_per_pixel;
77 const std::size_t gobs_in_y = 8; 92 const u32 x_endb = x_end * bytes_per_pixel;
78 const std::size_t gobs_size = gobs_in_x * gobs_in_y; 93 const u32 copy_size = 16;
79 const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; 94 const u32 gob_size_x = 64;
80 const std::size_t copy_size{16}; 95 const u32 gob_size_y = 8;
81 for (std::size_t y = 0; y < height; ++y) { 96 const u32 gob_size_z = 1;
82 const std::size_t initial_gob = 97 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
83 (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + 98 for (u32 z = z_start; z < z_end; z++) {
84 (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; 99 u32 y_address = z_address;
85 const std::size_t pixel_base{y * width * out_bytes_per_pixel}; 100 u32 pixel_base = layer_z * z + y_start * stride_x;
86 const auto& table = fast_swizzle_table[y % gobs_in_y]; 101 for (u32 y = y_start; y < y_end; y++) {
87 for (std::size_t xb = 0; xb < stride; xb += copy_size) { 102 const auto& table = fast_swizzle_table[y % gob_size_y];
88 const std::size_t gob_address{initial_gob + 103 for (u32 xb = x_startb; xb < x_endb; xb += copy_size) {
89 (xb / gobs_in_x) * gobs_size * block_height}; 104 const u32 swizzle_offset{y_address + table[(xb / copy_size) % 4]};
90 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;
91 const std::size_t out_x = xb * out_bytes_per_pixel / bytes_per_pixel; 106 const u32 pixel_index{out_x + pixel_base};
92 const std::size_t pixel_index{out_x + pixel_base}; 107 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
93 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 108 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
94 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 109 std::memcpy(data_ptrs[0], data_ptrs[1], copy_size);
95 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;
96 } 114 }
115 z_address += xy_block_size;
97 } 116 }
98} 117}
99 118
100void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 119/**
101 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) {
102 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) {
103 FastSwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, 178 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
104 unswizzled_data, unswizzle, block_height); 179 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
105 } else { 180 } else {
106 LegacySwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, 181 SwizzledData<false>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
107 unswizzled_data, unswizzle, block_height); 182 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
108 } 183 }
109} 184}
110 185
@@ -125,7 +200,9 @@ u32 BytesPerPixel(TextureFormat format) {
125 case TextureFormat::R32_G32_B32: 200 case TextureFormat::R32_G32_B32:
126 return 12; 201 return 12;
127 case TextureFormat::ASTC_2D_4X4: 202 case TextureFormat::ASTC_2D_4X4:
203 case TextureFormat::ASTC_2D_5X4:
128 case TextureFormat::ASTC_2D_8X8: 204 case TextureFormat::ASTC_2D_8X8:
205 case TextureFormat::ASTC_2D_8X5:
129 case TextureFormat::A8R8G8B8: 206 case TextureFormat::A8R8G8B8:
130 case TextureFormat::A2B10G10R10: 207 case TextureFormat::A2B10G10R10:
131 case TextureFormat::BF10GF11RF11: 208 case TextureFormat::BF10GF11RF11:
@@ -152,10 +229,11 @@ u32 BytesPerPixel(TextureFormat format) {
152} 229}
153 230
154std::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,
155 u32 height, u32 block_height) { 232 u32 height, u32 depth, u32 block_height, u32 block_depth) {
156 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); 233 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
157 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,
158 Memory::GetPointer(address), unswizzled_data.data(), true, block_height); 235 Memory::GetPointer(address), unswizzled_data.data(), true, block_height,
236 block_depth);
159 return unswizzled_data; 237 return unswizzled_data;
160} 238}
161 239
@@ -199,4 +277,19 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
199 return rgba_data; 277 return rgba_data;
200} 278}
201 279
280std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
281 u32 block_height, u32 block_depth) {
282 if (tiled) {
283 const u32 gobs_in_x = 64 / bytes_per_pixel;
284 const u32 gobs_in_y = 8;
285 const u32 gobs_in_z = 1;
286 const u32 aligned_width = Common::AlignUp(width, gobs_in_x);
287 const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
288 const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
289 return aligned_width * aligned_height * aligned_depth * bytes_per_pixel;
290 } else {
291 return width * height * depth * bytes_per_pixel;
292 }
293}
294
202} // namespace Tegra::Texture 295} // namespace Tegra::Texture
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 1f7b731be..aaf316947 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.
@@ -32,4 +29,10 @@ void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_
32std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, 29std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
33 u32 height); 30 u32 height);
34 31
32/**
33 * This function calculates the correct size of a texture depending if it's tiled or not.
34 */
35std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
36 u32 block_height, u32 block_depth);
37
35} // namespace Tegra::Texture 38} // namespace Tegra::Texture
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 8f31d825a..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;
@@ -161,7 +162,9 @@ struct TICEntry {
161 BitField<21, 3, TICHeaderVersion> header_version; 162 BitField<21, 3, TICHeaderVersion> header_version;
162 }; 163 };
163 union { 164 union {
165 BitField<0, 3, u32> block_width;
164 BitField<3, 3, u32> block_height; 166 BitField<3, 3, u32> block_height;
167 BitField<6, 3, u32> block_depth;
165 168
166 // High 16 bits of the pitch value 169 // High 16 bits of the pitch value
167 BitField<0, 16, u32> pitch_high; 170 BitField<0, 16, u32> pitch_high;
@@ -202,13 +205,24 @@ struct TICEntry {
202 return depth_minus_1 + 1; 205 return depth_minus_1 + 1;
203 } 206 }
204 207
208 u32 BlockWidth() const {
209 ASSERT(IsTiled());
210 // The block height is stored in log2 format.
211 return 1 << block_width;
212 }
213
205 u32 BlockHeight() const { 214 u32 BlockHeight() const {
206 ASSERT(header_version == TICHeaderVersion::BlockLinear || 215 ASSERT(IsTiled());
207 header_version == TICHeaderVersion::BlockLinearColorKey);
208 // The block height is stored in log2 format. 216 // The block height is stored in log2 format.
209 return 1 << block_height; 217 return 1 << block_height;
210 } 218 }
211 219
220 u32 BlockDepth() const {
221 ASSERT(IsTiled());
222 // The block height is stored in log2 format.
223 return 1 << block_depth;
224 }
225
212 bool IsTiled() const { 226 bool IsTiled() const {
213 return header_version == TICHeaderVersion::BlockLinear || 227 return header_version == TICHeaderVersion::BlockLinear ||
214 header_version == TICHeaderVersion::BlockLinearColorKey; 228 header_version == TICHeaderVersion::BlockLinearColorKey;
diff --git a/src/video_core/utils.h b/src/video_core/utils.h
index 681919ae3..237cc1307 100644
--- a/src/video_core/utils.h
+++ b/src/video_core/utils.h
@@ -169,16 +169,20 @@ static void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr,
169 const std::string nice_addr = fmt::format("0x{:016x}", addr); 169 const std::string nice_addr = fmt::format("0x{:016x}", addr);
170 std::string object_label; 170 std::string object_label;
171 171
172 switch (identifier) { 172 if (extra_info.empty()) {
173 case GL_TEXTURE: 173 switch (identifier) {
174 object_label = extra_info + "@" + nice_addr; 174 case GL_TEXTURE:
175 break; 175 object_label = "Texture@" + nice_addr;
176 case GL_PROGRAM: 176 break;
177 object_label = "ShaderProgram@" + nice_addr; 177 case GL_PROGRAM:
178 break; 178 object_label = "Shader@" + nice_addr;
179 default: 179 break;
180 object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr); 180 default:
181 break; 181 object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
182 break;
183 }
184 } else {
185 object_label = extra_info + '@' + nice_addr;
182 } 186 }
183 glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str())); 187 glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
184} 188}
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 d2b3de683..3881aba5f 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -27,9 +27,8 @@
27#include "yuzu/ui_settings.h" 27#include "yuzu/ui_settings.h"
28 28
29namespace { 29namespace {
30void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, 30void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const FileSys::NCA& nca,
31 const std::shared_ptr<FileSys::NCA>& nca, std::vector<u8>& icon, 31 std::vector<u8>& icon, std::string& name) {
32 std::string& name) {
33 auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca); 32 auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca);
34 if (icon_file != nullptr) 33 if (icon_file != nullptr)
35 icon = icon_file->ReadAllBytes(); 34 icon = icon_file->ReadAllBytes();
@@ -97,7 +96,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
97 FileSys::ContentRecordType::Program); 96 FileSys::ContentRecordType::Program);
98 97
99 for (const auto& game : installed_games) { 98 for (const auto& game : installed_games) {
100 const auto& file = cache->GetEntryUnparsed(game); 99 const auto file = cache->GetEntryUnparsed(game);
101 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); 100 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
102 if (!loader) 101 if (!loader)
103 continue; 102 continue;
@@ -108,9 +107,9 @@ void GameListWorker::AddInstalledTitlesToGameList() {
108 loader->ReadProgramId(program_id); 107 loader->ReadProgramId(program_id);
109 108
110 const FileSys::PatchManager patch{program_id}; 109 const FileSys::PatchManager patch{program_id};
111 const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); 110 const auto control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
112 if (control != nullptr) 111 if (control != nullptr)
113 GetMetadataFromControlNCA(patch, control, icon, name); 112 GetMetadataFromControlNCA(patch, *control, icon, name);
114 113
115 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); 114 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
116 115
@@ -136,9 +135,10 @@ void GameListWorker::AddInstalledTitlesToGameList() {
136 FileSys::ContentRecordType::Control); 135 FileSys::ContentRecordType::Control);
137 136
138 for (const auto& entry : control_data) { 137 for (const auto& entry : control_data) {
139 const auto nca = cache->GetEntry(entry); 138 auto nca = cache->GetEntry(entry);
140 if (nca != nullptr) 139 if (nca != nullptr) {
141 nca_control_map.insert_or_assign(entry.title_id, nca); 140 nca_control_map.insert_or_assign(entry.title_id, std::move(nca));
141 }
142 } 142 }
143} 143}
144 144
@@ -154,9 +154,11 @@ void GameListWorker::FillControlMap(const std::string& dir_path) {
154 QFileInfo file_info(physical_name.c_str()); 154 QFileInfo file_info(physical_name.c_str());
155 if (!is_dir && file_info.suffix().toStdString() == "nca") { 155 if (!is_dir && file_info.suffix().toStdString() == "nca") {
156 auto nca = 156 auto nca =
157 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));
158 if (nca->GetType() == FileSys::NCAContentType::Control) 158 if (nca->GetType() == FileSys::NCAContentType::Control) {
159 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 }
160 } 162 }
161 return true; 163 return true;
162 }; 164 };
@@ -197,8 +199,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
197 res2 == Loader::ResultStatus::Success) { 199 res2 == Loader::ResultStatus::Success) {
198 // Use from metadata pool. 200 // Use from metadata pool.
199 if (nca_control_map.find(program_id) != nca_control_map.end()) { 201 if (nca_control_map.find(program_id) != nca_control_map.end()) {
200 const auto nca = nca_control_map[program_id]; 202 const auto& nca = nca_control_map[program_id];
201 GetMetadataFromControlNCA(patch, nca, icon, name); 203 GetMetadataFromControlNCA(patch, *nca, icon, name);
202 } 204 }
203 } 205 }
204 206
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 e11833c5a..bef9df00d 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -31,6 +31,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
31#include <QDialogButtonBox> 31#include <QDialogButtonBox>
32#include <QFileDialog> 32#include <QFileDialog>
33#include <QMessageBox> 33#include <QMessageBox>
34#include <QtConcurrent/QtConcurrent>
34#include <QtGui> 35#include <QtGui>
35#include <QtWidgets> 36#include <QtWidgets>
36#include <fmt/format.h> 37#include <fmt/format.h>
@@ -171,8 +172,11 @@ GMainWindow::GMainWindow()
171 .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc)); 172 .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc));
172 show(); 173 show();
173 174
175 // Gen keys if necessary
176 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
177
174 // Necessary to load titles from nand in gamelist. 178 // Necessary to load titles from nand in gamelist.
175 Service::FileSystem::CreateFactories(vfs); 179 Service::FileSystem::CreateFactories(*vfs);
176 game_list->LoadCompatibilityList(); 180 game_list->LoadCompatibilityList();
177 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 181 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
178 182
@@ -443,6 +447,8 @@ void GMainWindow::ConnectMenuEvents() {
443 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 447 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
444 448
445 // Help 449 // Help
450 connect(ui.action_Rederive, &QAction::triggered, this,
451 std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
446 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout); 452 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
447} 453}
448 454
@@ -902,22 +908,20 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
902} 908}
903 909
904void GMainWindow::OnMenuLoadFile() { 910void GMainWindow::OnMenuLoadFile() {
905 QString extensions; 911 const QString extensions =
906 for (const auto& piece : game_list->supported_file_extensions) 912 QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main");
907 extensions += "*." + piece + " "; 913 const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)",
908 914 "%1 is an identifier for the Switch executable file extensions.")
909 extensions += "main "; 915 .arg(extensions);
916 const QString filename = QFileDialog::getOpenFileName(
917 this, tr("Load File"), UISettings::values.roms_path, file_filter);
910 918
911 QString file_filter = tr("Switch Executable") + " (" + extensions + ")"; 919 if (filename.isEmpty()) {
912 file_filter += ";;" + tr("All Files (*.*)"); 920 return;
913
914 QString filename = QFileDialog::getOpenFileName(this, tr("Load File"),
915 UISettings::values.roms_path, file_filter);
916 if (!filename.isEmpty()) {
917 UISettings::values.roms_path = QFileInfo(filename).path();
918
919 BootGame(filename);
920 } 921 }
922
923 UISettings::values.roms_path = QFileInfo(filename).path();
924 BootGame(filename);
921} 925}
922 926
923void GMainWindow::OnMenuLoadFolder() { 927void GMainWindow::OnMenuLoadFolder() {
@@ -1133,7 +1137,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
1133 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir 1137 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
1134 : FileUtil::UserPath::NANDDir, 1138 : FileUtil::UserPath::NANDDir,
1135 dir_path.toStdString()); 1139 dir_path.toStdString());
1136 Service::FileSystem::CreateFactories(vfs); 1140 Service::FileSystem::CreateFactories(*vfs);
1137 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1141 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
1138 } 1142 }
1139} 1143}
@@ -1375,6 +1379,86 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
1375 } 1379 }
1376} 1380}
1377 1381
1382void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1383 if (behavior == ReinitializeKeyBehavior::Warning) {
1384 const auto res = QMessageBox::information(
1385 this, tr("Confirm Key Rederivation"),
1386 tr("You are about to force rederive all of your keys. \nIf you do not know what this "
1387 "means or what you are doing, \nthis is a potentially destructive action. \nPlease "
1388 "make "
1389 "sure this is what you want \nand optionally make backups.\n\nThis will delete your "
1390 "autogenerated key files and re-run the key derivation module."),
1391 QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
1392
1393 if (res == QMessageBox::Cancel)
1394 return;
1395
1396 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
1397 "prod.keys_autogenerated");
1398 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
1399 "console.keys_autogenerated");
1400 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
1401 "title.keys_autogenerated");
1402 }
1403
1404 Core::Crypto::KeyManager keys{};
1405 if (keys.BaseDeriveNecessary()) {
1406 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory(
1407 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)};
1408
1409 const auto function = [this, &keys, &pdm] {
1410 keys.PopulateFromPartitionData(pdm);
1411 Service::FileSystem::CreateFactories(*vfs);
1412 keys.DeriveETicket(pdm);
1413 };
1414
1415 QString errors;
1416
1417 if (!pdm.HasFuses())
1418 errors += tr("- Missing fuses - Cannot derive SBK\n");
1419 if (!pdm.HasBoot0())
1420 errors += tr("- Missing BOOT0 - Cannot derive master keys\n");
1421 if (!pdm.HasPackage2())
1422 errors += tr("- Missing BCPKG2-1-Normal-Main - Cannot derive general keys\n");
1423 if (!pdm.HasProdInfo())
1424 errors += tr("- Missing PRODINFO - Cannot derive title keys\n");
1425
1426 if (!errors.isEmpty()) {
1427
1428 QMessageBox::warning(
1429 this, tr("Warning Missing Derivation Components"),
1430 tr("The following are missing from your configuration that may hinder key "
1431 "derivation. It will be attempted but may not complete.<br><br>") +
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."));
1437 }
1438
1439 QProgressDialog prog;
1440 prog.setRange(0, 0);
1441 prog.setLabelText(tr("Deriving keys...\nThis may take up to a minute depending \non your "
1442 "system's performance."));
1443 prog.setWindowTitle(tr("Deriving Keys"));
1444
1445 prog.show();
1446
1447 auto future = QtConcurrent::run(function);
1448 while (!future.isFinished()) {
1449 QCoreApplication::processEvents();
1450 }
1451
1452 prog.close();
1453 }
1454
1455 Service::FileSystem::CreateFactories(*vfs);
1456
1457 if (behavior == ReinitializeKeyBehavior::Warning) {
1458 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
1459 }
1460}
1461
1378bool GMainWindow::ConfirmClose() { 1462bool GMainWindow::ConfirmClose() {
1379 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) 1463 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
1380 return true; 1464 return true;
@@ -1483,7 +1567,7 @@ void GMainWindow::UpdateUITheme() {
1483 emit UpdateThemedIcons(); 1567 emit UpdateThemedIcons();
1484} 1568}
1485 1569
1486void GMainWindow::SetDiscordEnabled(bool state) { 1570void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
1487#ifdef USE_DISCORD_PRESENCE 1571#ifdef USE_DISCORD_PRESENCE
1488 if (state) { 1572 if (state) {
1489 discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>(); 1573 discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index fe0e9a50a..3663d6aed 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -41,6 +41,11 @@ enum class EmulatedDirectoryTarget {
41 SDMC, 41 SDMC,
42}; 42};
43 43
44enum class ReinitializeKeyBehavior {
45 NoWarning,
46 Warning,
47};
48
44namespace DiscordRPC { 49namespace DiscordRPC {
45class DiscordInterface; 50class DiscordInterface;
46} 51}
@@ -167,6 +172,7 @@ private slots:
167 void HideFullscreen(); 172 void HideFullscreen();
168 void ToggleWindowMode(); 173 void ToggleWindowMode();
169 void OnCoreError(Core::System::ResultStatus, std::string); 174 void OnCoreError(Core::System::ResultStatus, std::string);
175 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
170 176
171private: 177private:
172 void UpdateStatusBar(); 178 void UpdateStatusBar();
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index cb1664b21..9851f507d 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -103,6 +103,7 @@
103 </property> 103 </property>
104 <addaction name="action_Report_Compatibility"/> 104 <addaction name="action_Report_Compatibility"/>
105 <addaction name="separator"/> 105 <addaction name="separator"/>
106 <addaction name="action_Rederive"/>
106 <addaction name="action_About"/> 107 <addaction name="action_About"/>
107 </widget> 108 </widget>
108 <addaction name="menu_File"/> 109 <addaction name="menu_File"/>
@@ -159,6 +160,11 @@
159 <string>&amp;Stop</string> 160 <string>&amp;Stop</string>
160 </property> 161 </property>
161 </action> 162 </action>
163 <action name="action_Rederive">
164 <property name="text">
165 <string>Reinitialize keys...</string>
166 </property>
167 </action>
162 <action name="action_About"> 168 <action name="action_About">
163 <property name="text"> 169 <property name="text">
164 <string>About yuzu</string> 170 <string>About yuzu</string>
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