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.txt4
-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.cpp39
-rw-r--r--src/core/crypto/key_manager.h2
-rw-r--r--src/core/crypto/partition_data_manager.cpp80
-rw-r--r--src/core/crypto/partition_data_manager.h18
-rw-r--r--src/core/file_sys/bis_factory.cpp12
-rw-r--r--src/core/file_sys/bis_factory.h8
-rw-r--r--src/core/file_sys/content_archive.cpp2
-rw-r--r--src/core/file_sys/control_metadata.cpp13
-rw-r--r--src/core/file_sys/control_metadata.h1
-rw-r--r--src/core/file_sys/patch_manager.cpp12
-rw-r--r--src/core/file_sys/registered_cache.cpp14
-rw-r--r--src/core/file_sys/registered_cache.h12
-rw-r--r--src/core/file_sys/savedata_factory.cpp13
-rw-r--r--src/core/file_sys/sdmc_factory.cpp8
-rw-r--r--src/core/file_sys/sdmc_factory.h4
-rw-r--r--src/core/gdbstub/gdbstub.cpp6
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp2
-rw-r--r--src/core/hle/kernel/process.cpp10
-rw-r--r--src/core/hle/kernel/process.h21
-rw-r--r--src/core/hle/kernel/svc.cpp33
-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/loader.cpp3
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/nro.cpp10
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp23
-rw-r--r--src/core/loader/nso.h6
-rw-r--r--src/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.cpp193
-rw-r--r--src/video_core/textures/decoders.h15
-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.cpp44
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
66 files changed, 860 insertions, 575 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 4fddaafd1..78986deb5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -400,8 +400,8 @@ create_target_directory_groups(core)
400target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 400target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
401target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives) 401target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives)
402if (ENABLE_WEB_SERVICE) 402if (ENABLE_WEB_SERVICE)
403 add_definitions(-DENABLE_WEB_SERVICE) 403 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
404 target_link_libraries(core PUBLIC json-headers web_service) 404 target_link_libraries(core PRIVATE web_service)
405endif() 405endif()
406 406
407if (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 a59a7e1f5..fd0786068 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -98,7 +98,7 @@ std::array<u8, 144> DecryptKeyblob(const std::array<u8, 176>& encrypted_keyblob,
98 return keyblob; 98 return keyblob;
99} 99}
100 100
101void KeyManager::DeriveGeneralPurposeKeys(u8 crypto_revision) { 101void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
102 const auto kek_generation_source = 102 const auto kek_generation_source =
103 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)); 103 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
104 const auto key_generation_source = 104 const auto key_generation_source =
@@ -147,31 +147,38 @@ boost::optional<Key128> DeriveSDSeed() {
147 "rb+"); 147 "rb+");
148 if (!save_43.IsOpen()) 148 if (!save_43.IsOpen())
149 return boost::none; 149 return boost::none;
150
150 const FileUtil::IOFile sd_private( 151 const FileUtil::IOFile sd_private(
151 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); 152 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
152 if (!sd_private.IsOpen()) 153 if (!sd_private.IsOpen())
153 return boost::none; 154 return boost::none;
154 155
155 sd_private.Seek(0, SEEK_SET);
156 std::array<u8, 0x10> private_seed{}; 156 std::array<u8, 0x10> private_seed{};
157 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != 0x10) 157 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
158 return boost::none; 158 return boost::none;
159 }
159 160
160 std::array<u8, 0x10> buffer{}; 161 std::array<u8, 0x10> buffer{};
161 std::size_t offset = 0; 162 std::size_t offset = 0;
162 for (; offset + 0x10 < save_43.GetSize(); ++offset) { 163 for (; offset + 0x10 < save_43.GetSize(); ++offset) {
163 save_43.Seek(offset, SEEK_SET); 164 if (!save_43.Seek(offset, SEEK_SET)) {
165 return boost::none;
166 }
167
164 save_43.ReadBytes(buffer.data(), buffer.size()); 168 save_43.ReadBytes(buffer.data(), buffer.size());
165 if (buffer == private_seed) 169 if (buffer == private_seed) {
166 break; 170 break;
171 }
167 } 172 }
168 173
169 if (offset + 0x10 >= save_43.GetSize()) 174 if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
170 return boost::none; 175 return boost::none;
176 }
171 177
172 Key128 seed{}; 178 Key128 seed{};
173 save_43.Seek(offset + 0x10, SEEK_SET); 179 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
174 save_43.ReadBytes(seed.data(), seed.size()); 180 return boost::none;
181 }
175 return seed; 182 return seed;
176} 183}
177 184
@@ -234,7 +241,9 @@ std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
234 return {}; 241 return {};
235 242
236 std::vector<u8> buffer(ticket_save.GetSize()); 243 std::vector<u8> buffer(ticket_save.GetSize());
237 ticket_save.ReadBytes(buffer.data(), buffer.size()); 244 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
245 return {};
246 }
238 247
239 std::vector<TicketRaw> out; 248 std::vector<TicketRaw> out;
240 u32 magic{}; 249 u32 magic{};
@@ -261,6 +270,9 @@ static std::array<u8, size> operator^(const std::array<u8, size>& lhs,
261 270
262template <size_t target_size, size_t in_size> 271template <size_t target_size, size_t in_size>
263static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) { 272static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
273 // Avoids truncation overflow within the loop below.
274 static_assert(target_size <= 0xFF);
275
264 std::array<u8, in_size + 4> seed_exp{}; 276 std::array<u8, in_size + 4> seed_exp{};
265 std::memcpy(seed_exp.data(), seed.data(), in_size); 277 std::memcpy(seed_exp.data(), seed.data(), in_size);
266 278
@@ -268,7 +280,7 @@ static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
268 size_t i = 0; 280 size_t i = 0;
269 while (out.size() < target_size) { 281 while (out.size() < target_size) {
270 out.resize(out.size() + 0x20); 282 out.resize(out.size() + 0x20);
271 seed_exp[in_size + 3] = i; 283 seed_exp[in_size + 3] = static_cast<u8>(i);
272 mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0); 284 mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
273 ++i; 285 ++i;
274 } 286 }
@@ -299,10 +311,11 @@ boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
299 std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority)); 311 std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
300 if (cert_authority == 0) 312 if (cert_authority == 0)
301 return boost::none; 313 return boost::none;
302 if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) 314 if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {
303 LOG_INFO(Crypto, 315 LOG_INFO(Crypto,
304 "Attempting to parse ticket with non-standard certificate authority {:08X}.", 316 "Attempting to parse ticket with non-standard certificate authority {:08X}.",
305 cert_authority); 317 cert_authority);
318 }
306 319
307 Key128 rights_id; 320 Key128 rights_id;
308 std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128)); 321 std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
@@ -871,9 +884,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
871 "/system/save/80000000000000e2", 884 "/system/save/80000000000000e2",
872 "rb+"); 885 "rb+");
873 886
887 const auto blob2 = GetTicketblob(save2);
874 auto res = GetTicketblob(save1); 888 auto res = GetTicketblob(save1);
875 const auto res2 = GetTicketblob(save2); 889 res.insert(res.end(), blob2.begin(), blob2.end());
876 std::copy(res2.begin(), res2.end(), std::back_inserter(res));
877 890
878 for (const auto& raw : res) { 891 for (const auto& raw : res) {
879 const auto pair = ParseTicket(raw, rsa_key); 892 const auto pair = ParseTicket(raw, rsa_key);
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index a41abbdfc..cccb3c0ae 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -175,7 +175,7 @@ private:
175 void WriteKeyToFile(KeyCategory category, std::string_view keyname, 175 void WriteKeyToFile(KeyCategory category, std::string_view keyname,
176 const std::array<u8, Size>& key); 176 const std::array<u8, Size>& key);
177 177
178 void DeriveGeneralPurposeKeys(u8 crypto_revision); 178 void DeriveGeneralPurposeKeys(std::size_t crypto_revision);
179 179
180 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); 180 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
181 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); 181 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index d1c04e98d..25cee1f3a 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -11,7 +11,6 @@
11#include <array> 11#include <array>
12#include <cctype> 12#include <cctype>
13#include <cstring> 13#include <cstring>
14#include <boost/optional/optional.hpp>
15#include <mbedtls/sha256.h> 14#include <mbedtls/sha256.h>
16#include "common/assert.h" 15#include "common/assert.h"
17#include "common/common_funcs.h" 16#include "common/common_funcs.h"
@@ -19,7 +18,7 @@
19#include "common/hex_util.h" 18#include "common/hex_util.h"
20#include "common/logging/log.h" 19#include "common/logging/log.h"
21#include "common/string_util.h" 20#include "common/string_util.h"
22#include "core/crypto/ctr_encryption_layer.h" 21#include "common/swap.h"
23#include "core/crypto/key_manager.h" 22#include "core/crypto/key_manager.h"
24#include "core/crypto/partition_data_manager.h" 23#include "core/crypto/partition_data_manager.h"
25#include "core/crypto/xts_encryption_layer.h" 24#include "core/crypto/xts_encryption_layer.h"
@@ -302,10 +301,10 @@ FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
302 return nullptr; 301 return nullptr;
303} 302}
304 303
305PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir) 304PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_dir)
306 : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")), 305 : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")),
307 fuses(FindFileInDirWithNames(sysdata_dir, "fuse")), 306 fuses(FindFileInDirWithNames(sysdata_dir, "fuses")),
308 kfuses(FindFileInDirWithNames(sysdata_dir, "kfuse")), 307 kfuses(FindFileInDirWithNames(sysdata_dir, "kfuses")),
309 package2({ 308 package2({
310 FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"), 309 FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"),
311 FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"), 310 FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"),
@@ -314,13 +313,14 @@ PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir)
314 FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"), 313 FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"),
315 FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"), 314 FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"),
316 }), 315 }),
316 prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")),
317 secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")), 317 secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")),
318 package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")), 318 package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")),
319 secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{} 319 secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{}
320 : secure_monitor->ReadAllBytes()), 320 : secure_monitor->ReadAllBytes()),
321 package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{} 321 package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{}
322 : package1_decrypted->ReadAllBytes()), 322 : package1_decrypted->ReadAllBytes()) {
323 prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")) {} 323}
324 324
325PartitionDataManager::~PartitionDataManager() = default; 325PartitionDataManager::~PartitionDataManager() = default;
326 326
@@ -332,18 +332,19 @@ FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const {
332 return boot0; 332 return boot0;
333} 333}
334 334
335std::array<u8, 176> PartitionDataManager::GetEncryptedKeyblob(u8 index) const { 335PartitionDataManager::EncryptedKeyBlob PartitionDataManager::GetEncryptedKeyblob(
336 if (HasBoot0() && index < 32) 336 std::size_t index) const {
337 if (HasBoot0() && index < NUM_ENCRYPTED_KEYBLOBS)
337 return GetEncryptedKeyblobs()[index]; 338 return GetEncryptedKeyblobs()[index];
338 return {}; 339 return {};
339} 340}
340 341
341std::array<std::array<u8, 176>, 32> PartitionDataManager::GetEncryptedKeyblobs() const { 342PartitionDataManager::EncryptedKeyBlobs PartitionDataManager::GetEncryptedKeyblobs() const {
342 if (!HasBoot0()) 343 if (!HasBoot0())
343 return {}; 344 return {};
344 345
345 std::array<std::array<u8, 176>, 32> out{}; 346 EncryptedKeyBlobs out{};
346 for (size_t i = 0; i < 0x20; ++i) 347 for (size_t i = 0; i < out.size(); ++i)
347 boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200); 348 boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200);
348 return out; 349 return out;
349} 350}
@@ -389,7 +390,7 @@ std::array<u8, 16> PartitionDataManager::GetKeyblobMACKeySource() const {
389 return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]); 390 return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]);
390} 391}
391 392
392std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(u8 revision) const { 393std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(std::size_t revision) const {
393 if (keyblob_source_hashes[revision] == SHA256Hash{}) { 394 if (keyblob_source_hashes[revision] == SHA256Hash{}) {
394 LOG_WARNING(Crypto, 395 LOG_WARNING(Crypto,
395 "No keyblob source hash for crypto revision {:02X}! Cannot derive keys...", 396 "No keyblob source hash for crypto revision {:02X}! Cannot derive keys...",
@@ -446,7 +447,7 @@ bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
446 return false; 447 return false;
447} 448}
448 449
449void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2_keys, 450void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& package2_keys,
450 Package2Type type) { 451 Package2Type type) {
451 FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>( 452 FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>(
452 package2[static_cast<size_t>(type)], 453 package2[static_cast<size_t>(type)],
@@ -456,43 +457,38 @@ void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20>
456 if (file->ReadObject(&header) != sizeof(Package2Header)) 457 if (file->ReadObject(&header) != sizeof(Package2Header))
457 return; 458 return;
458 459
459 u8 revision = 0xFF; 460 std::size_t revision = 0xFF;
460 if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) { 461 if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) {
461 for (size_t i = 0; i < package2_keys.size(); ++i) { 462 for (std::size_t i = 0; i < package2_keys.size(); ++i) {
462 if (AttemptDecrypt(package2_keys[i], header)) 463 if (AttemptDecrypt(package2_keys[i], header)) {
463 revision = i; 464 revision = i;
465 }
464 } 466 }
465 } 467 }
466 468
467 if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) 469 if (header.magic != Common::MakeMagic('P', 'K', '2', '1'))
468 return; 470 return;
469 471
470 const std::vector<u8> s1_iv(header.section_ctr[1].begin(), header.section_ctr[1].end());
471
472 const auto a = std::make_shared<FileSys::OffsetVfsFile>( 472 const auto a = std::make_shared<FileSys::OffsetVfsFile>(
473 file, header.section_size[1], header.section_size[0] + sizeof(Package2Header)); 473 file, header.section_size[1], header.section_size[0] + sizeof(Package2Header));
474 474
475 auto c = a->ReadAllBytes(); 475 auto c = a->ReadAllBytes();
476 476
477 AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR); 477 AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR);
478 cipher.SetIV(s1_iv); 478 cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
479 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); 479 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
480 480
481 // package2_decrypted[static_cast<size_t>(type)] = s1;
482
483 INIHeader ini; 481 INIHeader ini;
484 std::memcpy(&ini, c.data(), sizeof(INIHeader)); 482 std::memcpy(&ini, c.data(), sizeof(INIHeader));
485 if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1')) 483 if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
486 return; 484 return;
487 485
488 std::map<u64, KIPHeader> kips{};
489 u64 offset = sizeof(INIHeader); 486 u64 offset = sizeof(INIHeader);
490 for (size_t i = 0; i < ini.process_count; ++i) { 487 for (size_t i = 0; i < ini.process_count; ++i) {
491 KIPHeader kip; 488 KIPHeader kip;
492 std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader)); 489 std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
493 if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1')) 490 if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
494 return; 491 return;
495 kips.emplace(offset, kip);
496 492
497 const auto name = 493 const auto name =
498 Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size()); 494 Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
@@ -503,33 +499,29 @@ void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20>
503 continue; 499 continue;
504 } 500 }
505 501
506 std::vector<u8> text(kip.sections[0].size_compressed); 502 const u64 initial_offset = sizeof(KIPHeader) + offset;
507 std::vector<u8> rodata(kip.sections[1].size_compressed); 503 const auto text_begin = c.cbegin() + initial_offset;
508 std::vector<u8> data(kip.sections[2].size_compressed); 504 const auto text_end = text_begin + kip.sections[0].size_compressed;
505 const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
509 506
510 u64 offset_sec = sizeof(KIPHeader) + offset; 507 const auto rodata_end = text_end + kip.sections[1].size_compressed;
511 std::memcpy(text.data(), c.data() + offset_sec, text.size()); 508 const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
512 offset_sec += text.size();
513 std::memcpy(rodata.data(), c.data() + offset_sec, rodata.size());
514 offset_sec += rodata.size();
515 std::memcpy(data.data(), c.data() + offset_sec, data.size());
516 509
517 offset += sizeof(KIPHeader) + kip.sections[0].size_compressed + 510 const auto data_end = rodata_end + kip.sections[2].size_compressed;
518 kip.sections[1].size_compressed + kip.sections[2].size_compressed; 511 const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
519 512
520 text = DecompressBLZ(text); 513 std::vector<u8> out;
521 rodata = DecompressBLZ(rodata); 514 out.reserve(text.size() + rodata.size() + data.size());
522 data = DecompressBLZ(data); 515 out.insert(out.end(), text.begin(), text.end());
516 out.insert(out.end(), rodata.begin(), rodata.end());
517 out.insert(out.end(), data.begin(), data.end());
523 518
524 std::vector<u8> out(text.size() + rodata.size() + data.size()); 519 offset += sizeof(KIPHeader) + out.size();
525 std::memcpy(out.data(), text.data(), text.size());
526 std::memcpy(out.data() + text.size(), rodata.data(), rodata.size());
527 std::memcpy(out.data() + text.size() + rodata.size(), data.data(), data.size());
528 520
529 if (name == "FS") 521 if (name == "FS")
530 package2_fs[static_cast<size_t>(type)] = out; 522 package2_fs[static_cast<size_t>(type)] = std::move(out);
531 else if (name == "spl") 523 else if (name == "spl")
532 package2_spl[static_cast<size_t>(type)] = out; 524 package2_spl[static_cast<size_t>(type)] = std::move(out);
533 } 525 }
534} 526}
535 527
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h
index 45c7fecfa..0ad007c72 100644
--- a/src/core/crypto/partition_data_manager.h
+++ b/src/core/crypto/partition_data_manager.h
@@ -5,9 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <vector> 7#include <vector>
8#include "common/common_funcs.h"
9#include "common/common_types.h" 8#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/file_sys/vfs_types.h" 9#include "core/file_sys/vfs_types.h"
12 10
13namespace Core::Crypto { 11namespace Core::Crypto {
@@ -24,15 +22,20 @@ enum class Package2Type {
24class PartitionDataManager { 22class PartitionDataManager {
25public: 23public:
26 static const u8 MAX_KEYBLOB_SOURCE_HASH; 24 static const u8 MAX_KEYBLOB_SOURCE_HASH;
25 static constexpr std::size_t NUM_ENCRYPTED_KEYBLOBS = 32;
26 static constexpr std::size_t ENCRYPTED_KEYBLOB_SIZE = 0xB0;
27 27
28 explicit PartitionDataManager(FileSys::VirtualDir sysdata_dir); 28 using EncryptedKeyBlob = std::array<u8, ENCRYPTED_KEYBLOB_SIZE>;
29 using EncryptedKeyBlobs = std::array<EncryptedKeyBlob, NUM_ENCRYPTED_KEYBLOBS>;
30
31 explicit PartitionDataManager(const FileSys::VirtualDir& sysdata_dir);
29 ~PartitionDataManager(); 32 ~PartitionDataManager();
30 33
31 // BOOT0 34 // BOOT0
32 bool HasBoot0() const; 35 bool HasBoot0() const;
33 FileSys::VirtualFile GetBoot0Raw() const; 36 FileSys::VirtualFile GetBoot0Raw() const;
34 std::array<u8, 0xB0> GetEncryptedKeyblob(u8 index) const; 37 EncryptedKeyBlob GetEncryptedKeyblob(std::size_t index) const;
35 std::array<std::array<u8, 0xB0>, 0x20> GetEncryptedKeyblobs() const; 38 EncryptedKeyBlobs GetEncryptedKeyblobs() const;
36 std::vector<u8> GetSecureMonitor() const; 39 std::vector<u8> GetSecureMonitor() const;
37 std::array<u8, 0x10> GetPackage2KeySource() const; 40 std::array<u8, 0x10> GetPackage2KeySource() const;
38 std::array<u8, 0x10> GetAESKekGenerationSource() const; 41 std::array<u8, 0x10> GetAESKekGenerationSource() const;
@@ -43,7 +46,7 @@ public:
43 std::vector<u8> GetPackage1Decrypted() const; 46 std::vector<u8> GetPackage1Decrypted() const;
44 std::array<u8, 0x10> GetMasterKeySource() const; 47 std::array<u8, 0x10> GetMasterKeySource() const;
45 std::array<u8, 0x10> GetKeyblobMACKeySource() const; 48 std::array<u8, 0x10> GetKeyblobMACKeySource() const;
46 std::array<u8, 0x10> GetKeyblobKeySource(u8 revision) const; 49 std::array<u8, 0x10> GetKeyblobKeySource(std::size_t revision) const;
47 50
48 // Fuses 51 // Fuses
49 bool HasFuses() const; 52 bool HasFuses() const;
@@ -57,7 +60,8 @@ public:
57 // Package2 60 // Package2
58 bool HasPackage2(Package2Type type = Package2Type::NormalMain) const; 61 bool HasPackage2(Package2Type type = Package2Type::NormalMain) const;
59 FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const; 62 FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const;
60 void DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2, Package2Type type); 63 void DecryptPackage2(const std::array<std::array<u8, 16>, 0x20>& package2_keys,
64 Package2Type type);
61 const std::vector<u8>& GetPackage2FSDecompressed( 65 const std::vector<u8>& GetPackage2FSDecompressed(
62 Package2Type type = Package2Type::NormalMain) const; 66 Package2Type type = Package2Type::NormalMain) const;
63 std::array<u8, 0x10> GetKeyAreaKeyApplicationSource( 67 std::array<u8, 0x10> GetKeyAreaKeyApplicationSource(
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 6102ef476..76a2b7e86 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -10,19 +10,19 @@ namespace FileSys {
10 10
11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_) 11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), 12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
13 sysnand_cache(std::make_shared<RegisteredCache>( 13 sysnand_cache(std::make_unique<RegisteredCache>(
14 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), 14 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
15 usrnand_cache(std::make_shared<RegisteredCache>( 15 usrnand_cache(std::make_unique<RegisteredCache>(
16 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {} 16 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
17 17
18BISFactory::~BISFactory() = default; 18BISFactory::~BISFactory() = default;
19 19
20std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const { 20RegisteredCache* BISFactory::GetSystemNANDContents() const {
21 return sysnand_cache; 21 return sysnand_cache.get();
22} 22}
23 23
24std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const { 24RegisteredCache* BISFactory::GetUserNANDContents() const {
25 return usrnand_cache; 25 return usrnand_cache.get();
26} 26}
27 27
28VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { 28VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index c352e0925..364d309bd 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -20,8 +20,8 @@ public:
20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root); 20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
21 ~BISFactory(); 21 ~BISFactory();
22 22
23 std::shared_ptr<RegisteredCache> GetSystemNANDContents() const; 23 RegisteredCache* GetSystemNANDContents() const;
24 std::shared_ptr<RegisteredCache> GetUserNANDContents() const; 24 RegisteredCache* GetUserNANDContents() const;
25 25
26 VirtualDir GetModificationLoadRoot(u64 title_id) const; 26 VirtualDir GetModificationLoadRoot(u64 title_id) const;
27 27
@@ -29,8 +29,8 @@ private:
29 VirtualDir nand_root; 29 VirtualDir nand_root;
30 VirtualDir load_root; 30 VirtualDir load_root;
31 31
32 std::shared_ptr<RegisteredCache> sysnand_cache; 32 std::unique_ptr<RegisteredCache> sysnand_cache;
33 std::shared_ptr<RegisteredCache> usrnand_cache; 33 std::unique_ptr<RegisteredCache> usrnand_cache;
34}; 34};
35 35
36} // namespace FileSys 36} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index aa1b3c17d..6dcec7816 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -133,7 +133,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
133 static_cast<u8>(type)); 133 static_cast<u8>(type));
134 u128 out_128{}; 134 u128 out_128{};
135 memcpy(out_128.data(), out.data(), 16); 135 memcpy(out_128.data(), out.data(), 16);
136 LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}", 136 LOG_TRACE(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",
137 master_key_id, header.key_index, out_128[1], out_128[0]); 137 master_key_id, header.key_index, out_128[1], out_128[0]);
138 138
139 return out; 139 return out;
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 5b1177a03..a012c2be9 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -17,11 +17,13 @@ const std::array<const char*, 15> LANGUAGE_NAMES = {
17}; 17};
18 18
19std::string LanguageEntry::GetApplicationName() const { 19std::string LanguageEntry::GetApplicationName() const {
20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200); 20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(),
21 application_name.size());
21} 22}
22 23
23std::string LanguageEntry::GetDeveloperName() const { 24std::string LanguageEntry::GetDeveloperName() const {
24 return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100); 25 return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(),
26 developer_name.size());
25} 27}
26 28
27NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) { 29NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
@@ -56,7 +58,12 @@ u64 NACP::GetTitleId() const {
56 return raw->title_id; 58 return raw->title_id;
57} 59}
58 60
61u64 NACP::GetDLCBaseTitleId() const {
62 return raw->dlc_base_title_id;
63}
64
59std::string NACP::GetVersionString() const { 65std::string NACP::GetVersionString() const {
60 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 0x10); 66 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
67 raw->version_string.size());
61} 68}
62} // namespace FileSys 69} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 43d6f0719..141f7e056 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -79,6 +79,7 @@ public:
79 std::string GetApplicationName(Language language = Language::Default) const; 79 std::string GetApplicationName(Language language = Language::Default) const;
80 std::string GetDeveloperName(Language language = Language::Default) const; 80 std::string GetDeveloperName(Language language = Language::Default) const;
81 u64 GetTitleId() const; 81 u64 GetTitleId() const;
82 u64 GetDLCBaseTitleId() const;
82 std::string GetVersionString() const; 83 std::string GetVersionString() const;
83 84
84private: 85private:
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 019caebe9..0117cb0bf 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -214,8 +214,14 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
214 214
215VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type, 215VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type,
216 VirtualFile update_raw) const { 216 VirtualFile update_raw) const {
217 LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id, 217 const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}",
218 static_cast<u8>(type)); 218 title_id, static_cast<u8>(type))
219 .c_str();
220
221 if (type == ContentRecordType::Program)
222 LOG_INFO(Loader, log_string);
223 else
224 LOG_DEBUG(Loader, log_string);
219 225
220 if (romfs == nullptr) 226 if (romfs == nullptr)
221 return romfs; 227 return romfs;
@@ -346,7 +352,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
346} 352}
347 353
348std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { 354std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
349 const auto& installed{Service::FileSystem::GetUnionContents()}; 355 const auto installed{Service::FileSystem::GetUnionContents()};
350 356
351 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); 357 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
352 if (base_control_nca == nullptr) 358 if (base_control_nca == nullptr)
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index e9b040689..1febb398e 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -308,14 +308,14 @@ VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
308 return GetEntryRaw(entry.title_id, entry.type); 308 return GetEntryRaw(entry.title_id, entry.type);
309} 309}
310 310
311std::shared_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const { 311std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
312 const auto raw = GetEntryRaw(title_id, type); 312 const auto raw = GetEntryRaw(title_id, type);
313 if (raw == nullptr) 313 if (raw == nullptr)
314 return nullptr; 314 return nullptr;
315 return std::make_shared<NCA>(raw); 315 return std::make_unique<NCA>(raw);
316} 316}
317 317
318std::shared_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const { 318std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
319 return GetEntry(entry.title_id, entry.type); 319 return GetEntry(entry.title_id, entry.type);
320} 320}
321 321
@@ -516,7 +516,7 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
516 }) != yuzu_meta.end(); 516 }) != yuzu_meta.end();
517} 517}
518 518
519RegisteredCacheUnion::RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches) 519RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches)
520 : caches(std::move(caches)) {} 520 : caches(std::move(caches)) {}
521 521
522void RegisteredCacheUnion::Refresh() { 522void RegisteredCacheUnion::Refresh() {
@@ -572,14 +572,14 @@ VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const
572 return GetEntryRaw(entry.title_id, entry.type); 572 return GetEntryRaw(entry.title_id, entry.type);
573} 573}
574 574
575std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const { 575std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
576 const auto raw = GetEntryRaw(title_id, type); 576 const auto raw = GetEntryRaw(title_id, type);
577 if (raw == nullptr) 577 if (raw == nullptr)
578 return nullptr; 578 return nullptr;
579 return std::make_shared<NCA>(raw); 579 return std::make_unique<NCA>(raw);
580} 580}
581 581
582std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const { 582std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
583 return GetEntry(entry.title_id, entry.type); 583 return GetEntry(entry.title_id, entry.type);
584} 584}
585 585
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index c0cd59fc5..5ddacba47 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -88,8 +88,8 @@ public:
88 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 88 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
89 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; 89 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
90 90
91 std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 91 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
92 std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; 92 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
93 93
94 std::vector<RegisteredCacheEntry> ListEntries() const; 94 std::vector<RegisteredCacheEntry> ListEntries() const;
95 // If a parameter is not boost::none, it will be filtered for from all entries. 95 // If a parameter is not boost::none, it will be filtered for from all entries.
@@ -142,7 +142,7 @@ private:
142// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface. 142// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.
143class RegisteredCacheUnion { 143class RegisteredCacheUnion {
144public: 144public:
145 explicit RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches); 145 explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches);
146 146
147 void Refresh(); 147 void Refresh();
148 148
@@ -157,8 +157,8 @@ public:
157 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 157 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
158 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; 158 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
159 159
160 std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 160 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
161 std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; 161 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
162 162
163 std::vector<RegisteredCacheEntry> ListEntries() const; 163 std::vector<RegisteredCacheEntry> ListEntries() const;
164 // If a parameter is not boost::none, it will be filtered for from all entries. 164 // If a parameter is not boost::none, it will be filtered for from all entries.
@@ -168,7 +168,7 @@ public:
168 boost::optional<u64> title_id = boost::none) const; 168 boost::optional<u64> title_id = boost::none) const;
169 169
170private: 170private:
171 std::vector<std::shared_ptr<RegisteredCache>> caches; 171 std::vector<RegisteredCache*> caches;
172}; 172};
173 173
174} // namespace FileSys 174} // namespace FileSys
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 47f2ab9e0..ef1aaebbb 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -51,6 +51,13 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
51 meta.title_id); 51 meta.title_id);
52 } 52 }
53 53
54 if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) {
55 LOG_WARNING(Service_FS,
56 "Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is "
57 "non-zero ({:016X}{:016X})",
58 meta.user_id[1], meta.user_id[0]);
59 }
60
54 std::string save_directory = 61 std::string save_directory =
55 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); 62 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
56 63
@@ -92,6 +99,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
92 case SaveDataSpaceId::NandUser: 99 case SaveDataSpaceId::NandUser:
93 out = "/user/"; 100 out = "/user/";
94 break; 101 break;
102 case SaveDataSpaceId::TemporaryStorage:
103 out = "/temp/";
104 break;
95 default: 105 default:
96 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); 106 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
97 } 107 }
@@ -100,10 +110,11 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
100 case SaveDataType::SystemSaveData: 110 case SaveDataType::SystemSaveData:
101 return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); 111 return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
102 case SaveDataType::SaveData: 112 case SaveDataType::SaveData:
113 case SaveDataType::DeviceSaveData:
103 return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], 114 return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
104 title_id); 115 title_id);
105 case SaveDataType::TemporaryStorage: 116 case SaveDataType::TemporaryStorage:
106 return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], 117 return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
107 title_id); 118 title_id);
108 default: 119 default:
109 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); 120 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index d66a9c9a4..bd3a57058 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -10,10 +10,10 @@
10namespace FileSys { 10namespace FileSys {
11 11
12SDMCFactory::SDMCFactory(VirtualDir dir_) 12SDMCFactory::SDMCFactory(VirtualDir dir_)
13 : dir(std::move(dir_)), contents(std::make_shared<RegisteredCache>( 13 : dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>(
14 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), 14 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
15 [](const VirtualFile& file, const NcaID& id) { 15 [](const VirtualFile& file, const NcaID& id) {
16 return std::make_shared<NAX>(file, id)->GetDecrypted(); 16 return NAX{file, id}.GetDecrypted();
17 })) {} 17 })) {}
18 18
19SDMCFactory::~SDMCFactory() = default; 19SDMCFactory::~SDMCFactory() = default;
@@ -22,8 +22,8 @@ ResultVal<VirtualDir> SDMCFactory::Open() {
22 return MakeResult<VirtualDir>(dir); 22 return MakeResult<VirtualDir>(dir);
23} 23}
24 24
25std::shared_ptr<RegisteredCache> SDMCFactory::GetSDMCContents() const { 25RegisteredCache* SDMCFactory::GetSDMCContents() const {
26 return contents; 26 return contents.get();
27} 27}
28 28
29} // namespace FileSys 29} // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index ea12149de..42794ba5b 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -19,12 +19,12 @@ public:
19 ~SDMCFactory(); 19 ~SDMCFactory();
20 20
21 ResultVal<VirtualDir> Open(); 21 ResultVal<VirtualDir> Open();
22 std::shared_ptr<RegisteredCache> GetSDMCContents() const; 22 RegisteredCache* GetSDMCContents() const;
23 23
24private: 24private:
25 VirtualDir dir; 25 VirtualDir dir;
26 26
27 std::shared_ptr<RegisteredCache> contents; 27 std::unique_ptr<RegisteredCache> contents;
28}; 28};
29 29
30} // namespace FileSys 30} // namespace FileSys
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index e961ef121..bdcc889e0 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -207,7 +207,7 @@ void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
207 207
208static Kernel::Thread* FindThreadById(int id) { 208static Kernel::Thread* FindThreadById(int id) {
209 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 209 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
210 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); 210 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
211 for (auto& thread : threads) { 211 for (auto& thread : threads) {
212 if (thread->GetThreadID() == static_cast<u32>(id)) { 212 if (thread->GetThreadID() == static_cast<u32>(id)) {
213 current_core = core; 213 current_core = core;
@@ -597,7 +597,7 @@ static void HandleQuery() {
597 } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { 597 } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
598 std::string val = "m"; 598 std::string val = "m";
599 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 599 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
600 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); 600 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
601 for (const auto& thread : threads) { 601 for (const auto& thread : threads) {
602 val += fmt::format("{:x}", thread->GetThreadID()); 602 val += fmt::format("{:x}", thread->GetThreadID());
603 val += ","; 603 val += ",";
@@ -612,7 +612,7 @@ static void HandleQuery() {
612 buffer += "l<?xml version=\"1.0\"?>"; 612 buffer += "l<?xml version=\"1.0\"?>";
613 buffer += "<threads>"; 613 buffer += "<threads>";
614 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 614 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
615 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); 615 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
616 for (const auto& thread : threads) { 616 for (const auto& thread : threads) {
617 buffer += 617 buffer +=
618 fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*", 618 fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index ebf193930..57157beb4 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -39,7 +39,7 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)
39 std::vector<SharedPtr<Thread>>& waiting_threads, 39 std::vector<SharedPtr<Thread>>& waiting_threads,
40 VAddr arb_addr) { 40 VAddr arb_addr) {
41 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 41 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
42 const auto& thread_list = scheduler->GetThreadList(); 42 const auto& thread_list = scheduler.GetThreadList();
43 43
44 for (const auto& thread : thread_list) { 44 for (const auto& thread : thread_list) {
45 if (thread->GetArbiterWaitAddress() == arb_addr) 45 if (thread->GetArbiterWaitAddress() == arb_addr)
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index c80b2c507..073dd5a7d 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -153,11 +153,11 @@ void Process::PrepareForTermination() {
153 } 153 }
154 }; 154 };
155 155
156 auto& system = Core::System::GetInstance(); 156 const auto& system = Core::System::GetInstance();
157 stop_threads(system.Scheduler(0)->GetThreadList()); 157 stop_threads(system.Scheduler(0).GetThreadList());
158 stop_threads(system.Scheduler(1)->GetThreadList()); 158 stop_threads(system.Scheduler(1).GetThreadList());
159 stop_threads(system.Scheduler(2)->GetThreadList()); 159 stop_threads(system.Scheduler(2).GetThreadList());
160 stop_threads(system.Scheduler(3)->GetThreadList()); 160 stop_threads(system.Scheduler(3).GetThreadList());
161} 161}
162 162
163/** 163/**
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 73ec01e11..f2816943a 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -24,6 +24,7 @@ class ProgramMetadata;
24namespace Kernel { 24namespace Kernel {
25 25
26class KernelCore; 26class KernelCore;
27class ResourceLimit;
27 28
28struct AddressMapping { 29struct AddressMapping {
29 // Address and size must be page-aligned 30 // Address and size must be page-aligned
@@ -57,9 +58,23 @@ union ProcessFlags {
57 BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000). 58 BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
58}; 59};
59 60
60enum class ProcessStatus { Created, Running, Exited }; 61/**
61 62 * Indicates the status of a Process instance.
62class ResourceLimit; 63 *
64 * @note These match the values as used by kernel,
65 * so new entries should only be added if RE
66 * shows that a new value has been introduced.
67 */
68enum class ProcessStatus {
69 Created,
70 CreatedWithDebuggerAttached,
71 Running,
72 WaitingForDebuggerToAttach,
73 DebuggerAttached,
74 Exiting,
75 Exited,
76 DebugBreak,
77};
63 78
64struct CodeSet final { 79struct CodeSet final {
65 struct Segment { 80 struct Segment {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index e406df829..3e5f11f2b 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -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/loader.cpp b/src/core/loader/loader.cpp
index 91659ec17..9cd0b0ccd 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
93 return "unknown"; 93 return "unknown";
94} 94}
95 95
96constexpr std::array<const char*, 59> RESULT_MESSAGES{ 96constexpr std::array<const char*, 60> RESULT_MESSAGES{
97 "The operation completed successfully.", 97 "The operation completed successfully.",
98 "The loader requested to load is already loaded.", 98 "The loader requested to load is already loaded.",
99 "The operation is not implemented.", 99 "The operation is not implemented.",
@@ -128,6 +128,7 @@ constexpr std::array<const char*, 59> RESULT_MESSAGES{
128 "The RomFS could not be found.", 128 "The RomFS could not be found.",
129 "The ELF file has incorrect size as determined by the header.", 129 "The ELF file has incorrect size as determined by the header.",
130 "There was a general error loading the NRO into emulated memory.", 130 "There was a general error loading the NRO into emulated memory.",
131 "There was a general error loading the NSO into emulated memory.",
131 "There is no icon available.", 132 "There is no icon available.",
132 "There is no control data available.", 133 "There is no control data available.",
133 "The NAX file has a bad header.", 134 "The NAX file has a bad header.",
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 0e0333db5..e562b3a04 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -90,6 +90,7 @@ enum class ResultStatus : u16 {
90 ErrorNoRomFS, 90 ErrorNoRomFS,
91 ErrorIncorrectELFFileSize, 91 ErrorIncorrectELFFileSize,
92 ErrorLoadingNRO, 92 ErrorLoadingNRO,
93 ErrorLoadingNSO,
93 ErrorNoIcon, 94 ErrorNoIcon,
94 ErrorNoControl, 95 ErrorNoControl,
95 ErrorBadNAXHeader, 96 ErrorBadNAXHeader,
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 576fe692a..243b499f2 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -127,10 +127,10 @@ static constexpr u32 PageAlignSize(u32 size) {
127 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 127 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
128} 128}
129 129
130bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { 130bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
131 // Read NSO header 131 // Read NSO header
132 NroHeader nro_header{}; 132 NroHeader nro_header{};
133 if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { 133 if (sizeof(NroHeader) != file.ReadObject(&nro_header)) {
134 return {}; 134 return {};
135 } 135 }
136 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { 136 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
@@ -138,7 +138,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
138 } 138 }
139 139
140 // Build program image 140 // Build program image
141 std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size)); 141 std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size));
142 if (program_image.size() != PageAlignSize(nro_header.file_size)) { 142 if (program_image.size() != PageAlignSize(nro_header.file_size)) {
143 return {}; 143 return {};
144 } 144 }
@@ -182,7 +182,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
182 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); 182 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
183 183
184 // Register module with GDBStub 184 // Register module with GDBStub
185 GDBStub::RegisterModule(file->GetName(), load_base, load_base); 185 GDBStub::RegisterModule(file.GetName(), load_base, load_base);
186 186
187 return true; 187 return true;
188} 188}
@@ -195,7 +195,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
195 // Load NRO 195 // Load NRO
196 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 196 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
197 197
198 if (!LoadNro(file, base_address)) { 198 if (!LoadNro(*file, base_address)) {
199 return ResultStatus::ErrorLoadingNRO; 199 return ResultStatus::ErrorLoadingNRO;
200 } 200 }
201 201
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 04b46119a..50ee5a78a 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -41,7 +41,7 @@ public:
41 bool IsRomFSUpdatable() const override; 41 bool IsRomFSUpdatable() const override;
42 42
43private: 43private:
44 bool LoadNro(FileSys::VirtualFile file, VAddr load_base); 44 bool LoadNro(const FileSys::VfsFile& file, VAddr load_base);
45 45
46 std::vector<u8> icon_data; 46 std::vector<u8> icon_data;
47 std::unique_ptr<FileSys::NACP> nacp; 47 std::unique_ptr<FileSys::NACP> nacp;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index ba57db9bf..68efca5c0 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -93,17 +93,14 @@ static constexpr u32 PageAlignSize(u32 size) {
93 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 93 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
94} 94}
95 95
96VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, 96std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAddr load_base,
97 bool should_pass_arguments, 97 bool should_pass_arguments,
98 boost::optional<FileSys::PatchManager> pm) { 98 std::optional<FileSys::PatchManager> pm) {
99 if (file == nullptr) 99 if (file.GetSize() < sizeof(NsoHeader))
100 return {};
101
102 if (file->GetSize() < sizeof(NsoHeader))
103 return {}; 100 return {};
104 101
105 NsoHeader nso_header{}; 102 NsoHeader nso_header{};
106 if (sizeof(NsoHeader) != file->ReadObject(&nso_header)) 103 if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
107 return {}; 104 return {};
108 105
109 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) 106 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
@@ -114,7 +111,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
114 std::vector<u8> program_image; 111 std::vector<u8> program_image;
115 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { 112 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
116 std::vector<u8> data = 113 std::vector<u8> data =
117 file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); 114 file.ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
118 if (nso_header.IsSegmentCompressed(i)) { 115 if (nso_header.IsSegmentCompressed(i)) {
119 data = DecompressSegment(data, nso_header.segments[i]); 116 data = DecompressSegment(data, nso_header.segments[i]);
120 } 117 }
@@ -157,7 +154,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
157 program_image.resize(image_size); 154 program_image.resize(image_size);
158 155
159 // Apply patches if necessary 156 // Apply patches if necessary
160 if (pm != boost::none && pm->HasNSOPatch(nso_header.build_id)) { 157 if (pm && pm->HasNSOPatch(nso_header.build_id)) {
161 std::vector<u8> pi_header(program_image.size() + 0x100); 158 std::vector<u8> pi_header(program_image.size() + 0x100);
162 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); 159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
163 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); 160 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
@@ -172,7 +169,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
172 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); 169 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
173 170
174 // Register module with GDBStub 171 // Register module with GDBStub
175 GDBStub::RegisterModule(file->GetName(), load_base, load_base); 172 GDBStub::RegisterModule(file.GetName(), load_base, load_base);
176 173
177 return load_base + image_size; 174 return load_base + image_size;
178} 175}
@@ -184,7 +181,9 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
184 181
185 // Load module 182 // Load module
186 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 183 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
187 LoadModule(file, base_address, true); 184 if (!LoadModule(*file, base_address, true)) {
185 return ResultStatus::ErrorLoadingNSO;
186 }
188 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); 187 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
189 188
190 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); 189 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 70ab3b718..433306139 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <optional>
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "core/file_sys/patch_manager.h" 9#include "core/file_sys/patch_manager.h"
9#include "core/loader/linker.h" 10#include "core/loader/linker.h"
@@ -36,8 +37,9 @@ public:
36 return IdentifyType(file); 37 return IdentifyType(file);
37 } 38 }
38 39
39 static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, bool should_pass_arguments, 40 static std::optional<VAddr> LoadModule(const FileSys::VfsFile& file, VAddr load_base,
40 boost::optional<FileSys::PatchManager> pm = boost::none); 41 bool should_pass_arguments,
42 std::optional<FileSys::PatchManager> pm = {});
41 43
42 ResultStatus Load(Kernel::Process& process) override; 44 ResultStatus Load(Kernel::Process& process) override;
43}; 45};
diff --git a/src/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 60c99c126..28dba0084 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());
@@ -2667,14 +2665,14 @@ private:
2667 const std::string pred = 2665 const std::string pred =
2668 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); 2666 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);
2669 const std::string combiner = GetPredicateCombiner(instr.csetp.op); 2667 const std::string combiner = GetPredicateCombiner(instr.csetp.op);
2670 const std::string controlCode = regs.GetControlCode(instr.csetp.cc); 2668 const std::string control_code = regs.GetControlCode(instr.csetp.cc);
2671 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { 2669 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {
2672 SetPredicate(instr.csetp.pred3, 2670 SetPredicate(instr.csetp.pred3,
2673 '(' + controlCode + ") " + combiner + " (" + pred + ')'); 2671 '(' + control_code + ") " + combiner + " (" + pred + ')');
2674 } 2672 }
2675 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { 2673 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
2676 SetPredicate(instr.csetp.pred0, 2674 SetPredicate(instr.csetp.pred0,
2677 "!(" + controlCode + ") " + combiner + " (" + pred + ')'); 2675 "!(" + control_code + ") " + combiner + " (" + pred + ')');
2678 } 2676 }
2679 break; 2677 break;
2680 } 2678 }
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 0d2456b56..18ab723f7 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -40,72 +40,146 @@ struct alignas(64) SwizzleTable {
40constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>(); 40constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>();
41constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>(); 41constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>();
42 42
43static void LegacySwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 43/**
44 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, 44 * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
45 u32 block_height) { 45 * Instead of going gob by gob, we map the coordinates inside a block and manage from
46 * those. Block_Width is assumed to be 1.
47 */
48void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
49 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
50 const u32 y_end, const u32 z_end, const u32 tile_offset,
51 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
52 const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
46 std::array<u8*, 2> data_ptrs; 53 std::array<u8*, 2> data_ptrs;
47 const std::size_t stride = width * bytes_per_pixel; 54 u32 z_address = tile_offset;
48 const std::size_t gobs_in_x = 64; 55 const u32 gob_size_x = 64;
49 const std::size_t gobs_in_y = 8; 56 const u32 gob_size_y = 8;
50 const std::size_t gobs_size = gobs_in_x * gobs_in_y; 57 const u32 gob_size_z = 1;
51 const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; 58 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
52 for (std::size_t y = 0; y < height; ++y) { 59 for (u32 z = z_start; z < z_end; z++) {
53 const std::size_t gob_y_address = 60 u32 y_address = z_address;
54 (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + 61 u32 pixel_base = layer_z * z + y_start * stride_x;
55 (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; 62 for (u32 y = y_start; y < y_end; y++) {
56 const auto& table = legacy_swizzle_table[y % gobs_in_y]; 63 const auto& table = legacy_swizzle_table[y % gob_size_y];
57 for (std::size_t x = 0; x < width; ++x) { 64 for (u32 x = x_start; x < x_end; x++) {
58 const std::size_t gob_address = 65 const u32 swizzle_offset{y_address + table[x * bytes_per_pixel % gob_size_x]};
59 gob_y_address + (x * bytes_per_pixel / gobs_in_x) * gobs_size * block_height; 66 const u32 pixel_index{x * out_bytes_per_pixel + pixel_base};
60 const std::size_t x2 = x * bytes_per_pixel; 67 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
61 const std::size_t swizzle_offset = gob_address + table[x2 % gobs_in_x]; 68 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
62 const std::size_t pixel_index = (x + y * width) * out_bytes_per_pixel; 69 std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
63 70 }
64 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 71 pixel_base += stride_x;
65 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 72 if ((y + 1) % gob_size_y == 0)
66 73 y_address += gob_size;
67 std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
68 } 74 }
75 z_address += xy_block_size;
69 } 76 }
70} 77}
71 78
72static void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 79/**
73 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, 80 * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
74 u32 block_height) { 81 * Instead of going gob by gob, we map the coordinates inside a block and manage from
82 * those. Block_Width is assumed to be 1.
83 */
84void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
85 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
86 const u32 y_end, const u32 z_end, const u32 tile_offset,
87 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
88 const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
75 std::array<u8*, 2> data_ptrs; 89 std::array<u8*, 2> data_ptrs;
76 const std::size_t stride{width * bytes_per_pixel}; 90 u32 z_address = tile_offset;
77 const std::size_t gobs_in_x = 64; 91 const u32 x_startb = x_start * bytes_per_pixel;
78 const std::size_t gobs_in_y = 8; 92 const u32 x_endb = x_end * bytes_per_pixel;
79 const std::size_t gobs_size = gobs_in_x * gobs_in_y; 93 const u32 copy_size = 16;
80 const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; 94 const u32 gob_size_x = 64;
81 const std::size_t copy_size{16}; 95 const u32 gob_size_y = 8;
82 for (std::size_t y = 0; y < height; ++y) { 96 const u32 gob_size_z = 1;
83 const std::size_t initial_gob = 97 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
84 (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + 98 for (u32 z = z_start; z < z_end; z++) {
85 (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; 99 u32 y_address = z_address;
86 const std::size_t pixel_base{y * width * out_bytes_per_pixel}; 100 u32 pixel_base = layer_z * z + y_start * stride_x;
87 const auto& table = fast_swizzle_table[y % gobs_in_y]; 101 for (u32 y = y_start; y < y_end; y++) {
88 for (std::size_t xb = 0; xb < stride; xb += copy_size) { 102 const auto& table = fast_swizzle_table[y % gob_size_y];
89 const std::size_t gob_address{initial_gob + 103 for (u32 xb = x_startb; xb < x_endb; xb += copy_size) {
90 (xb / gobs_in_x) * gobs_size * block_height}; 104 const u32 swizzle_offset{y_address + table[(xb / copy_size) % 4]};
91 const std::size_t swizzle_offset{gob_address + table[(xb / 16) % 4]}; 105 const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
92 const std::size_t out_x = xb * out_bytes_per_pixel / bytes_per_pixel; 106 const u32 pixel_index{out_x + pixel_base};
93 const std::size_t pixel_index{out_x + pixel_base}; 107 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
94 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 108 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
95 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 109 std::memcpy(data_ptrs[0], data_ptrs[1], copy_size);
96 std::memcpy(data_ptrs[0], data_ptrs[1], copy_size); 110 }
111 pixel_base += stride_x;
112 if ((y + 1) % gob_size_y == 0)
113 y_address += gob_size;
114 }
115 z_address += xy_block_size;
116 }
117}
118
119/**
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 }
97 } 170 }
98 } 171 }
99} 172}
100 173
101void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 174void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
102 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height) { 175 u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
176 bool unswizzle, u32 block_height, u32 block_depth) {
103 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { 177 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
104 FastSwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, 178 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
105 unswizzled_data, unswizzle, block_height); 179 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
106 } else { 180 } else {
107 LegacySwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, 181 SwizzledData<false>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
108 unswizzled_data, unswizzle, block_height); 182 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
109 } 183 }
110} 184}
111 185
@@ -126,7 +200,9 @@ u32 BytesPerPixel(TextureFormat format) {
126 case TextureFormat::R32_G32_B32: 200 case TextureFormat::R32_G32_B32:
127 return 12; 201 return 12;
128 case TextureFormat::ASTC_2D_4X4: 202 case TextureFormat::ASTC_2D_4X4:
203 case TextureFormat::ASTC_2D_5X4:
129 case TextureFormat::ASTC_2D_8X8: 204 case TextureFormat::ASTC_2D_8X8:
205 case TextureFormat::ASTC_2D_8X5:
130 case TextureFormat::A8R8G8B8: 206 case TextureFormat::A8R8G8B8:
131 case TextureFormat::A2B10G10R10: 207 case TextureFormat::A2B10G10R10:
132 case TextureFormat::BF10GF11RF11: 208 case TextureFormat::BF10GF11RF11:
@@ -153,10 +229,11 @@ u32 BytesPerPixel(TextureFormat format) {
153} 229}
154 230
155std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 231std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
156 u32 height, u32 block_height) { 232 u32 height, u32 depth, u32 block_height, u32 block_depth) {
157 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); 233 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
158 CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel, 234 CopySwizzledData(width / tile_size, height / tile_size, depth, bytes_per_pixel, bytes_per_pixel,
159 Memory::GetPointer(address), unswizzled_data.data(), true, block_height); 235 Memory::GetPointer(address), unswizzled_data.data(), true, block_height,
236 block_depth);
160 return unswizzled_data; 237 return unswizzled_data;
161} 238}
162 239
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 234d250af..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.
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 fc186dc2d..bef9df00d 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -176,7 +176,7 @@ GMainWindow::GMainWindow()
176 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); 176 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
177 177
178 // Necessary to load titles from nand in gamelist. 178 // Necessary to load titles from nand in gamelist.
179 Service::FileSystem::CreateFactories(vfs); 179 Service::FileSystem::CreateFactories(*vfs);
180 game_list->LoadCompatibilityList(); 180 game_list->LoadCompatibilityList();
181 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 181 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
182 182
@@ -908,22 +908,20 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
908} 908}
909 909
910void GMainWindow::OnMenuLoadFile() { 910void GMainWindow::OnMenuLoadFile() {
911 QString extensions; 911 const QString extensions =
912 for (const auto& piece : game_list->supported_file_extensions) 912 QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main");
913 extensions += "*." + piece + " "; 913 const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)",
914 "%1 is an identifier for the Switch executable file extensions.")
915 .arg(extensions);
916 const QString filename = QFileDialog::getOpenFileName(
917 this, tr("Load File"), UISettings::values.roms_path, file_filter);
914 918
915 extensions += "main "; 919 if (filename.isEmpty()) {
916 920 return;
917 QString file_filter = tr("Switch Executable") + " (" + extensions + ")";
918 file_filter += ";;" + tr("All Files (*.*)");
919
920 QString filename = QFileDialog::getOpenFileName(this, tr("Load File"),
921 UISettings::values.roms_path, file_filter);
922 if (!filename.isEmpty()) {
923 UISettings::values.roms_path = QFileInfo(filename).path();
924
925 BootGame(filename);
926 } 921 }
922
923 UISettings::values.roms_path = QFileInfo(filename).path();
924 BootGame(filename);
927} 925}
928 926
929void GMainWindow::OnMenuLoadFolder() { 927void GMainWindow::OnMenuLoadFolder() {
@@ -1139,7 +1137,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
1139 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir 1137 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
1140 : FileUtil::UserPath::NANDDir, 1138 : FileUtil::UserPath::NANDDir,
1141 dir_path.toStdString()); 1139 dir_path.toStdString());
1142 Service::FileSystem::CreateFactories(vfs); 1140 Service::FileSystem::CreateFactories(*vfs);
1143 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1141 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
1144 } 1142 }
1145} 1143}
@@ -1410,7 +1408,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1410 1408
1411 const auto function = [this, &keys, &pdm] { 1409 const auto function = [this, &keys, &pdm] {
1412 keys.PopulateFromPartitionData(pdm); 1410 keys.PopulateFromPartitionData(pdm);
1413 Service::FileSystem::CreateFactories(vfs); 1411 Service::FileSystem::CreateFactories(*vfs);
1414 keys.DeriveETicket(pdm); 1412 keys.DeriveETicket(pdm);
1415 }; 1413 };
1416 1414
@@ -1430,8 +1428,12 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1430 QMessageBox::warning( 1428 QMessageBox::warning(
1431 this, tr("Warning Missing Derivation Components"), 1429 this, tr("Warning Missing Derivation Components"),
1432 tr("The following are missing from your configuration that may hinder key " 1430 tr("The following are missing from your configuration that may hinder key "
1433 "derivation. It will be attempted but may not complete.\n\n") + 1431 "derivation. It will be attempted but may not complete.<br><br>") +
1434 errors); 1432 errors +
1433 tr("<br><br>You can get all of these and dump all of your games easily by "
1434 "following <a href='https://yuzu-emu.org/help/quickstart/quickstart/'>the "
1435 "quickstart guide</a>. Alternatively, you can use another method of dumping "
1436 "to obtain all of your keys."));
1435 } 1437 }
1436 1438
1437 QProgressDialog prog; 1439 QProgressDialog prog;
@@ -1450,7 +1452,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1450 prog.close(); 1452 prog.close();
1451 } 1453 }
1452 1454
1453 Service::FileSystem::CreateFactories(vfs); 1455 Service::FileSystem::CreateFactories(*vfs);
1454 1456
1455 if (behavior == ReinitializeKeyBehavior::Warning) { 1457 if (behavior == ReinitializeKeyBehavior::Warning) {
1456 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1458 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
@@ -1565,7 +1567,7 @@ void GMainWindow::UpdateUITheme() {
1565 emit UpdateThemedIcons(); 1567 emit UpdateThemedIcons();
1566} 1568}
1567 1569
1568void GMainWindow::SetDiscordEnabled(bool state) { 1570void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
1569#ifdef USE_DISCORD_PRESENCE 1571#ifdef USE_DISCORD_PRESENCE
1570 if (state) { 1572 if (state) {
1571 discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>(); 1573 discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>();
diff --git a/src/yuzu_cmd/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