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.cpp7
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h4
-rw-r--r--src/core/core.cpp43
-rw-r--r--src/core/core.h8
-rw-r--r--src/core/core_cpu.cpp14
-rw-r--r--src/core/core_cpu.h17
-rw-r--r--src/core/crypto/key_manager.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/control_metadata.cpp13
-rw-r--r--src/core/file_sys/control_metadata.h1
-rw-r--r--src/core/file_sys/patch_manager.cpp2
-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/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/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/svc.cpp39
-rw-r--r--src/core/hle/kernel/svc_wrap.h8
-rw-r--r--src/core/hle/kernel/thread.cpp16
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp18
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp29
-rw-r--r--src/core/hle/service/filesystem/filesystem.h12
-rw-r--r--src/core/hle/service/ns/pl_u.cpp2
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/hle/service/service.h3
-rw-r--r--src/core/hle/service/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/video_core/engines/fermi_2d.cpp14
-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.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp59
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h20
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp8
-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.h1
-rw-r--r--src/web_service/telemetry_json.cpp90
-rw-r--r--src/web_service/telemetry_json.h24
-rw-r--r--src/web_service/verify_login.cpp1
-rw-r--r--src/web_service/web_backend.cpp235
-rw-r--r--src/web_service/web_backend.h58
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp5
-rw-r--r--src/yuzu/debugger/wait_tree.cpp9
-rw-r--r--src/yuzu/game_list_worker.cpp19
-rw-r--r--src/yuzu/game_list_worker.h2
-rw-r--r--src/yuzu/main.cpp118
-rw-r--r--src/yuzu/main.h6
-rw-r--r--src/yuzu/main.ui6
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
71 files changed, 2508 insertions, 667 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 e4a676e91..78986deb5 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
@@ -397,8 +400,8 @@ create_target_directory_groups(core)
397target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 400target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
398target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives) 401target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives)
399if (ENABLE_WEB_SERVICE) 402if (ENABLE_WEB_SERVICE)
400 add_definitions(-DENABLE_WEB_SERVICE) 403 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
401 target_link_libraries(core PUBLIC json-headers web_service) 404 target_link_libraries(core PRIVATE web_service)
402endif() 405endif()
403 406
404if (ARCHITECTURE_x86_64) 407if (ARCHITECTURE_x86_64)
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 0762321a9..4d2491870 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -144,7 +144,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
144 144
145 // Multi-process state 145 // Multi-process state
146 config.processor_id = core_index; 146 config.processor_id = core_index;
147 config.global_monitor = &exclusive_monitor->monitor; 147 config.global_monitor = &exclusive_monitor.monitor;
148 148
149 // System registers 149 // System registers
150 config.tpidrro_el0 = &cb->tpidrro_el0; 150 config.tpidrro_el0 = &cb->tpidrro_el0;
@@ -171,10 +171,9 @@ void ARM_Dynarmic::Step() {
171 cb->InterpreterFallback(jit->GetPC(), 1); 171 cb->InterpreterFallback(jit->GetPC(), 1);
172} 172}
173 173
174ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 174ARM_Dynarmic::ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index)
175 std::size_t core_index)
176 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index}, 175 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index},
177 exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} { 176 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
178 ThreadContext ctx{}; 177 ThreadContext ctx{};
179 inner_unicorn.SaveContext(ctx); 178 inner_unicorn.SaveContext(ctx);
180 PageTableChanged(); 179 PageTableChanged();
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 4ee92ee27..512bf8ce9 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -23,7 +23,7 @@ class DynarmicExclusiveMonitor;
23 23
24class ARM_Dynarmic final : public ARM_Interface { 24class ARM_Dynarmic final : public ARM_Interface {
25public: 25public:
26 ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, std::size_t core_index); 26 ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
27 ~ARM_Dynarmic(); 27 ~ARM_Dynarmic();
28 28
29 void MapBackingMemory(VAddr address, std::size_t size, u8* memory, 29 void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
@@ -62,7 +62,7 @@ private:
62 ARM_Unicorn inner_unicorn; 62 ARM_Unicorn inner_unicorn;
63 63
64 std::size_t core_index; 64 std::size_t core_index;
65 std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor; 65 DynarmicExclusiveMonitor& exclusive_monitor;
66 66
67 Memory::PageTable* current_page_table = nullptr; 67 Memory::PageTable* current_page_table = nullptr;
68}; 68};
diff --git a/src/core/core.cpp b/src/core/core.cpp
index e2fb9e038..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();
@@ -139,16 +139,16 @@ struct System::Impl {
139 auto main_process = Kernel::Process::Create(kernel, "main"); 139 auto main_process = Kernel::Process::Create(kernel, "main");
140 kernel.MakeCurrentProcess(main_process.get()); 140 kernel.MakeCurrentProcess(main_process.get());
141 141
142 cpu_barrier = std::make_shared<CpuBarrier>(); 142 cpu_barrier = std::make_unique<CpuBarrier>();
143 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); 143 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
144 for (std::size_t index = 0; index < cpu_cores.size(); ++index) { 144 for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
145 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); 145 cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);
146 } 146 }
147 147
148 telemetry_session = std::make_unique<Core::TelemetrySession>(); 148 telemetry_session = std::make_unique<Core::TelemetrySession>();
149 service_manager = std::make_shared<Service::SM::ServiceManager>(); 149 service_manager = std::make_shared<Service::SM::ServiceManager>();
150 150
151 Service::Init(service_manager, virtual_filesystem); 151 Service::Init(service_manager, *virtual_filesystem);
152 GDBStub::Init(); 152 GDBStub::Init();
153 153
154 renderer = VideoCore::CreateRenderer(emu_window); 154 renderer = VideoCore::CreateRenderer(emu_window);
@@ -160,12 +160,12 @@ struct System::Impl {
160 160
161 // Create threads for CPU cores 1-3, and build thread_to_cpu map 161 // Create threads for CPU cores 1-3, and build thread_to_cpu map
162 // CPU core 0 is run on the main thread 162 // CPU core 0 is run on the main thread
163 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 163 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
164 if (Settings::values.use_multi_core) { 164 if (Settings::values.use_multi_core) {
165 for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) { 165 for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
166 cpu_core_threads[index] = 166 cpu_core_threads[index] =
167 std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]); 167 std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
168 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1]; 168 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();
169 } 169 }
170 } 170 }
171 171
@@ -245,6 +245,7 @@ struct System::Impl {
245 for (auto& cpu_core : cpu_cores) { 245 for (auto& cpu_core : cpu_cores) {
246 cpu_core.reset(); 246 cpu_core.reset();
247 } 247 }
248 cpu_exclusive_monitor.reset();
248 cpu_barrier.reset(); 249 cpu_barrier.reset();
249 250
250 // Shutdown kernel and core timing 251 // Shutdown kernel and core timing
@@ -282,9 +283,9 @@ struct System::Impl {
282 std::unique_ptr<VideoCore::RendererBase> renderer; 283 std::unique_ptr<VideoCore::RendererBase> renderer;
283 std::unique_ptr<Tegra::GPU> gpu_core; 284 std::unique_ptr<Tegra::GPU> gpu_core;
284 std::shared_ptr<Tegra::DebugContext> debug_context; 285 std::shared_ptr<Tegra::DebugContext> debug_context;
285 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor; 286 std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
286 std::shared_ptr<CpuBarrier> cpu_barrier; 287 std::unique_ptr<CpuBarrier> cpu_barrier;
287 std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; 288 std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
288 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; 289 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
289 std::size_t active_core{}; ///< Active core, only used in single thread mode 290 std::size_t active_core{}; ///< Active core, only used in single thread mode
290 291
@@ -298,7 +299,7 @@ struct System::Impl {
298 std::string status_details = ""; 299 std::string status_details = "";
299 300
300 /// Map of guest threads to CPU cores 301 /// Map of guest threads to CPU cores
301 std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu; 302 std::map<std::thread::id, Cpu*> thread_to_cpu;
302 303
303 Core::PerfStats perf_stats; 304 Core::PerfStats perf_stats;
304 Core::FrameLimiter frame_limiter; 305 Core::FrameLimiter frame_limiter;
@@ -354,12 +355,15 @@ std::size_t System::CurrentCoreIndex() {
354} 355}
355 356
356Kernel::Scheduler& System::CurrentScheduler() { 357Kernel::Scheduler& System::CurrentScheduler() {
357 return *CurrentCpuCore().Scheduler(); 358 return CurrentCpuCore().Scheduler();
358} 359}
359 360
360const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_index) { 361Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
361 ASSERT(core_index < NUM_CPU_CORES); 362 return CpuCore(core_index).Scheduler();
362 return impl->cpu_cores[core_index]->Scheduler(); 363}
364
365const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const {
366 return CpuCore(core_index).Scheduler();
363} 367}
364 368
365Kernel::Process* System::CurrentProcess() { 369Kernel::Process* System::CurrentProcess() {
@@ -380,6 +384,11 @@ Cpu& System::CpuCore(std::size_t core_index) {
380 return *impl->cpu_cores[core_index]; 384 return *impl->cpu_cores[core_index];
381} 385}
382 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
383ExclusiveMonitor& System::Monitor() { 392ExclusiveMonitor& System::Monitor() {
384 return *impl->cpu_exclusive_monitor; 393 return *impl->cpu_exclusive_monitor;
385} 394}
diff --git a/src/core/core.h b/src/core/core.h
index ea4d53914..173be45f8 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -156,6 +156,9 @@ public:
156 /// Gets a CPU interface to the CPU core with the specified index 156 /// Gets a CPU interface to the CPU core with the specified index
157 Cpu& CpuCore(std::size_t core_index); 157 Cpu& CpuCore(std::size_t core_index);
158 158
159 /// Gets a CPU interface to the CPU core with the specified index
160 const Cpu& CpuCore(std::size_t core_index) const;
161
159 /// Gets the exclusive monitor 162 /// Gets the exclusive monitor
160 ExclusiveMonitor& Monitor(); 163 ExclusiveMonitor& Monitor();
161 164
@@ -172,7 +175,10 @@ public:
172 const VideoCore::RendererBase& Renderer() const; 175 const VideoCore::RendererBase& Renderer() const;
173 176
174 /// Gets the scheduler for the CPU core with the specified index 177 /// Gets the scheduler for the CPU core with the specified index
175 const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index); 178 Kernel::Scheduler& Scheduler(std::size_t core_index);
179
180 /// Gets the scheduler for the CPU core with the specified index
181 const Kernel::Scheduler& Scheduler(std::size_t core_index) const;
176 182
177 /// Provides a pointer to the current process 183 /// Provides a pointer to the current process
178 Kernel::Process* CurrentProcess(); 184 Kernel::Process* CurrentProcess();
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 265f8ed9c..fffda8a99 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -49,10 +49,8 @@ bool CpuBarrier::Rendezvous() {
49 return false; 49 return false;
50} 50}
51 51
52Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 52Cpu::Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index)
53 std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index) 53 : cpu_barrier{cpu_barrier}, core_index{core_index} {
54 : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
55
56 if (Settings::values.use_cpu_jit) { 54 if (Settings::values.use_cpu_jit) {
57#ifdef ARCHITECTURE_x86_64 55#ifdef ARCHITECTURE_x86_64
58 arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index); 56 arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index);
@@ -64,15 +62,15 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
64 arm_interface = std::make_unique<ARM_Unicorn>(); 62 arm_interface = std::make_unique<ARM_Unicorn>();
65 } 63 }
66 64
67 scheduler = std::make_shared<Kernel::Scheduler>(*arm_interface); 65 scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface);
68} 66}
69 67
70Cpu::~Cpu() = default; 68Cpu::~Cpu() = default;
71 69
72std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) { 70std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
73 if (Settings::values.use_cpu_jit) { 71 if (Settings::values.use_cpu_jit) {
74#ifdef ARCHITECTURE_x86_64 72#ifdef ARCHITECTURE_x86_64
75 return std::make_shared<DynarmicExclusiveMonitor>(num_cores); 73 return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
76#else 74#else
77 return nullptr; // TODO(merry): Passthrough exclusive monitor 75 return nullptr; // TODO(merry): Passthrough exclusive monitor
78#endif 76#endif
@@ -83,7 +81,7 @@ std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_core
83 81
84void Cpu::RunLoop(bool tight_loop) { 82void Cpu::RunLoop(bool tight_loop) {
85 // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step 83 // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
86 if (!cpu_barrier->Rendezvous()) { 84 if (!cpu_barrier.Rendezvous()) {
87 // If rendezvous failed, session has been killed 85 // If rendezvous failed, session has been killed
88 return; 86 return;
89 } 87 }
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index ee7e04abc..1d2bdc6cd 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -41,8 +41,7 @@ private:
41 41
42class Cpu { 42class Cpu {
43public: 43public:
44 Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 44 Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index);
45 std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index);
46 ~Cpu(); 45 ~Cpu();
47 46
48 void RunLoop(bool tight_loop = true); 47 void RunLoop(bool tight_loop = true);
@@ -59,8 +58,12 @@ public:
59 return *arm_interface; 58 return *arm_interface;
60 } 59 }
61 60
62 const std::shared_ptr<Kernel::Scheduler>& Scheduler() const { 61 Kernel::Scheduler& Scheduler() {
63 return scheduler; 62 return *scheduler;
63 }
64
65 const Kernel::Scheduler& Scheduler() const {
66 return *scheduler;
64 } 67 }
65 68
66 bool IsMainCore() const { 69 bool IsMainCore() const {
@@ -71,14 +74,14 @@ public:
71 return core_index; 74 return core_index;
72 } 75 }
73 76
74 static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores); 77 static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);
75 78
76private: 79private:
77 void Reschedule(); 80 void Reschedule();
78 81
79 std::unique_ptr<ARM_Interface> arm_interface; 82 std::unique_ptr<ARM_Interface> arm_interface;
80 std::shared_ptr<CpuBarrier> cpu_barrier; 83 CpuBarrier& cpu_barrier;
81 std::shared_ptr<Kernel::Scheduler> scheduler; 84 std::unique_ptr<Kernel::Scheduler> scheduler;
82 85
83 std::atomic<bool> reschedule_pending = false; 86 std::atomic<bool> reschedule_pending = false;
84 std::size_t core_index; 87 std::size_t core_index;
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index 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/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 5b1177a03..a012c2be9 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -17,11 +17,13 @@ const std::array<const char*, 15> LANGUAGE_NAMES = {
17}; 17};
18 18
19std::string LanguageEntry::GetApplicationName() const { 19std::string LanguageEntry::GetApplicationName() const {
20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200); 20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(),
21 application_name.size());
21} 22}
22 23
23std::string LanguageEntry::GetDeveloperName() const { 24std::string LanguageEntry::GetDeveloperName() const {
24 return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100); 25 return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(),
26 developer_name.size());
25} 27}
26 28
27NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) { 29NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
@@ -56,7 +58,12 @@ u64 NACP::GetTitleId() const {
56 return raw->title_id; 58 return raw->title_id;
57} 59}
58 60
61u64 NACP::GetDLCBaseTitleId() const {
62 return raw->dlc_base_title_id;
63}
64
59std::string NACP::GetVersionString() const { 65std::string NACP::GetVersionString() const {
60 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 0x10); 66 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
67 raw->version_string.size());
61} 68}
62} // namespace FileSys 69} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 43d6f0719..141f7e056 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -79,6 +79,7 @@ public:
79 std::string GetApplicationName(Language language = Language::Default) const; 79 std::string GetApplicationName(Language language = Language::Default) const;
80 std::string GetDeveloperName(Language language = Language::Default) const; 80 std::string GetDeveloperName(Language language = Language::Default) const;
81 u64 GetTitleId() const; 81 u64 GetTitleId() const;
82 u64 GetDLCBaseTitleId() const;
82 std::string GetVersionString() const; 83 std::string GetVersionString() const;
83 84
84private: 85private:
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 8c73a8f41..0117cb0bf 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -352,7 +352,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
352} 352}
353 353
354std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { 354std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
355 const auto& installed{Service::FileSystem::GetUnionContents()}; 355 const auto installed{Service::FileSystem::GetUnionContents()};
356 356
357 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); 357 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
358 if (base_control_nca == nullptr) 358 if (base_control_nca == nullptr)
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index e9b040689..1febb398e 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -308,14 +308,14 @@ VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
308 return GetEntryRaw(entry.title_id, entry.type); 308 return GetEntryRaw(entry.title_id, entry.type);
309} 309}
310 310
311std::shared_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const { 311std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
312 const auto raw = GetEntryRaw(title_id, type); 312 const auto raw = GetEntryRaw(title_id, type);
313 if (raw == nullptr) 313 if (raw == nullptr)
314 return nullptr; 314 return nullptr;
315 return std::make_shared<NCA>(raw); 315 return std::make_unique<NCA>(raw);
316} 316}
317 317
318std::shared_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const { 318std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
319 return GetEntry(entry.title_id, entry.type); 319 return GetEntry(entry.title_id, entry.type);
320} 320}
321 321
@@ -516,7 +516,7 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
516 }) != yuzu_meta.end(); 516 }) != yuzu_meta.end();
517} 517}
518 518
519RegisteredCacheUnion::RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches) 519RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches)
520 : caches(std::move(caches)) {} 520 : caches(std::move(caches)) {}
521 521
522void RegisteredCacheUnion::Refresh() { 522void RegisteredCacheUnion::Refresh() {
@@ -572,14 +572,14 @@ VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const
572 return GetEntryRaw(entry.title_id, entry.type); 572 return GetEntryRaw(entry.title_id, entry.type);
573} 573}
574 574
575std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const { 575std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
576 const auto raw = GetEntryRaw(title_id, type); 576 const auto raw = GetEntryRaw(title_id, type);
577 if (raw == nullptr) 577 if (raw == nullptr)
578 return nullptr; 578 return nullptr;
579 return std::make_shared<NCA>(raw); 579 return std::make_unique<NCA>(raw);
580} 580}
581 581
582std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const { 582std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
583 return GetEntry(entry.title_id, entry.type); 583 return GetEntry(entry.title_id, entry.type);
584} 584}
585 585
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index c0cd59fc5..5ddacba47 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -88,8 +88,8 @@ public:
88 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 88 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
89 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; 89 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
90 90
91 std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 91 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
92 std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; 92 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
93 93
94 std::vector<RegisteredCacheEntry> ListEntries() const; 94 std::vector<RegisteredCacheEntry> ListEntries() const;
95 // If a parameter is not boost::none, it will be filtered for from all entries. 95 // If a parameter is not boost::none, it will be filtered for from all entries.
@@ -142,7 +142,7 @@ private:
142// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface. 142// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.
143class RegisteredCacheUnion { 143class RegisteredCacheUnion {
144public: 144public:
145 explicit RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches); 145 explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches);
146 146
147 void Refresh(); 147 void Refresh();
148 148
@@ -157,8 +157,8 @@ public:
157 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 157 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
158 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; 158 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
159 159
160 std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 160 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
161 std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; 161 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
162 162
163 std::vector<RegisteredCacheEntry> ListEntries() const; 163 std::vector<RegisteredCacheEntry> ListEntries() const;
164 // If a parameter is not boost::none, it will be filtered for from all entries. 164 // If a parameter is not boost::none, it will be filtered for from all entries.
@@ -168,7 +168,7 @@ public:
168 boost::optional<u64> title_id = boost::none) const; 168 boost::optional<u64> title_id = boost::none) const;
169 169
170private: 170private:
171 std::vector<std::shared_ptr<RegisteredCache>> caches; 171 std::vector<RegisteredCache*> caches;
172}; 172};
173 173
174} // namespace FileSys 174} // namespace FileSys
diff --git a/src/core/file_sys/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/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/svc.cpp b/src/core/hle/kernel/svc.cpp
index 863ecfa74..3e5f11f2b 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -95,12 +95,12 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add
95 95
96 const VAddr dst_end_address = dst_addr + size; 96 const VAddr dst_end_address = dst_addr + size;
97 if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() && 97 if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() &&
98 dst_addr < vm_manager.GetHeapRegionEndAddress()) { 98 vm_manager.GetHeapRegionEndAddress() > dst_addr) {
99 return ERR_INVALID_MEMORY_RANGE; 99 return ERR_INVALID_MEMORY_RANGE;
100 } 100 }
101 101
102 if (dst_end_address > vm_manager.GetNewMapRegionBaseAddress() && 102 if (dst_end_address > vm_manager.GetMapRegionBaseAddress() &&
103 dst_addr < vm_manager.GetMapRegionEndAddress()) { 103 vm_manager.GetMapRegionEndAddress() > dst_addr) {
104 return ERR_INVALID_MEMORY_RANGE; 104 return ERR_INVALID_MEMORY_RANGE;
105 } 105 }
106 106
@@ -389,6 +389,12 @@ static void Break(u32 reason, u64 info1, u64 info2) {
389 "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}",
390 reason, info1, info2); 390 reason, info1, info2);
391 ASSERT(false); 391 ASSERT(false);
392
393 Core::CurrentProcess()->PrepareForTermination();
394
395 // Kill the current thread
396 GetCurrentThread()->Stop();
397 Core::System::GetInstance().PrepareReschedule();
392 } 398 }
393} 399}
394 400
@@ -803,7 +809,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
803 std::vector<SharedPtr<Thread>>& waiting_threads, 809 std::vector<SharedPtr<Thread>>& waiting_threads,
804 VAddr condvar_addr) { 810 VAddr condvar_addr) {
805 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 811 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
806 const auto& thread_list = scheduler->GetThreadList(); 812 const auto& thread_list = scheduler.GetThreadList();
807 813
808 for (const auto& thread : thread_list) { 814 for (const auto& thread : thread_list) {
809 if (thread->GetCondVarWaitAddress() == condvar_addr) 815 if (thread->GetCondVarWaitAddress() == condvar_addr)
@@ -1092,6 +1098,29 @@ static ResultCode ClearEvent(Handle handle) {
1092 return RESULT_SUCCESS; 1098 return RESULT_SUCCESS;
1093} 1099}
1094 1100
1101static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
1102 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
1103
1104 // This function currently only allows retrieving a process' status.
1105 enum class InfoType {
1106 Status,
1107 };
1108
1109 const auto& kernel = Core::System::GetInstance().Kernel();
1110 const auto process = kernel.HandleTable().Get<Process>(process_handle);
1111 if (!process) {
1112 return ERR_INVALID_HANDLE;
1113 }
1114
1115 const auto info_type = static_cast<InfoType>(type);
1116 if (info_type != InfoType::Status) {
1117 return ERR_INVALID_ENUM_VALUE;
1118 }
1119
1120 *out = static_cast<u64>(process->GetStatus());
1121 return RESULT_SUCCESS;
1122}
1123
1095namespace { 1124namespace {
1096struct FunctionDef { 1125struct FunctionDef {
1097 using Func = void(); 1126 using Func = void();
@@ -1227,7 +1256,7 @@ static const FunctionDef SVC_Table[] = {
1227 {0x79, nullptr, "CreateProcess"}, 1256 {0x79, nullptr, "CreateProcess"},
1228 {0x7A, nullptr, "StartProcess"}, 1257 {0x7A, nullptr, "StartProcess"},
1229 {0x7B, nullptr, "TerminateProcess"}, 1258 {0x7B, nullptr, "TerminateProcess"},
1230 {0x7C, nullptr, "GetProcessInfo"}, 1259 {0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"},
1231 {0x7D, nullptr, "CreateResourceLimit"}, 1260 {0x7D, nullptr, "CreateResourceLimit"},
1232 {0x7E, nullptr, "SetResourceLimitLimitValue"}, 1261 {0x7E, nullptr, "SetResourceLimitLimitValue"},
1233 {0x7F, nullptr, "CallSecureMonitor"}, 1262 {0x7F, nullptr, "CallSecureMonitor"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index cbb80c3c4..b09753c80 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -77,6 +77,14 @@ void SvcWrap() {
77 FuncReturn(retval); 77 FuncReturn(retval);
78} 78}
79 79
80template <ResultCode func(u64*, u32, u32)>
81void SvcWrap() {
82 u64 param_1 = 0;
83 u32 retval = func(&param_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw;
84 Core::CurrentArmInterface().SetReg(1, param_1);
85 FuncReturn(retval);
86}
87
80template <ResultCode func(u32, u64)> 88template <ResultCode func(u32, u64)>
81void SvcWrap() { 89void SvcWrap() {
82 FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw); 90 FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 352ce1725..35ec98c1a 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -97,7 +97,7 @@ void Thread::CancelWakeupTimer() {
97static boost::optional<s32> GetNextProcessorId(u64 mask) { 97static boost::optional<s32> GetNextProcessorId(u64 mask) {
98 for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) { 98 for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) {
99 if (mask & (1ULL << index)) { 99 if (mask & (1ULL << index)) {
100 if (!Core::System::GetInstance().Scheduler(index)->GetCurrentThread()) { 100 if (!Core::System::GetInstance().Scheduler(index).GetCurrentThread()) {
101 // Core is enabled and not running any threads, use this one 101 // Core is enabled and not running any threads, use this one
102 return index; 102 return index;
103 } 103 }
@@ -147,14 +147,14 @@ void Thread::ResumeFromWait() {
147 new_processor_id = processor_id; 147 new_processor_id = processor_id;
148 } 148 }
149 if (ideal_core != -1 && 149 if (ideal_core != -1 &&
150 Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { 150 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
151 new_processor_id = ideal_core; 151 new_processor_id = ideal_core;
152 } 152 }
153 153
154 ASSERT(*new_processor_id < 4); 154 ASSERT(*new_processor_id < 4);
155 155
156 // Add thread to new core's scheduler 156 // Add thread to new core's scheduler
157 auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id); 157 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
158 158
159 if (*new_processor_id != processor_id) { 159 if (*new_processor_id != processor_id) {
160 // Remove thread from previous core's scheduler 160 // Remove thread from previous core's scheduler
@@ -169,7 +169,7 @@ void Thread::ResumeFromWait() {
169 next_scheduler->ScheduleThread(this, current_priority); 169 next_scheduler->ScheduleThread(this, current_priority);
170 170
171 // Change thread's scheduler 171 // Change thread's scheduler
172 scheduler = next_scheduler.get(); 172 scheduler = next_scheduler;
173 173
174 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); 174 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
175} 175}
@@ -230,7 +230,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
230 thread->name = std::move(name); 230 thread->name = std::move(name);
231 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); 231 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
232 thread->owner_process = &owner_process; 232 thread->owner_process = &owner_process;
233 thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get(); 233 thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id);
234 thread->scheduler->AddThread(thread, priority); 234 thread->scheduler->AddThread(thread, priority);
235 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); 235 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
236 236
@@ -375,14 +375,14 @@ void Thread::ChangeCore(u32 core, u64 mask) {
375 new_processor_id = processor_id; 375 new_processor_id = processor_id;
376 } 376 }
377 if (ideal_core != -1 && 377 if (ideal_core != -1 &&
378 Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { 378 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
379 new_processor_id = ideal_core; 379 new_processor_id = ideal_core;
380 } 380 }
381 381
382 ASSERT(*new_processor_id < 4); 382 ASSERT(*new_processor_id < 4);
383 383
384 // Add thread to new core's scheduler 384 // Add thread to new core's scheduler
385 auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id); 385 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
386 386
387 if (*new_processor_id != processor_id) { 387 if (*new_processor_id != processor_id) {
388 // Remove thread from previous core's scheduler 388 // Remove thread from previous core's scheduler
@@ -397,7 +397,7 @@ void Thread::ChangeCore(u32 core, u64 mask) {
397 next_scheduler->ScheduleThread(this, current_priority); 397 next_scheduler->ScheduleThread(this, current_priority);
398 398
399 // Change thread's scheduler 399 // Change thread's scheduler
400 scheduler = next_scheduler.get(); 400 scheduler = next_scheduler;
401 401
402 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); 402 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
403} 403}
diff --git a/src/core/hle/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/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/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/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/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 9a59b65b3..f356f9a03 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -267,7 +267,7 @@ enum class ControlCode : u64 {
267 GTU = 12, 267 GTU = 12,
268 NEU = 13, 268 NEU = 13,
269 GEU = 14, 269 GEU = 14,
270 // 270 T = 15,
271 OFF = 16, 271 OFF = 16,
272 LO = 17, 272 LO = 17,
273 SFF = 18, 273 SFF = 18,
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 84582c777..8d5f277e2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -286,7 +286,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
286 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); 286 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
287 287
288 // Bind the buffer 288 // Bind the buffer
289 glBindBufferRange(GL_UNIFORM_BUFFER, stage, buffer_cache.GetHandle(), offset, sizeof(ubo)); 289 glBindBufferRange(GL_UNIFORM_BUFFER, static_cast<GLuint>(stage), buffer_cache.GetHandle(),
290 offset, static_cast<GLsizeiptr>(sizeof(ubo)));
290 291
291 Shader shader{shader_cache.GetStageProgram(program)}; 292 Shader shader{shader_cache.GetStageProgram(program)};
292 293
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 65a220c41..801d45144 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -231,6 +231,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
231 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI 231 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI
232 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI 232 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI
233 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 233 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8
234 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5
235 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4
234 236
235 // Depth formats 237 // Depth formats
236 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F 238 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -277,7 +279,9 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType
277static bool IsPixelFormatASTC(PixelFormat format) { 279static bool IsPixelFormatASTC(PixelFormat format) {
278 switch (format) { 280 switch (format) {
279 case PixelFormat::ASTC_2D_4X4: 281 case PixelFormat::ASTC_2D_4X4:
282 case PixelFormat::ASTC_2D_5X4:
280 case PixelFormat::ASTC_2D_8X8: 283 case PixelFormat::ASTC_2D_8X8:
284 case PixelFormat::ASTC_2D_8X5:
281 return true; 285 return true;
282 default: 286 default:
283 return false; 287 return false;
@@ -288,8 +292,12 @@ static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
288 switch (format) { 292 switch (format) {
289 case PixelFormat::ASTC_2D_4X4: 293 case PixelFormat::ASTC_2D_4X4:
290 return {4, 4}; 294 return {4, 4};
295 case PixelFormat::ASTC_2D_5X4:
296 return {5, 4};
291 case PixelFormat::ASTC_2D_8X8: 297 case PixelFormat::ASTC_2D_8X8:
292 return {8, 8}; 298 return {8, 8};
299 case PixelFormat::ASTC_2D_8X5:
300 return {8, 5};
293 default: 301 default:
294 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format)); 302 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
295 UNREACHABLE(); 303 UNREACHABLE();
@@ -323,8 +331,8 @@ static bool IsFormatBCn(PixelFormat format) {
323} 331}
324 332
325template <bool morton_to_gl, PixelFormat format> 333template <bool morton_to_gl, PixelFormat format>
326void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::size_t gl_buffer_size, 334void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer,
327 VAddr addr) { 335 std::size_t gl_buffer_size, VAddr addr) {
328 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; 336 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
329 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); 337 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
330 338
@@ -333,7 +341,7 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::si
333 // pixel values. 341 // pixel values.
334 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; 342 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
335 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( 343 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
336 addr, tile_size, bytes_per_pixel, stride, height, block_height); 344 addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth);
337 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())}; 345 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
338 memcpy(gl_buffer, data.data(), size_to_copy); 346 memcpy(gl_buffer, data.data(), size_to_copy);
339 } else { 347 } else {
@@ -345,7 +353,7 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::si
345 } 353 }
346} 354}
347 355
348static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), 356static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
349 SurfaceParams::MaxPixelFormat> 357 SurfaceParams::MaxPixelFormat>
350 morton_to_gl_fns = { 358 morton_to_gl_fns = {
351 // clang-format off 359 // clang-format off
@@ -395,6 +403,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
395 MortonCopy<true, PixelFormat::RG32UI>, 403 MortonCopy<true, PixelFormat::RG32UI>,
396 MortonCopy<true, PixelFormat::R32UI>, 404 MortonCopy<true, PixelFormat::R32UI>,
397 MortonCopy<true, PixelFormat::ASTC_2D_8X8>, 405 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
406 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
407 MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
398 MortonCopy<true, PixelFormat::Z32F>, 408 MortonCopy<true, PixelFormat::Z32F>,
399 MortonCopy<true, PixelFormat::Z16>, 409 MortonCopy<true, PixelFormat::Z16>,
400 MortonCopy<true, PixelFormat::Z24S8>, 410 MortonCopy<true, PixelFormat::Z24S8>,
@@ -403,7 +413,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
403 // clang-format on 413 // clang-format on
404}; 414};
405 415
406static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), 416static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
407 SurfaceParams::MaxPixelFormat> 417 SurfaceParams::MaxPixelFormat>
408 gl_to_morton_fns = { 418 gl_to_morton_fns = {
409 // clang-format off 419 // clang-format off
@@ -455,6 +465,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
455 MortonCopy<false, PixelFormat::RG32UI>, 465 MortonCopy<false, PixelFormat::RG32UI>,
456 MortonCopy<false, PixelFormat::R32UI>, 466 MortonCopy<false, PixelFormat::R32UI>,
457 nullptr, 467 nullptr,
468 nullptr,
469 nullptr,
458 MortonCopy<false, PixelFormat::Z32F>, 470 MortonCopy<false, PixelFormat::Z32F>,
459 MortonCopy<false, PixelFormat::Z16>, 471 MortonCopy<false, PixelFormat::Z16>,
460 MortonCopy<false, PixelFormat::Z24S8>, 472 MortonCopy<false, PixelFormat::Z24S8>,
@@ -790,7 +802,9 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
790 u32 width, u32 height) { 802 u32 width, u32 height) {
791 switch (pixel_format) { 803 switch (pixel_format) {
792 case PixelFormat::ASTC_2D_4X4: 804 case PixelFormat::ASTC_2D_4X4:
793 case PixelFormat::ASTC_2D_8X8: { 805 case PixelFormat::ASTC_2D_8X8:
806 case PixelFormat::ASTC_2D_8X5:
807 case PixelFormat::ASTC_2D_5X4: {
794 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. 808 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
795 u32 block_width{}; 809 u32 block_width{};
796 u32 block_height{}; 810 u32 block_height{};
@@ -827,36 +841,23 @@ void CachedSurface::LoadGLBuffer() {
827 841
828 if (params.is_tiled) { 842 if (params.is_tiled) {
829 gl_buffer.resize(total_size); 843 gl_buffer.resize(total_size);
844 u32 depth = params.depth;
845 u32 block_depth = params.block_depth;
830 846
831 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", 847 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
832 params.block_width, static_cast<u32>(params.target)); 848 params.block_width, static_cast<u32>(params.target));
833 ASSERT_MSG(params.block_depth == 1, "Block depth is defined as {} on texture type {}",
834 params.block_depth, static_cast<u32>(params.target));
835 849
836 // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do 850 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
837 // this for 3D textures, etc. 851 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
838 switch (params.target) { 852 depth = 1U;
839 case SurfaceParams::SurfaceTarget::Texture2D: 853 block_depth = 1U;
840 // Pass impl. to the fallback code below
841 break;
842 case SurfaceParams::SurfaceTarget::Texture2DArray:
843 case SurfaceParams::SurfaceTarget::TextureCubemap:
844 for (std::size_t index = 0; index < params.depth; ++index) {
845 const std::size_t offset{index * copy_size};
846 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
847 params.width, params.block_height, params.height, gl_buffer.data() + offset,
848 copy_size, params.addr + offset);
849 }
850 break;
851 default:
852 LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}",
853 static_cast<u32>(params.target));
854 UNREACHABLE();
855 } 854 }
856 855
856 const std::size_t size = copy_size * depth;
857
857 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( 858 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
858 params.width, params.block_height, params.height, gl_buffer.data(), copy_size, 859 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
859 params.addr); 860 size, params.addr);
860 } else { 861 } else {
861 const u8* const texture_src_data_end{texture_src_data + total_size}; 862 const u8* const texture_src_data_end{texture_src_data + total_size};
862 gl_buffer.assign(texture_src_data, texture_src_data_end); 863 gl_buffer.assign(texture_src_data, texture_src_data_end);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 66d98ad4e..0b8ae3eb4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -74,19 +74,21 @@ struct SurfaceParams {
74 RG32UI = 43, 74 RG32UI = 43,
75 R32UI = 44, 75 R32UI = 44,
76 ASTC_2D_8X8 = 45, 76 ASTC_2D_8X8 = 45,
77 ASTC_2D_8X5 = 46,
78 ASTC_2D_5X4 = 47,
77 79
78 MaxColorFormat, 80 MaxColorFormat,
79 81
80 // Depth formats 82 // Depth formats
81 Z32F = 46, 83 Z32F = 48,
82 Z16 = 47, 84 Z16 = 49,
83 85
84 MaxDepthFormat, 86 MaxDepthFormat,
85 87
86 // DepthStencil formats 88 // DepthStencil formats
87 Z24S8 = 48, 89 Z24S8 = 50,
88 S8Z24 = 49, 90 S8Z24 = 51,
89 Z32FS8 = 50, 91 Z32FS8 = 52,
90 92
91 MaxDepthStencilFormat, 93 MaxDepthStencilFormat,
92 94
@@ -220,6 +222,8 @@ struct SurfaceParams {
220 1, // RG32UI 222 1, // RG32UI
221 1, // R32UI 223 1, // R32UI
222 4, // ASTC_2D_8X8 224 4, // ASTC_2D_8X8
225 4, // ASTC_2D_8X5
226 4, // ASTC_2D_5X4
223 1, // Z32F 227 1, // Z32F
224 1, // Z16 228 1, // Z16
225 1, // Z24S8 229 1, // Z24S8
@@ -282,6 +286,8 @@ struct SurfaceParams {
282 64, // RG32UI 286 64, // RG32UI
283 32, // R32UI 287 32, // R32UI
284 16, // ASTC_2D_8X8 288 16, // ASTC_2D_8X8
289 32, // ASTC_2D_8X5
290 32, // ASTC_2D_5X4
285 32, // Z32F 291 32, // Z32F
286 16, // Z16 292 16, // Z16
287 32, // Z24S8 293 32, // Z24S8
@@ -553,8 +559,12 @@ struct SurfaceParams {
553 return PixelFormat::BC6H_SF16; 559 return PixelFormat::BC6H_SF16;
554 case Tegra::Texture::TextureFormat::ASTC_2D_4X4: 560 case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
555 return PixelFormat::ASTC_2D_4X4; 561 return PixelFormat::ASTC_2D_4X4;
562 case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
563 return PixelFormat::ASTC_2D_5X4;
556 case Tegra::Texture::TextureFormat::ASTC_2D_8X8: 564 case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
557 return PixelFormat::ASTC_2D_8X8; 565 return PixelFormat::ASTC_2D_8X8;
566 case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
567 return PixelFormat::ASTC_2D_8X5;
558 case Tegra::Texture::TextureFormat::R16_G16: 568 case Tegra::Texture::TextureFormat::R16_G16:
559 switch (component_type) { 569 switch (component_type) {
560 case Tegra::Texture::ComponentType::FLOAT: 570 case Tegra::Texture::ComponentType::FLOAT:
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 8dfb49507..ca063d90d 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1436,7 +1436,6 @@ private:
1436 1436
1437 break; 1437 break;
1438 } 1438 }
1439
1440 case OpCode::Type::Shift: { 1439 case OpCode::Type::Shift: {
1441 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); 1440 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);
1442 std::string op_b; 1441 std::string op_b;
@@ -1478,7 +1477,6 @@ private:
1478 } 1477 }
1479 break; 1478 break;
1480 } 1479 }
1481
1482 case OpCode::Type::ArithmeticIntegerImmediate: { 1480 case OpCode::Type::ArithmeticIntegerImmediate: {
1483 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); 1481 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
1484 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());
@@ -2626,14 +2624,14 @@ private:
2626 const std::string pred = 2624 const std::string pred =
2627 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); 2625 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);
2628 const std::string combiner = GetPredicateCombiner(instr.csetp.op); 2626 const std::string combiner = GetPredicateCombiner(instr.csetp.op);
2629 const std::string controlCode = regs.GetControlCode(instr.csetp.cc); 2627 const std::string control_code = regs.GetControlCode(instr.csetp.cc);
2630 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { 2628 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {
2631 SetPredicate(instr.csetp.pred3, 2629 SetPredicate(instr.csetp.pred3,
2632 '(' + controlCode + ") " + combiner + " (" + pred + ')'); 2630 '(' + control_code + ") " + combiner + " (" + pred + ')');
2633 } 2631 }
2634 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { 2632 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
2635 SetPredicate(instr.csetp.pred0, 2633 SetPredicate(instr.csetp.pred0,
2636 "!(" + controlCode + ") " + combiner + " (" + pred + ')'); 2634 "!(" + control_code + ") " + combiner + " (" + pred + ')');
2637 } 2635 }
2638 break; 2636 break;
2639 } 2637 }
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 58d17abcb..5947bd2b9 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -141,6 +141,7 @@ static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size");
141 141
142struct TICEntry { 142struct TICEntry {
143 static constexpr u32 DefaultBlockHeight = 16; 143 static constexpr u32 DefaultBlockHeight = 16;
144 static constexpr u32 DefaultBlockDepth = 1;
144 145
145 union { 146 union {
146 u32 raw; 147 u32 raw;
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index 033ea1ea4..0a8f2bd9e 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -2,96 +2,114 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <thread> 5#include <json.hpp>
6#include "common/assert.h"
7#include "common/detached_tasks.h" 6#include "common/detached_tasks.h"
7#include "common/web_result.h"
8#include "web_service/telemetry_json.h" 8#include "web_service/telemetry_json.h"
9#include "web_service/web_backend.h" 9#include "web_service/web_backend.h"
10 10
11namespace WebService { 11namespace WebService {
12 12
13TelemetryJson::TelemetryJson(const std::string& host, const std::string& username, 13struct TelemetryJson::Impl {
14 const std::string& token) 14 Impl(std::string host, std::string username, std::string token)
15 : host(std::move(host)), username(std::move(username)), token(std::move(token)) {} 15 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {}
16TelemetryJson::~TelemetryJson() = default;
17 16
18template <class T> 17 nlohmann::json& TopSection() {
19void TelemetryJson::Serialize(Telemetry::FieldType type, const std::string& name, T value) { 18 return sections[static_cast<u8>(Telemetry::FieldType::None)];
20 sections[static_cast<u8>(type)][name] = value; 19 }
21}
22 20
23void TelemetryJson::SerializeSection(Telemetry::FieldType type, const std::string& name) { 21 const nlohmann::json& TopSection() const {
24 TopSection()[name] = sections[static_cast<unsigned>(type)]; 22 return sections[static_cast<u8>(Telemetry::FieldType::None)];
25} 23 }
24
25 template <class T>
26 void Serialize(Telemetry::FieldType type, const std::string& name, T value) {
27 sections[static_cast<u8>(type)][name] = value;
28 }
29
30 void SerializeSection(Telemetry::FieldType type, const std::string& name) {
31 TopSection()[name] = sections[static_cast<unsigned>(type)];
32 }
33
34 nlohmann::json output;
35 std::array<nlohmann::json, 7> sections;
36 std::string host;
37 std::string username;
38 std::string token;
39};
40
41TelemetryJson::TelemetryJson(std::string host, std::string username, std::string token)
42 : impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {}
43TelemetryJson::~TelemetryJson() = default;
26 44
27void TelemetryJson::Visit(const Telemetry::Field<bool>& field) { 45void TelemetryJson::Visit(const Telemetry::Field<bool>& field) {
28 Serialize(field.GetType(), field.GetName(), field.GetValue()); 46 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
29} 47}
30 48
31void TelemetryJson::Visit(const Telemetry::Field<double>& field) { 49void TelemetryJson::Visit(const Telemetry::Field<double>& field) {
32 Serialize(field.GetType(), field.GetName(), field.GetValue()); 50 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
33} 51}
34 52
35void TelemetryJson::Visit(const Telemetry::Field<float>& field) { 53void TelemetryJson::Visit(const Telemetry::Field<float>& field) {
36 Serialize(field.GetType(), field.GetName(), field.GetValue()); 54 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
37} 55}
38 56
39void TelemetryJson::Visit(const Telemetry::Field<u8>& field) { 57void TelemetryJson::Visit(const Telemetry::Field<u8>& field) {
40 Serialize(field.GetType(), field.GetName(), field.GetValue()); 58 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
41} 59}
42 60
43void TelemetryJson::Visit(const Telemetry::Field<u16>& field) { 61void TelemetryJson::Visit(const Telemetry::Field<u16>& field) {
44 Serialize(field.GetType(), field.GetName(), field.GetValue()); 62 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
45} 63}
46 64
47void TelemetryJson::Visit(const Telemetry::Field<u32>& field) { 65void TelemetryJson::Visit(const Telemetry::Field<u32>& field) {
48 Serialize(field.GetType(), field.GetName(), field.GetValue()); 66 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
49} 67}
50 68
51void TelemetryJson::Visit(const Telemetry::Field<u64>& field) { 69void TelemetryJson::Visit(const Telemetry::Field<u64>& field) {
52 Serialize(field.GetType(), field.GetName(), field.GetValue()); 70 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
53} 71}
54 72
55void TelemetryJson::Visit(const Telemetry::Field<s8>& field) { 73void TelemetryJson::Visit(const Telemetry::Field<s8>& field) {
56 Serialize(field.GetType(), field.GetName(), field.GetValue()); 74 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
57} 75}
58 76
59void TelemetryJson::Visit(const Telemetry::Field<s16>& field) { 77void TelemetryJson::Visit(const Telemetry::Field<s16>& field) {
60 Serialize(field.GetType(), field.GetName(), field.GetValue()); 78 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
61} 79}
62 80
63void TelemetryJson::Visit(const Telemetry::Field<s32>& field) { 81void TelemetryJson::Visit(const Telemetry::Field<s32>& field) {
64 Serialize(field.GetType(), field.GetName(), field.GetValue()); 82 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
65} 83}
66 84
67void TelemetryJson::Visit(const Telemetry::Field<s64>& field) { 85void TelemetryJson::Visit(const Telemetry::Field<s64>& field) {
68 Serialize(field.GetType(), field.GetName(), field.GetValue()); 86 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
69} 87}
70 88
71void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) { 89void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) {
72 Serialize(field.GetType(), field.GetName(), field.GetValue()); 90 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
73} 91}
74 92
75void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) { 93void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) {
76 Serialize(field.GetType(), field.GetName(), std::string(field.GetValue())); 94 impl->Serialize(field.GetType(), field.GetName(), std::string(field.GetValue()));
77} 95}
78 96
79void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) { 97void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) {
80 Serialize(field.GetType(), field.GetName(), field.GetValue().count()); 98 impl->Serialize(field.GetType(), field.GetName(), field.GetValue().count());
81} 99}
82 100
83void TelemetryJson::Complete() { 101void TelemetryJson::Complete() {
84 SerializeSection(Telemetry::FieldType::App, "App"); 102 impl->SerializeSection(Telemetry::FieldType::App, "App");
85 SerializeSection(Telemetry::FieldType::Session, "Session"); 103 impl->SerializeSection(Telemetry::FieldType::Session, "Session");
86 SerializeSection(Telemetry::FieldType::Performance, "Performance"); 104 impl->SerializeSection(Telemetry::FieldType::Performance, "Performance");
87 SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); 105 impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
88 SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); 106 impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
89 SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); 107 impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
90 108
91 auto content = TopSection().dump(); 109 auto content = impl->TopSection().dump();
92 // Send the telemetry async but don't handle the errors since they were written to the log 110 // Send the telemetry async but don't handle the errors since they were written to the log
93 Common::DetachedTasks::AddTask( 111 Common::DetachedTasks::AddTask(
94 [host{this->host}, username{this->username}, token{this->token}, content]() { 112 [host{impl->host}, username{impl->username}, token{impl->token}, content]() {
95 Client{host, username, token}.PostJson("/telemetry", content, true); 113 Client{host, username, token}.PostJson("/telemetry", content, true);
96 }); 114 });
97} 115}
diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h
index 0fe6f9a3e..93371414a 100644
--- a/src/web_service/telemetry_json.h
+++ b/src/web_service/telemetry_json.h
@@ -4,11 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <chrono>
8#include <string> 8#include <string>
9#include <json.hpp>
10#include "common/telemetry.h" 9#include "common/telemetry.h"
11#include "common/web_result.h"
12 10
13namespace WebService { 11namespace WebService {
14 12
@@ -18,8 +16,8 @@ namespace WebService {
18 */ 16 */
19class TelemetryJson : public Telemetry::VisitorInterface { 17class TelemetryJson : public Telemetry::VisitorInterface {
20public: 18public:
21 TelemetryJson(const std::string& host, const std::string& username, const std::string& token); 19 TelemetryJson(std::string host, std::string username, std::string token);
22 ~TelemetryJson(); 20 ~TelemetryJson() override;
23 21
24 void Visit(const Telemetry::Field<bool>& field) override; 22 void Visit(const Telemetry::Field<bool>& field) override;
25 void Visit(const Telemetry::Field<double>& field) override; 23 void Visit(const Telemetry::Field<double>& field) override;
@@ -39,20 +37,8 @@ public:
39 void Complete() override; 37 void Complete() override;
40 38
41private: 39private:
42 nlohmann::json& TopSection() { 40 struct Impl;
43 return sections[static_cast<u8>(Telemetry::FieldType::None)]; 41 std::unique_ptr<Impl> impl;
44 }
45
46 template <class T>
47 void Serialize(Telemetry::FieldType type, const std::string& name, T value);
48
49 void SerializeSection(Telemetry::FieldType type, const std::string& name);
50
51 nlohmann::json output;
52 std::array<nlohmann::json, 7> sections;
53 std::string host;
54 std::string username;
55 std::string token;
56}; 42};
57 43
58} // namespace WebService 44} // namespace WebService
diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp
index 124aa3863..ca4b43b93 100644
--- a/src/web_service/verify_login.cpp
+++ b/src/web_service/verify_login.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <json.hpp> 5#include <json.hpp>
6#include "common/web_result.h"
6#include "web_service/verify_login.h" 7#include "web_service/verify_login.h"
7#include "web_service/web_backend.h" 8#include "web_service/web_backend.h"
8 9
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 787b0fbcb..b7737b615 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -3,9 +3,11 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstdlib> 5#include <cstdlib>
6#include <mutex>
6#include <string> 7#include <string>
7#include <thread>
8#include <LUrlParser.h> 8#include <LUrlParser.h>
9#include <httplib.h>
10#include "common/common_types.h"
9#include "common/logging/log.h" 11#include "common/logging/log.h"
10#include "common/web_result.h" 12#include "common/web_result.h"
11#include "core/settings.h" 13#include "core/settings.h"
@@ -20,99 +22,132 @@ constexpr u32 HTTPS_PORT = 443;
20 22
21constexpr u32 TIMEOUT_SECONDS = 30; 23constexpr u32 TIMEOUT_SECONDS = 30;
22 24
23Client::JWTCache Client::jwt_cache{}; 25struct Client::Impl {
26 Impl(std::string host, std::string username, std::string token)
27 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {
28 std::lock_guard<std::mutex> lock(jwt_cache.mutex);
29 if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
30 jwt = jwt_cache.jwt;
31 }
32 }
33
34 /// A generic function handles POST, GET and DELETE request together
35 Common::WebResult GenericJson(const std::string& method, const std::string& path,
36 const std::string& data, bool allow_anonymous) {
37 if (jwt.empty()) {
38 UpdateJWT();
39 }
40
41 if (jwt.empty() && !allow_anonymous) {
42 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
43 return Common::WebResult{Common::WebResult::Code::CredentialsMissing,
44 "Credentials needed"};
45 }
46
47 auto result = GenericJson(method, path, data, jwt);
48 if (result.result_string == "401") {
49 // Try again with new JWT
50 UpdateJWT();
51 result = GenericJson(method, path, data, jwt);
52 }
24 53
25Client::Client(const std::string& host, const std::string& username, const std::string& token) 54 return result;
26 : host(host), username(username), token(token) {
27 std::lock_guard<std::mutex> lock(jwt_cache.mutex);
28 if (username == jwt_cache.username && token == jwt_cache.token) {
29 jwt = jwt_cache.jwt;
30 } 55 }
31}
32 56
33Common::WebResult Client::GenericJson(const std::string& method, const std::string& path, 57 /**
34 const std::string& data, const std::string& jwt, 58 * A generic function with explicit authentication method specified
35 const std::string& username, const std::string& token) { 59 * JWT is used if the jwt parameter is not empty
36 if (cli == nullptr) { 60 * username + token is used if jwt is empty but username and token are not empty
37 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); 61 * anonymous if all of jwt, username and token are empty
38 int port; 62 */
39 if (parsedUrl.m_Scheme == "http") { 63 Common::WebResult GenericJson(const std::string& method, const std::string& path,
40 if (!parsedUrl.GetPort(&port)) { 64 const std::string& data, const std::string& jwt = "",
41 port = HTTP_PORT; 65 const std::string& username = "", const std::string& token = "") {
42 } 66 if (cli == nullptr) {
43 cli = 67 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
44 std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port, TIMEOUT_SECONDS); 68 int port;
45 } else if (parsedUrl.m_Scheme == "https") { 69 if (parsedUrl.m_Scheme == "http") {
46 if (!parsedUrl.GetPort(&port)) { 70 if (!parsedUrl.GetPort(&port)) {
47 port = HTTPS_PORT; 71 port = HTTP_PORT;
72 }
73 cli = std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port,
74 TIMEOUT_SECONDS);
75 } else if (parsedUrl.m_Scheme == "https") {
76 if (!parsedUrl.GetPort(&port)) {
77 port = HTTPS_PORT;
78 }
79 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port,
80 TIMEOUT_SECONDS);
81 } else {
82 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
83 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};
48 } 84 }
49 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port,
50 TIMEOUT_SECONDS);
51 } else {
52 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
53 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};
54 } 85 }
55 } 86 if (cli == nullptr) {
56 if (cli == nullptr) { 87 LOG_ERROR(WebService, "Invalid URL {}", host + path);
57 LOG_ERROR(WebService, "Invalid URL {}", host + path); 88 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"};
58 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"}; 89 }
59 }
60 90
61 httplib::Headers params; 91 httplib::Headers params;
62 if (!jwt.empty()) { 92 if (!jwt.empty()) {
63 params = { 93 params = {
64 {std::string("Authorization"), fmt::format("Bearer {}", jwt)}, 94 {std::string("Authorization"), fmt::format("Bearer {}", jwt)},
65 }; 95 };
66 } else if (!username.empty()) { 96 } else if (!username.empty()) {
67 params = { 97 params = {
68 {std::string("x-username"), username}, 98 {std::string("x-username"), username},
69 {std::string("x-token"), token}, 99 {std::string("x-token"), token},
100 };
101 }
102
103 params.emplace(std::string("api-version"),
104 std::string(API_VERSION.begin(), API_VERSION.end()));
105 if (method != "GET") {
106 params.emplace(std::string("Content-Type"), std::string("application/json"));
70 }; 107 };
71 }
72 108
73 params.emplace(std::string("api-version"), std::string(API_VERSION.begin(), API_VERSION.end())); 109 httplib::Request request;
74 if (method != "GET") { 110 request.method = method;
75 params.emplace(std::string("Content-Type"), std::string("application/json")); 111 request.path = path;
76 }; 112 request.headers = params;
113 request.body = data;
77 114
78 httplib::Request request; 115 httplib::Response response;
79 request.method = method;
80 request.path = path;
81 request.headers = params;
82 request.body = data;
83 116
84 httplib::Response response; 117 if (!cli->send(request, response)) {
118 LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
119 return Common::WebResult{Common::WebResult::Code::LibError, "Null response"};
120 }
85 121
86 if (!cli->send(request, response)) { 122 if (response.status >= 400) {
87 LOG_ERROR(WebService, "{} to {} returned null", method, host + path); 123 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
88 return Common::WebResult{Common::WebResult::Code::LibError, "Null response"}; 124 response.status);
89 } 125 return Common::WebResult{Common::WebResult::Code::HttpError,
126 std::to_string(response.status)};
127 }
90 128
91 if (response.status >= 400) { 129 auto content_type = response.headers.find("content-type");
92 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
93 response.status);
94 return Common::WebResult{Common::WebResult::Code::HttpError,
95 std::to_string(response.status)};
96 }
97 130
98 auto content_type = response.headers.find("content-type"); 131 if (content_type == response.headers.end()) {
132 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path);
133 return Common::WebResult{Common::WebResult::Code::WrongContent, ""};
134 }
99 135
100 if (content_type == response.headers.end()) { 136 if (content_type->second.find("application/json") == std::string::npos &&
101 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path); 137 content_type->second.find("text/html; charset=utf-8") == std::string::npos) {
102 return Common::WebResult{Common::WebResult::Code::WrongContent, ""}; 138 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path,
139 content_type->second);
140 return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"};
141 }
142 return Common::WebResult{Common::WebResult::Code::Success, "", response.body};
103 } 143 }
104 144
105 if (content_type->second.find("application/json") == std::string::npos && 145 // Retrieve a new JWT from given username and token
106 content_type->second.find("text/html; charset=utf-8") == std::string::npos) { 146 void UpdateJWT() {
107 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path, 147 if (username.empty() || token.empty()) {
108 content_type->second); 148 return;
109 return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"}; 149 }
110 }
111 return Common::WebResult{Common::WebResult::Code::Success, "", response.body};
112}
113 150
114void Client::UpdateJWT() {
115 if (!username.empty() && !token.empty()) {
116 auto result = GenericJson("POST", "/jwt/internal", "", "", username, token); 151 auto result = GenericJson("POST", "/jwt/internal", "", "", username, token);
117 if (result.result_code != Common::WebResult::Code::Success) { 152 if (result.result_code != Common::WebResult::Code::Success) {
118 LOG_ERROR(WebService, "UpdateJWT failed"); 153 LOG_ERROR(WebService, "UpdateJWT failed");
@@ -123,27 +158,39 @@ void Client::UpdateJWT() {
123 jwt_cache.jwt = jwt = result.returned_data; 158 jwt_cache.jwt = jwt = result.returned_data;
124 } 159 }
125 } 160 }
126}
127 161
128Common::WebResult Client::GenericJson(const std::string& method, const std::string& path, 162 std::string host;
129 const std::string& data, bool allow_anonymous) { 163 std::string username;
130 if (jwt.empty()) { 164 std::string token;
131 UpdateJWT(); 165 std::string jwt;
132 } 166 std::unique_ptr<httplib::Client> cli;
167
168 struct JWTCache {
169 std::mutex mutex;
170 std::string username;
171 std::string token;
172 std::string jwt;
173 };
174 static inline JWTCache jwt_cache;
175};
133 176
134 if (jwt.empty() && !allow_anonymous) { 177Client::Client(std::string host, std::string username, std::string token)
135 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); 178 : impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {}
136 return Common::WebResult{Common::WebResult::Code::CredentialsMissing, "Credentials needed"};
137 }
138 179
139 auto result = GenericJson(method, path, data, jwt); 180Client::~Client() = default;
140 if (result.result_string == "401") { 181
141 // Try again with new JWT 182Common::WebResult Client::PostJson(const std::string& path, const std::string& data,
142 UpdateJWT(); 183 bool allow_anonymous) {
143 result = GenericJson(method, path, data, jwt); 184 return impl->GenericJson("POST", path, data, allow_anonymous);
144 } 185}
186
187Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
188 return impl->GenericJson("GET", path, "", allow_anonymous);
189}
145 190
146 return result; 191Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data,
192 bool allow_anonymous) {
193 return impl->GenericJson("DELETE", path, data, allow_anonymous);
147} 194}
148 195
149} // namespace WebService 196} // namespace WebService
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h
index d75fbcc15..c637e09df 100644
--- a/src/web_service/web_backend.h
+++ b/src/web_service/web_backend.h
@@ -4,23 +4,19 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional> 7#include <memory>
8#include <mutex>
9#include <string> 8#include <string>
10#include <tuple>
11#include <httplib.h>
12#include "common/common_types.h"
13#include "common/web_result.h"
14 9
15namespace httplib { 10namespace Common {
16class Client; 11struct WebResult;
17} 12}
18 13
19namespace WebService { 14namespace WebService {
20 15
21class Client { 16class Client {
22public: 17public:
23 Client(const std::string& host, const std::string& username, const std::string& token); 18 Client(std::string host, std::string username, std::string token);
19 ~Client();
24 20
25 /** 21 /**
26 * Posts JSON to the specified path. 22 * Posts JSON to the specified path.
@@ -30,9 +26,7 @@ public:
30 * @return the result of the request. 26 * @return the result of the request.
31 */ 27 */
32 Common::WebResult PostJson(const std::string& path, const std::string& data, 28 Common::WebResult PostJson(const std::string& path, const std::string& data,
33 bool allow_anonymous) { 29 bool allow_anonymous);
34 return GenericJson("POST", path, data, allow_anonymous);
35 }
36 30
37 /** 31 /**
38 * Gets JSON from the specified path. 32 * Gets JSON from the specified path.
@@ -40,9 +34,7 @@ public:
40 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 34 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
41 * @return the result of the request. 35 * @return the result of the request.
42 */ 36 */
43 Common::WebResult GetJson(const std::string& path, bool allow_anonymous) { 37 Common::WebResult GetJson(const std::string& path, bool allow_anonymous);
44 return GenericJson("GET", path, "", allow_anonymous);
45 }
46 38
47 /** 39 /**
48 * Deletes JSON to the specified path. 40 * Deletes JSON to the specified path.
@@ -52,41 +44,11 @@ public:
52 * @return the result of the request. 44 * @return the result of the request.
53 */ 45 */
54 Common::WebResult DeleteJson(const std::string& path, const std::string& data, 46 Common::WebResult DeleteJson(const std::string& path, const std::string& data,
55 bool allow_anonymous) { 47 bool allow_anonymous);
56 return GenericJson("DELETE", path, data, allow_anonymous);
57 }
58 48
59private: 49private:
60 /// A generic function handles POST, GET and DELETE request together 50 struct Impl;
61 Common::WebResult GenericJson(const std::string& method, const std::string& path, 51 std::unique_ptr<Impl> impl;
62 const std::string& data, bool allow_anonymous);
63
64 /**
65 * A generic function with explicit authentication method specified
66 * JWT is used if the jwt parameter is not empty
67 * username + token is used if jwt is empty but username and token are not empty
68 * anonymous if all of jwt, username and token are empty
69 */
70 Common::WebResult GenericJson(const std::string& method, const std::string& path,
71 const std::string& data, const std::string& jwt = "",
72 const std::string& username = "", const std::string& token = "");
73
74 // Retrieve a new JWT from given username and token
75 void UpdateJWT();
76
77 std::string host;
78 std::string username;
79 std::string token;
80 std::string jwt;
81 std::unique_ptr<httplib::Client> cli;
82
83 struct JWTCache {
84 std::mutex mutex;
85 std::string username;
86 std::string token;
87 std::string jwt;
88 };
89 static JWTCache jwt_cache;
90}; 52};
91 53
92} // namespace WebService 54} // namespace WebService
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index cbcd5dd5f..44d423da2 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -386,8 +386,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
386 386
387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. 387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. 388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
389 auto unswizzled_data = Tegra::Texture::UnswizzleTexture( 389 auto unswizzled_data =
390 *address, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height); 390 Tegra::Texture::UnswizzleTexture(*address, 1, Tegra::Texture::BytesPerPixel(surface_format),
391 surface_width, surface_height, 1U);
391 392
392 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, 393 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
393 surface_width, surface_height); 394 surface_width, surface_height);
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 4a09da685..7403e9ccd 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -66,10 +66,11 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList()
66 } 66 }
67 }; 67 };
68 68
69 add_threads(Core::System::GetInstance().Scheduler(0)->GetThreadList()); 69 const auto& system = Core::System::GetInstance();
70 add_threads(Core::System::GetInstance().Scheduler(1)->GetThreadList()); 70 add_threads(system.Scheduler(0).GetThreadList());
71 add_threads(Core::System::GetInstance().Scheduler(2)->GetThreadList()); 71 add_threads(system.Scheduler(1).GetThreadList());
72 add_threads(Core::System::GetInstance().Scheduler(3)->GetThreadList()); 72 add_threads(system.Scheduler(2).GetThreadList());
73 add_threads(system.Scheduler(3).GetThreadList());
73 74
74 return item_list; 75 return item_list;
75} 76}
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 8f99a1c78..3881aba5f 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -96,7 +96,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
96 FileSys::ContentRecordType::Program); 96 FileSys::ContentRecordType::Program);
97 97
98 for (const auto& game : installed_games) { 98 for (const auto& game : installed_games) {
99 const auto& file = cache->GetEntryUnparsed(game); 99 const auto file = cache->GetEntryUnparsed(game);
100 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); 100 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
101 if (!loader) 101 if (!loader)
102 continue; 102 continue;
@@ -107,7 +107,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
107 loader->ReadProgramId(program_id); 107 loader->ReadProgramId(program_id);
108 108
109 const FileSys::PatchManager patch{program_id}; 109 const FileSys::PatchManager patch{program_id};
110 const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); 110 const auto control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
111 if (control != nullptr) 111 if (control != nullptr)
112 GetMetadataFromControlNCA(patch, *control, icon, name); 112 GetMetadataFromControlNCA(patch, *control, icon, name);
113 113
@@ -135,9 +135,10 @@ void GameListWorker::AddInstalledTitlesToGameList() {
135 FileSys::ContentRecordType::Control); 135 FileSys::ContentRecordType::Control);
136 136
137 for (const auto& entry : control_data) { 137 for (const auto& entry : control_data) {
138 const auto nca = cache->GetEntry(entry); 138 auto nca = cache->GetEntry(entry);
139 if (nca != nullptr) 139 if (nca != nullptr) {
140 nca_control_map.insert_or_assign(entry.title_id, nca); 140 nca_control_map.insert_or_assign(entry.title_id, std::move(nca));
141 }
141 } 142 }
142} 143}
143 144
@@ -153,9 +154,11 @@ void GameListWorker::FillControlMap(const std::string& dir_path) {
153 QFileInfo file_info(physical_name.c_str()); 154 QFileInfo file_info(physical_name.c_str());
154 if (!is_dir && file_info.suffix().toStdString() == "nca") { 155 if (!is_dir && file_info.suffix().toStdString() == "nca") {
155 auto nca = 156 auto nca =
156 std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); 157 std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
157 if (nca->GetType() == FileSys::NCAContentType::Control) 158 if (nca->GetType() == FileSys::NCAContentType::Control) {
158 nca_control_map.insert_or_assign(nca->GetTitleId(), nca); 159 const u64 title_id = nca->GetTitleId();
160 nca_control_map.insert_or_assign(title_id, std::move(nca));
161 }
159 } 162 }
160 return true; 163 return true;
161 }; 164 };
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h
index 09d20c42f..0e42d0bde 100644
--- a/src/yuzu/game_list_worker.h
+++ b/src/yuzu/game_list_worker.h
@@ -63,7 +63,7 @@ private:
63 void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); 63 void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);
64 64
65 std::shared_ptr<FileSys::VfsFilesystem> vfs; 65 std::shared_ptr<FileSys::VfsFilesystem> vfs;
66 std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map; 66 std::map<u64, std::unique_ptr<FileSys::NCA>> nca_control_map;
67 QStringList watch_list; 67 QStringList watch_list;
68 QString dir_path; 68 QString dir_path;
69 bool deep_scan; 69 bool deep_scan;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 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/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