summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/arm/cpu_interrupt_handler.cpp6
-rw-r--r--src/core/arm/cpu_interrupt_handler.h3
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp13
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp13
-rw-r--r--src/core/core.cpp14
-rw-r--r--src/core/core_timing.cpp12
-rw-r--r--src/core/core_timing_util.cpp1
-rw-r--r--src/core/core_timing_util.h1
-rw-r--r--src/core/cpu_manager.cpp29
-rw-r--r--src/core/crypto/aes_util.cpp2
-rw-r--r--src/core/crypto/key_manager.cpp449
-rw-r--r--src/core/crypto/key_manager.h8
-rw-r--r--src/core/crypto/partition_data_manager.cpp210
-rw-r--r--src/core/file_sys/bis_factory.cpp2
-rw-r--r--src/core/file_sys/registered_cache.cpp35
-rw-r--r--src/core/file_sys/registered_cache.h6
-rw-r--r--src/core/file_sys/system_archive/mii_model.cpp18
-rw-r--r--src/core/file_sys/system_archive/ng_word.cpp42
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.cpp9
-rw-r--r--src/core/file_sys/vfs.cpp56
-rw-r--r--src/core/file_sys/vfs_libzip.cpp2
-rw-r--r--src/core/file_sys/vfs_real.cpp241
-rw-r--r--src/core/file_sys/vfs_real.h8
-rw-r--r--src/core/file_sys/vfs_vector.h13
-rw-r--r--src/core/file_sys/xts_archive.cpp2
-rw-r--r--src/core/frontend/emu_window.h4
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp2
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp46
-rw-r--r--src/core/hle/kernel/memory/page_table.cpp23
-rw-r--r--src/core/hle/kernel/memory/system_control.cpp21
-rw-r--r--src/core/hle/kernel/memory/system_control.h5
-rw-r--r--src/core/hle/kernel/scheduler.cpp31
-rw-r--r--src/core/hle/kernel/scheduler.h2
-rw-r--r--src/core/hle/result.h12
-rw-r--r--src/core/hle/service/acc/acc.cpp8
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp22
-rw-r--r--src/core/hle/service/am/am.cpp6
-rw-r--r--src/core/hle/service/am/am.h7
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp3
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp19
-rw-r--r--src/core/hle/service/audio/audout_u.cpp2
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp26
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp80
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp239
-rw-r--r--src/core/hle/service/hid/controllers/npad.h11
-rw-r--r--src/core/hle/service/hid/hid.cpp14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h3
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h10
-rw-r--r--src/core/hle/service/sm/sm.h2
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp4
-rw-r--r--src/core/loader/loader.cpp4
-rw-r--r--src/core/memory/cheat_engine.cpp26
-rw-r--r--src/core/perf_stats.cpp4
-rw-r--r--src/core/reporter.cpp9
-rw-r--r--src/core/settings.cpp54
-rw-r--r--src/core/settings.h345
-rw-r--r--src/core/telemetry_session.cpp14
-rw-r--r--src/core/telemetry_session.h5
-rw-r--r--src/core/tools/freezer.cpp31
-rw-r--r--src/core/tools/freezer.h7
60 files changed, 1045 insertions, 1251 deletions
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
index df0350881..9c8898700 100644
--- a/src/core/arm/cpu_interrupt_handler.cpp
+++ b/src/core/arm/cpu_interrupt_handler.cpp
@@ -7,9 +7,7 @@
7 7
8namespace Core { 8namespace Core {
9 9
10CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} { 10CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {}
11 interrupt_event = std::make_unique<Common::Event>();
12}
13 11
14CPUInterruptHandler::~CPUInterruptHandler() = default; 12CPUInterruptHandler::~CPUInterruptHandler() = default;
15 13
@@ -17,7 +15,7 @@ void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
17 if (is_interrupted_) { 15 if (is_interrupted_) {
18 interrupt_event->Set(); 16 interrupt_event->Set();
19 } 17 }
20 this->is_interrupted = is_interrupted_; 18 is_interrupted = is_interrupted_;
21} 19}
22 20
23void CPUInterruptHandler::AwaitInterrupt() { 21void CPUInterruptHandler::AwaitInterrupt() {
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
index 3d062d326..71e582f79 100644
--- a/src/core/arm/cpu_interrupt_handler.h
+++ b/src/core/arm/cpu_interrupt_handler.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
8 9
9namespace Common { 10namespace Common {
@@ -32,8 +33,8 @@ public:
32 void AwaitInterrupt(); 33 void AwaitInterrupt();
33 34
34private: 35private:
35 bool is_interrupted{};
36 std::unique_ptr<Common::Event> interrupt_event; 36 std::unique_ptr<Common::Event> interrupt_event;
37 std::atomic_bool is_interrupted{false};
37}; 38};
38 39
39} // namespace Core 40} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 443ca72eb..b5f28a86e 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -143,7 +143,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
143 config.wall_clock_cntpct = uses_wall_clock; 143 config.wall_clock_cntpct = uses_wall_clock;
144 144
145 // Safe optimizations 145 // Safe optimizations
146 if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) { 146 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
147 if (!Settings::values.cpuopt_page_tables) { 147 if (!Settings::values.cpuopt_page_tables) {
148 config.page_table = nullptr; 148 config.page_table = nullptr;
149 } 149 }
@@ -170,6 +170,17 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
170 } 170 }
171 } 171 }
172 172
173 // Unsafe optimizations
174 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) {
175 config.unsafe_optimizations = true;
176 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
177 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
178 }
179 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
180 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
181 }
182 }
183
173 return std::make_unique<Dynarmic::A32::Jit>(config); 184 return std::make_unique<Dynarmic::A32::Jit>(config);
174} 185}
175 186
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index a63a04a25..ce9968724 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -195,7 +195,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
195 config.wall_clock_cntpct = uses_wall_clock; 195 config.wall_clock_cntpct = uses_wall_clock;
196 196
197 // Safe optimizations 197 // Safe optimizations
198 if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) { 198 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
199 if (!Settings::values.cpuopt_page_tables) { 199 if (!Settings::values.cpuopt_page_tables) {
200 config.page_table = nullptr; 200 config.page_table = nullptr;
201 } 201 }
@@ -222,6 +222,17 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
222 } 222 }
223 } 223 }
224 224
225 // Unsafe optimizations
226 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) {
227 config.unsafe_optimizations = true;
228 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
229 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
230 }
231 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
232 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
233 }
234 }
235
225 return std::make_shared<Dynarmic::A64::Jit>(config); 236 return std::make_shared<Dynarmic::A64::Jit>(config);
226} 237}
227 238
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 42277e2cd..c2c0eec0b 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -113,7 +113,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
113 return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName()); 113 return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
114 } 114 }
115 115
116 if (FileUtil::IsDirectory(path)) 116 if (Common::FS::IsDirectory(path))
117 return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read); 117 return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read);
118 118
119 return vfs->OpenFile(path, FileSys::Mode::Read); 119 return vfs->OpenFile(path, FileSys::Mode::Read);
@@ -269,14 +269,14 @@ struct System::Impl {
269 // Log last frame performance stats if game was loded 269 // Log last frame performance stats if game was loded
270 if (perf_stats) { 270 if (perf_stats) {
271 const auto perf_results = GetAndResetPerfStats(); 271 const auto perf_results = GetAndResetPerfStats();
272 telemetry_session->AddField(Telemetry::FieldType::Performance, 272 constexpr auto performance = Common::Telemetry::FieldType::Performance;
273 "Shutdown_EmulationSpeed", 273
274 telemetry_session->AddField(performance, "Shutdown_EmulationSpeed",
274 perf_results.emulation_speed * 100.0); 275 perf_results.emulation_speed * 100.0);
275 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate", 276 telemetry_session->AddField(performance, "Shutdown_Framerate", perf_results.game_fps);
276 perf_results.game_fps); 277 telemetry_session->AddField(performance, "Shutdown_Frametime",
277 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
278 perf_results.frametime * 1000.0); 278 perf_results.frametime * 1000.0);
279 telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS", 279 telemetry_session->AddField(performance, "Mean_Frametime_MS",
280 perf_stats->GetMeanFrametime()); 280 perf_stats->GetMeanFrametime());
281 } 281 }
282 282
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 71af26ec5..e6c8461a5 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -7,14 +7,14 @@
7#include <string> 7#include <string>
8#include <tuple> 8#include <tuple>
9 9
10#include "common/assert.h"
11#include "common/microprofile.h" 10#include "common/microprofile.h"
12#include "core/core_timing.h" 11#include "core/core_timing.h"
13#include "core/core_timing_util.h" 12#include "core/core_timing_util.h"
13#include "core/hardware_properties.h"
14 14
15namespace Core::Timing { 15namespace Core::Timing {
16 16
17constexpr u64 MAX_SLICE_LENGTH = 4000; 17constexpr s64 MAX_SLICE_LENGTH = 4000;
18 18
19std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { 19std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
20 return std::make_shared<EventType>(std::move(callback), std::move(name)); 20 return std::make_shared<EventType>(std::move(callback), std::move(name));
@@ -37,10 +37,8 @@ struct CoreTiming::Event {
37 } 37 }
38}; 38};
39 39
40CoreTiming::CoreTiming() { 40CoreTiming::CoreTiming()
41 clock = 41 : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {}
42 Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ);
43}
44 42
45CoreTiming::~CoreTiming() = default; 43CoreTiming::~CoreTiming() = default;
46 44
@@ -136,7 +134,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
136 134
137void CoreTiming::AddTicks(u64 ticks) { 135void CoreTiming::AddTicks(u64 ticks) {
138 this->ticks += ticks; 136 this->ticks += ticks;
139 downcount -= ticks; 137 downcount -= static_cast<s64>(ticks);
140} 138}
141 139
142void CoreTiming::Idle() { 140void CoreTiming::Idle() {
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp
index aefc63663..8ce8e602e 100644
--- a/src/core/core_timing_util.cpp
+++ b/src/core/core_timing_util.cpp
@@ -8,6 +8,7 @@
8#include <limits> 8#include <limits>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/uint128.h" 10#include "common/uint128.h"
11#include "core/hardware_properties.h"
11 12
12namespace Core::Timing { 13namespace Core::Timing {
13 14
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
index 2ed979e14..e4a046bf9 100644
--- a/src/core/core_timing_util.h
+++ b/src/core/core_timing_util.h
@@ -6,7 +6,6 @@
6 6
7#include <chrono> 7#include <chrono>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hardware_properties.h"
10 9
11namespace Core::Timing { 10namespace Core::Timing {
12 11
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 03651e285..688b99eba 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -41,9 +41,9 @@ void CpuManager::Shutdown() {
41 running_mode = false; 41 running_mode = false;
42 Pause(false); 42 Pause(false);
43 if (is_multicore) { 43 if (is_multicore) {
44 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 44 for (auto& data : core_data) {
45 core_data[core].host_thread->join(); 45 data.host_thread->join();
46 core_data[core].host_thread.reset(); 46 data.host_thread.reset();
47 } 47 }
48 } else { 48 } else {
49 core_data[0].host_thread->join(); 49 core_data[0].host_thread->join();
@@ -166,25 +166,23 @@ void CpuManager::MultiCorePause(bool paused) {
166 bool all_not_barrier = false; 166 bool all_not_barrier = false;
167 while (!all_not_barrier) { 167 while (!all_not_barrier) {
168 all_not_barrier = true; 168 all_not_barrier = true;
169 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 169 for (const auto& data : core_data) {
170 all_not_barrier &= 170 all_not_barrier &= !data.is_running.load() && data.initialized.load();
171 !core_data[core].is_running.load() && core_data[core].initialized.load();
172 } 171 }
173 } 172 }
174 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 173 for (auto& data : core_data) {
175 core_data[core].enter_barrier->Set(); 174 data.enter_barrier->Set();
176 } 175 }
177 if (paused_state.load()) { 176 if (paused_state.load()) {
178 bool all_barrier = false; 177 bool all_barrier = false;
179 while (!all_barrier) { 178 while (!all_barrier) {
180 all_barrier = true; 179 all_barrier = true;
181 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 180 for (const auto& data : core_data) {
182 all_barrier &= 181 all_barrier &= data.is_paused.load() && data.initialized.load();
183 core_data[core].is_paused.load() && core_data[core].initialized.load();
184 } 182 }
185 } 183 }
186 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 184 for (auto& data : core_data) {
187 core_data[core].exit_barrier->Set(); 185 data.exit_barrier->Set();
188 } 186 }
189 } 187 }
190 } else { 188 } else {
@@ -192,9 +190,8 @@ void CpuManager::MultiCorePause(bool paused) {
192 bool all_barrier = false; 190 bool all_barrier = false;
193 while (!all_barrier) { 191 while (!all_barrier) {
194 all_barrier = true; 192 all_barrier = true;
195 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 193 for (const auto& data : core_data) {
196 all_barrier &= 194 all_barrier &= data.is_paused.load() && data.initialized.load();
197 core_data[core].is_paused.load() && core_data[core].initialized.load();
198 } 195 }
199 } 196 }
200 /// Don't release the barrier 197 /// Don't release the barrier
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp
index 330996b24..6a9734812 100644
--- a/src/core/crypto/aes_util.cpp
+++ b/src/core/crypto/aes_util.cpp
@@ -116,7 +116,7 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8*
116 116
117 for (std::size_t i = 0; i < size; i += sector_size) { 117 for (std::size_t i = 0; i < size; i += sector_size) {
118 SetIV(CalculateNintendoTweak(sector_id++)); 118 SetIV(CalculateNintendoTweak(sector_id++));
119 Transcode<u8, u8>(src + i, sector_size, dest + i, op); 119 Transcode(src + i, sector_size, dest + i, op);
120 } 120 }
121} 121}
122 122
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index f87fe0abc..dc591c730 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -36,18 +36,86 @@
36#include "core/settings.h" 36#include "core/settings.h"
37 37
38namespace Core::Crypto { 38namespace Core::Crypto {
39namespace {
39 40
40constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; 41constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
41constexpr u64 FULL_TICKET_SIZE = 0x400; 42constexpr u64 FULL_TICKET_SIZE = 0x400;
42 43
43using namespace Common; 44using Common::AsArray;
44 45
45const std::array<SHA256Hash, 2> eticket_source_hashes{ 46// clang-format off
46 "B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source 47constexpr std::array eticket_source_hashes{
47 "E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source 48 AsArray("B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"), // eticket_rsa_kek_source
49 AsArray("E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"), // eticket_rsa_kekek_source
48}; 50};
51// clang-format on
49 52
50const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ 53constexpr std::array<std::pair<std::string_view, KeyIndex<S128KeyType>>, 30> s128_file_id{{
54 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
55 {"eticket_rsa_kek_source",
56 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
57 {"eticket_rsa_kekek_source",
58 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
59 {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
60 {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
61 {"rsa_oaep_kek_generation_source",
62 {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
63 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
64 {"aes_kek_generation_source",
65 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
66 {"aes_key_generation_source",
67 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
68 {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
69 {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
70 {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
71 {"key_area_key_application_source",
72 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
73 static_cast<u64>(KeyAreaKeyType::Application)}},
74 {"key_area_key_ocean_source",
75 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
76 static_cast<u64>(KeyAreaKeyType::Ocean)}},
77 {"key_area_key_system_source",
78 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
79 static_cast<u64>(KeyAreaKeyType::System)}},
80 {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
81 {"keyblob_mac_key_source",
82 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},
83 {"tsec_key", {S128KeyType::TSEC, 0, 0}},
84 {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
85 {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
86 {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
87 {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
88 {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
89 {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
90 {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
91 {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
92 {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
93 {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
94 {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
95 {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
96}};
97
98auto Find128ByName(std::string_view name) {
99 return std::find_if(s128_file_id.begin(), s128_file_id.end(),
100 [&name](const auto& pair) { return pair.first == name; });
101}
102
103constexpr std::array<std::pair<std::string_view, KeyIndex<S256KeyType>>, 6> s256_file_id{{
104 {"header_key", {S256KeyType::Header, 0, 0}},
105 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
106 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
107 {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
108 {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
109 {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
110}};
111
112auto Find256ByName(std::string_view name) {
113 return std::find_if(s256_file_id.begin(), s256_file_id.end(),
114 [&name](const auto& pair) { return pair.first == name; });
115}
116
117using KeyArray = std::array<std::pair<std::pair<S128KeyType, u64>, std::string_view>, 7>;
118constexpr KeyArray KEYS_VARIABLE_LENGTH{{
51 {{S128KeyType::Master, 0}, "master_key_"}, 119 {{S128KeyType::Master, 0}, "master_key_"},
52 {{S128KeyType::Package1, 0}, "package1_key_"}, 120 {{S128KeyType::Package1, 0}, "package1_key_"},
53 {{S128KeyType::Package2, 0}, "package2_key_"}, 121 {{S128KeyType::Package2, 0}, "package2_key_"},
@@ -55,14 +123,13 @@ const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
55 {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"}, 123 {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"},
56 {{S128KeyType::Keyblob, 0}, "keyblob_key_"}, 124 {{S128KeyType::Keyblob, 0}, "keyblob_key_"},
57 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"}, 125 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
58}; 126}};
59 127
60namespace {
61template <std::size_t Size> 128template <std::size_t Size>
62bool IsAllZeroArray(const std::array<u8, Size>& array) { 129bool IsAllZeroArray(const std::array<u8, Size>& array) {
63 return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; }); 130 return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; });
64} 131}
65} // namespace 132} // Anonymous namespace
66 133
67u64 GetSignatureTypeDataSize(SignatureType type) { 134u64 GetSignatureTypeDataSize(SignatureType type) {
68 switch (type) { 135 switch (type) {
@@ -94,13 +161,13 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
94} 161}
95 162
96SignatureType Ticket::GetSignatureType() const { 163SignatureType Ticket::GetSignatureType() const {
97 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 164 if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
98 return ticket->sig_type; 165 return ticket->sig_type;
99 } 166 }
100 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 167 if (const auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
101 return ticket->sig_type; 168 return ticket->sig_type;
102 } 169 }
103 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 170 if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
104 return ticket->sig_type; 171 return ticket->sig_type;
105 } 172 }
106 173
@@ -108,13 +175,13 @@ SignatureType Ticket::GetSignatureType() const {
108} 175}
109 176
110TicketData& Ticket::GetData() { 177TicketData& Ticket::GetData() {
111 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 178 if (auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
112 return ticket->data; 179 return ticket->data;
113 } 180 }
114 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 181 if (auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
115 return ticket->data; 182 return ticket->data;
116 } 183 }
117 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 184 if (auto* ticket = std::get_if<ECDSATicket>(&data)) {
118 return ticket->data; 185 return ticket->data;
119 } 186 }
120 187
@@ -122,13 +189,13 @@ TicketData& Ticket::GetData() {
122} 189}
123 190
124const TicketData& Ticket::GetData() const { 191const TicketData& Ticket::GetData() const {
125 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 192 if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
126 return ticket->data; 193 return ticket->data;
127 } 194 }
128 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 195 if (const auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
129 return ticket->data; 196 return ticket->data;
130 } 197 }
131 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 198 if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
132 return ticket->data; 199 return ticket->data;
133 } 200 }
134 201
@@ -231,8 +298,9 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
231} 298}
232 299
233RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { 300RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const {
234 if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) 301 if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) {
235 return {}; 302 return {};
303 }
236 304
237 const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); 305 const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek);
238 306
@@ -259,27 +327,30 @@ Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source)
259} 327}
260 328
261std::optional<Key128> DeriveSDSeed() { 329std::optional<Key128> DeriveSDSeed() {
262 const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 330 const Common::FS::IOFile save_43(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
263 "/system/save/8000000000000043", 331 "/system/save/8000000000000043",
264 "rb+"); 332 "rb+");
265 if (!save_43.IsOpen()) 333 if (!save_43.IsOpen()) {
266 return {}; 334 return std::nullopt;
335 }
267 336
268 const FileUtil::IOFile sd_private( 337 const Common::FS::IOFile sd_private(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
269 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); 338 "/Nintendo/Contents/private",
270 if (!sd_private.IsOpen()) 339 "rb+");
271 return {}; 340 if (!sd_private.IsOpen()) {
341 return std::nullopt;
342 }
272 343
273 std::array<u8, 0x10> private_seed{}; 344 std::array<u8, 0x10> private_seed{};
274 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) { 345 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
275 return {}; 346 return std::nullopt;
276 } 347 }
277 348
278 std::array<u8, 0x10> buffer{}; 349 std::array<u8, 0x10> buffer{};
279 std::size_t offset = 0; 350 std::size_t offset = 0;
280 for (; offset + 0x10 < save_43.GetSize(); ++offset) { 351 for (; offset + 0x10 < save_43.GetSize(); ++offset) {
281 if (!save_43.Seek(offset, SEEK_SET)) { 352 if (!save_43.Seek(offset, SEEK_SET)) {
282 return {}; 353 return std::nullopt;
283 } 354 }
284 355
285 save_43.ReadBytes(buffer.data(), buffer.size()); 356 save_43.ReadBytes(buffer.data(), buffer.size());
@@ -289,23 +360,26 @@ std::optional<Key128> DeriveSDSeed() {
289 } 360 }
290 361
291 if (!save_43.Seek(offset + 0x10, SEEK_SET)) { 362 if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
292 return {}; 363 return std::nullopt;
293 } 364 }
294 365
295 Key128 seed{}; 366 Key128 seed{};
296 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) { 367 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
297 return {}; 368 return std::nullopt;
298 } 369 }
299 return seed; 370 return seed;
300} 371}
301 372
302Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) { 373Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) {
303 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek))) 374 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek))) {
304 return Loader::ResultStatus::ErrorMissingSDKEKSource; 375 return Loader::ResultStatus::ErrorMissingSDKEKSource;
305 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration))) 376 }
377 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration))) {
306 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource; 378 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource;
307 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) 379 }
380 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) {
308 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource; 381 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource;
382 }
309 383
310 const auto sd_kek_source = 384 const auto sd_kek_source =
311 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek)); 385 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek));
@@ -318,14 +392,17 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
318 GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen); 392 GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen);
319 keys.SetKey(S128KeyType::SDKek, sd_kek); 393 keys.SetKey(S128KeyType::SDKek, sd_kek);
320 394
321 if (!keys.HasKey(S128KeyType::SDSeed)) 395 if (!keys.HasKey(S128KeyType::SDSeed)) {
322 return Loader::ResultStatus::ErrorMissingSDSeed; 396 return Loader::ResultStatus::ErrorMissingSDSeed;
397 }
323 const auto sd_seed = keys.GetKey(S128KeyType::SDSeed); 398 const auto sd_seed = keys.GetKey(S128KeyType::SDSeed);
324 399
325 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save))) 400 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save))) {
326 return Loader::ResultStatus::ErrorMissingSDSaveKeySource; 401 return Loader::ResultStatus::ErrorMissingSDSaveKeySource;
327 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA))) 402 }
403 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA))) {
328 return Loader::ResultStatus::ErrorMissingSDNCAKeySource; 404 return Loader::ResultStatus::ErrorMissingSDNCAKeySource;
405 }
329 406
330 std::array<Key256, 2> sd_key_sources{ 407 std::array<Key256, 2> sd_key_sources{
331 keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)), 408 keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)),
@@ -334,8 +411,9 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
334 411
335 // Combine sources and seed 412 // Combine sources and seed
336 for (auto& source : sd_key_sources) { 413 for (auto& source : sd_key_sources) {
337 for (std::size_t i = 0; i < source.size(); ++i) 414 for (std::size_t i = 0; i < source.size(); ++i) {
338 source[i] ^= sd_seed[i & 0xF]; 415 source[i] ^= sd_seed[i & 0xF];
416 }
339 } 417 }
340 418
341 AESCipher<Key128> cipher(sd_kek, Mode::ECB); 419 AESCipher<Key128> cipher(sd_kek, Mode::ECB);
@@ -353,9 +431,10 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
353 return Loader::ResultStatus::Success; 431 return Loader::ResultStatus::Success;
354} 432}
355 433
356std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save) { 434std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save) {
357 if (!ticket_save.IsOpen()) 435 if (!ticket_save.IsOpen()) {
358 return {}; 436 return {};
437 }
359 438
360 std::vector<u8> buffer(ticket_save.GetSize()); 439 std::vector<u8> buffer(ticket_save.GetSize());
361 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) { 440 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
@@ -415,7 +494,7 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
415 offset = i + 1; 494 offset = i + 1;
416 break; 495 break;
417 } else if (data[i] != 0x0) { 496 } else if (data[i] != 0x0) {
418 return {}; 497 return std::nullopt;
419 } 498 }
420 } 499 }
421 500
@@ -425,16 +504,18 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
425std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, 504std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
426 const RSAKeyPair<2048>& key) { 505 const RSAKeyPair<2048>& key) {
427 const auto issuer = ticket.GetData().issuer; 506 const auto issuer = ticket.GetData().issuer;
428 if (IsAllZeroArray(issuer)) 507 if (IsAllZeroArray(issuer)) {
429 return {}; 508 return std::nullopt;
509 }
430 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { 510 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') {
431 LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); 511 LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority.");
432 } 512 }
433 513
434 Key128 rights_id = ticket.GetData().rights_id; 514 Key128 rights_id = ticket.GetData().rights_id;
435 515
436 if (rights_id == Key128{}) 516 if (rights_id == Key128{}) {
437 return {}; 517 return std::nullopt;
518 }
438 519
439 if (!std::any_of(ticket.GetData().title_key_common_pad.begin(), 520 if (!std::any_of(ticket.GetData().title_key_common_pad.begin(),
440 ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) { 521 ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) {
@@ -466,15 +547,17 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
466 std::array<u8, 0xDF> m_2; 547 std::array<u8, 0xDF> m_2;
467 std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size()); 548 std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
468 549
469 if (m_0 != 0) 550 if (m_0 != 0) {
470 return {}; 551 return std::nullopt;
552 }
471 553
472 m_1 = m_1 ^ MGF1<0x20>(m_2); 554 m_1 = m_1 ^ MGF1<0x20>(m_2);
473 m_2 = m_2 ^ MGF1<0xDF>(m_1); 555 m_2 = m_2 ^ MGF1<0xDF>(m_1);
474 556
475 const auto offset = FindTicketOffset(m_2); 557 const auto offset = FindTicketOffset(m_2);
476 if (!offset) 558 if (!offset) {
477 return {}; 559 return std::nullopt;
560 }
478 ASSERT(*offset > 0); 561 ASSERT(*offset > 0);
479 562
480 Key128 key_temp{}; 563 Key128 key_temp{};
@@ -485,8 +568,8 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
485 568
486KeyManager::KeyManager() { 569KeyManager::KeyManager() {
487 // Initialize keys 570 // Initialize keys
488 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); 571 const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
489 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 572 const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
490 if (Settings::values.use_dev_keys) { 573 if (Settings::values.use_dev_keys) {
491 dev_mode = true; 574 dev_mode = true;
492 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false); 575 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);
@@ -504,34 +587,39 @@ KeyManager::KeyManager() {
504} 587}
505 588
506static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) { 589static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
507 if (base.size() < begin + length) 590 if (base.size() < begin + length) {
508 return false; 591 return false;
592 }
509 return std::all_of(base.begin() + begin, base.begin() + begin + length, 593 return std::all_of(base.begin() + begin, base.begin() + begin + length,
510 [](u8 c) { return std::isxdigit(c); }); 594 [](u8 c) { return std::isxdigit(c); });
511} 595}
512 596
513void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { 597void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
514 std::ifstream file; 598 std::ifstream file;
515 OpenFStream(file, filename, std::ios_base::in); 599 Common::FS::OpenFStream(file, filename, std::ios_base::in);
516 if (!file.is_open()) 600 if (!file.is_open()) {
517 return; 601 return;
602 }
518 603
519 std::string line; 604 std::string line;
520 while (std::getline(file, line)) { 605 while (std::getline(file, line)) {
521 std::vector<std::string> out; 606 std::vector<std::string> out;
522 std::stringstream stream(line); 607 std::stringstream stream(line);
523 std::string item; 608 std::string item;
524 while (std::getline(stream, item, '=')) 609 while (std::getline(stream, item, '=')) {
525 out.push_back(std::move(item)); 610 out.push_back(std::move(item));
611 }
526 612
527 if (out.size() != 2) 613 if (out.size() != 2) {
528 continue; 614 continue;
615 }
529 616
530 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end()); 617 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
531 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end()); 618 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
532 619
533 if (out[0].compare(0, 1, "#") == 0) 620 if (out[0].compare(0, 1, "#") == 0) {
534 continue; 621 continue;
622 }
535 623
536 if (is_title_keys) { 624 if (is_title_keys) {
537 auto rights_id_raw = Common::HexStringToArray<16>(out[0]); 625 auto rights_id_raw = Common::HexStringToArray<16>(out[0]);
@@ -541,24 +629,26 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
541 s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key; 629 s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
542 } else { 630 } else {
543 out[0] = Common::ToLower(out[0]); 631 out[0] = Common::ToLower(out[0]);
544 if (s128_file_id.find(out[0]) != s128_file_id.end()) { 632 if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
545 const auto index = s128_file_id.at(out[0]); 633 const auto& index = iter128->second;
546 Key128 key = Common::HexStringToArray<16>(out[1]); 634 const Key128 key = Common::HexStringToArray<16>(out[1]);
547 s128_keys[{index.type, index.field1, index.field2}] = key; 635 s128_keys[{index.type, index.field1, index.field2}] = key;
548 } else if (s256_file_id.find(out[0]) != s256_file_id.end()) { 636 } else if (const auto iter256 = Find256ByName(out[0]); iter256 != s256_file_id.end()) {
549 const auto index = s256_file_id.at(out[0]); 637 const auto& index = iter256->second;
550 Key256 key = Common::HexStringToArray<32>(out[1]); 638 const Key256 key = Common::HexStringToArray<32>(out[1]);
551 s256_keys[{index.type, index.field1, index.field2}] = key; 639 s256_keys[{index.type, index.field1, index.field2}] = key;
552 } else if (out[0].compare(0, 8, "keyblob_") == 0 && 640 } else if (out[0].compare(0, 8, "keyblob_") == 0 &&
553 out[0].compare(0, 9, "keyblob_k") != 0) { 641 out[0].compare(0, 9, "keyblob_k") != 0) {
554 if (!ValidCryptoRevisionString(out[0], 8, 2)) 642 if (!ValidCryptoRevisionString(out[0], 8, 2)) {
555 continue; 643 continue;
644 }
556 645
557 const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16); 646 const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16);
558 keyblobs[index] = Common::HexStringToArray<0x90>(out[1]); 647 keyblobs[index] = Common::HexStringToArray<0x90>(out[1]);
559 } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) { 648 } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) {
560 if (!ValidCryptoRevisionString(out[0], 18, 2)) 649 if (!ValidCryptoRevisionString(out[0], 18, 2)) {
561 continue; 650 continue;
651 }
562 652
563 const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16); 653 const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16);
564 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); 654 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
@@ -566,8 +656,9 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
566 eticket_extended_kek = Common::HexStringToArray<576>(out[1]); 656 eticket_extended_kek = Common::HexStringToArray<576>(out[1]);
567 } else { 657 } else {
568 for (const auto& kv : KEYS_VARIABLE_LENGTH) { 658 for (const auto& kv : KEYS_VARIABLE_LENGTH) {
569 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) 659 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) {
570 continue; 660 continue;
661 }
571 if (out[0].compare(0, kv.second.size(), kv.second) == 0) { 662 if (out[0].compare(0, kv.second.size(), kv.second) == 0) {
572 const auto index = 663 const auto index =
573 std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16); 664 std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16);
@@ -602,10 +693,11 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
602 693
603void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, 694void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
604 const std::string& filename, bool title) { 695 const std::string& filename, bool title) {
605 if (FileUtil::Exists(dir1 + DIR_SEP + filename)) 696 if (Common::FS::Exists(dir1 + DIR_SEP + filename)) {
606 LoadFromFile(dir1 + DIR_SEP + filename, title); 697 LoadFromFile(dir1 + DIR_SEP + filename, title);
607 else if (FileUtil::Exists(dir2 + DIR_SEP + filename)) 698 } else if (Common::FS::Exists(dir2 + DIR_SEP + filename)) {
608 LoadFromFile(dir2 + DIR_SEP + filename, title); 699 LoadFromFile(dir2 + DIR_SEP + filename, title);
700 }
609} 701}
610 702
611bool KeyManager::BaseDeriveNecessary() const { 703bool KeyManager::BaseDeriveNecessary() const {
@@ -613,8 +705,9 @@ bool KeyManager::BaseDeriveNecessary() const {
613 return !HasKey(key_type, index1, index2); 705 return !HasKey(key_type, index1, index2);
614 }; 706 };
615 707
616 if (check_key_existence(S256KeyType::Header)) 708 if (check_key_existence(S256KeyType::Header)) {
617 return true; 709 return true;
710 }
618 711
619 for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) { 712 for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) {
620 if (check_key_existence(S128KeyType::Master, i) || 713 if (check_key_existence(S128KeyType::Master, i) ||
@@ -639,14 +732,16 @@ bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) const {
639} 732}
640 733
641Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const { 734Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const {
642 if (!HasKey(id, field1, field2)) 735 if (!HasKey(id, field1, field2)) {
643 return {}; 736 return {};
737 }
644 return s128_keys.at({id, field1, field2}); 738 return s128_keys.at({id, field1, field2});
645} 739}
646 740
647Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const { 741Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
648 if (!HasKey(id, field1, field2)) 742 if (!HasKey(id, field1, field2)) {
649 return {}; 743 return {};
744 }
650 return s256_keys.at({id, field1, field2}); 745 return s256_keys.at({id, field1, field2});
651} 746}
652 747
@@ -668,7 +763,7 @@ Key256 KeyManager::GetBISKey(u8 partition_id) const {
668template <size_t Size> 763template <size_t Size>
669void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname, 764void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
670 const std::array<u8, Size>& key) { 765 const std::array<u8, Size>& key) {
671 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 766 const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
672 std::string filename = "title.keys_autogenerated"; 767 std::string filename = "title.keys_autogenerated";
673 if (category == KeyCategory::Standard) { 768 if (category == KeyCategory::Standard) {
674 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated"; 769 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
@@ -677,9 +772,9 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
677 } 772 }
678 773
679 const auto path = yuzu_keys_dir + DIR_SEP + filename; 774 const auto path = yuzu_keys_dir + DIR_SEP + filename;
680 const auto add_info_text = !FileUtil::Exists(path); 775 const auto add_info_text = !Common::FS::Exists(path);
681 FileUtil::CreateFullPath(path); 776 Common::FS::CreateFullPath(path);
682 FileUtil::IOFile file{path, "a"}; 777 Common::FS::IOFile file{path, "a"};
683 if (!file.IsOpen()) { 778 if (!file.IsOpen()) {
684 return; 779 return;
685 } 780 }
@@ -712,8 +807,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
712 } 807 }
713 808
714 const auto iter2 = std::find_if( 809 const auto iter2 = std::find_if(
715 s128_file_id.begin(), s128_file_id.end(), 810 s128_file_id.begin(), s128_file_id.end(), [&id, &field1, &field2](const auto& elem) {
716 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
717 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == 811 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
718 std::tie(id, field1, field2); 812 std::tie(id, field1, field2);
719 }); 813 });
@@ -723,9 +817,11 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
723 817
724 // Variable cases 818 // Variable cases
725 if (id == S128KeyType::KeyArea) { 819 if (id == S128KeyType::KeyArea) {
726 static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}", 820 static constexpr std::array<const char*, 3> kak_names = {
727 "key_area_key_ocean_{:02X}", 821 "key_area_key_application_{:02X}",
728 "key_area_key_system_{:02X}"}; 822 "key_area_key_ocean_{:02X}",
823 "key_area_key_system_{:02X}",
824 };
729 WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key); 825 WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key);
730 } else if (id == S128KeyType::Master) { 826 } else if (id == S128KeyType::Master) {
731 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key); 827 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
@@ -751,8 +847,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
751 return; 847 return;
752 } 848 }
753 const auto iter = std::find_if( 849 const auto iter = std::find_if(
754 s256_file_id.begin(), s256_file_id.end(), 850 s256_file_id.begin(), s256_file_id.end(), [&id, &field1, &field2](const auto& elem) {
755 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
756 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == 851 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
757 std::tie(id, field1, field2); 852 std::tie(id, field1, field2);
758 }); 853 });
@@ -763,29 +858,31 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
763} 858}
764 859
765bool KeyManager::KeyFileExists(bool title) { 860bool KeyManager::KeyFileExists(bool title) {
766 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); 861 const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
767 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 862 const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
768 if (title) { 863 if (title) {
769 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "title.keys") || 864 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "title.keys") ||
770 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "title.keys"); 865 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "title.keys");
771 } 866 }
772 867
773 if (Settings::values.use_dev_keys) { 868 if (Settings::values.use_dev_keys) {
774 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") || 869 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") ||
775 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys"); 870 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys");
776 } 871 }
777 872
778 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") || 873 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") ||
779 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys"); 874 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys");
780} 875}
781 876
782void KeyManager::DeriveSDSeedLazy() { 877void KeyManager::DeriveSDSeedLazy() {
783 if (HasKey(S128KeyType::SDSeed)) 878 if (HasKey(S128KeyType::SDSeed)) {
784 return; 879 return;
880 }
785 881
786 const auto res = DeriveSDSeed(); 882 const auto res = DeriveSDSeed();
787 if (res) 883 if (res) {
788 SetKey(S128KeyType::SDSeed, *res); 884 SetKey(S128KeyType::SDSeed, *res);
885 }
789} 886}
790 887
791static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) { 888static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
@@ -797,11 +894,13 @@ static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
797} 894}
798 895
799void KeyManager::DeriveBase() { 896void KeyManager::DeriveBase() {
800 if (!BaseDeriveNecessary()) 897 if (!BaseDeriveNecessary()) {
801 return; 898 return;
899 }
802 900
803 if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC)) 901 if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC)) {
804 return; 902 return;
903 }
805 904
806 const auto has_bis = [this](u64 id) { 905 const auto has_bis = [this](u64 id) {
807 return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) && 906 return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) &&
@@ -818,10 +917,11 @@ void KeyManager::DeriveBase() {
818 static_cast<u64>(BISKeyType::Tweak)); 917 static_cast<u64>(BISKeyType::Tweak));
819 }; 918 };
820 919
821 if (has_bis(2) && !has_bis(3)) 920 if (has_bis(2) && !has_bis(3)) {
822 copy_bis(2, 3); 921 copy_bis(2, 3);
823 else if (has_bis(3) && !has_bis(2)) 922 } else if (has_bis(3) && !has_bis(2)) {
824 copy_bis(3, 2); 923 copy_bis(3, 2);
924 }
825 925
826 std::bitset<32> revisions(0xFFFFFFFF); 926 std::bitset<32> revisions(0xFFFFFFFF);
827 for (size_t i = 0; i < revisions.size(); ++i) { 927 for (size_t i = 0; i < revisions.size(); ++i) {
@@ -831,15 +931,17 @@ void KeyManager::DeriveBase() {
831 } 931 }
832 } 932 }
833 933
834 if (!revisions.any()) 934 if (!revisions.any()) {
835 return; 935 return;
936 }
836 937
837 const auto sbk = GetKey(S128KeyType::SecureBoot); 938 const auto sbk = GetKey(S128KeyType::SecureBoot);
838 const auto tsec = GetKey(S128KeyType::TSEC); 939 const auto tsec = GetKey(S128KeyType::TSEC);
839 940
840 for (size_t i = 0; i < revisions.size(); ++i) { 941 for (size_t i = 0; i < revisions.size(); ++i) {
841 if (!revisions[i]) 942 if (!revisions[i]) {
842 continue; 943 continue;
944 }
843 945
844 // Derive keyblob key 946 // Derive keyblob key
845 const auto key = DeriveKeyblobKey( 947 const auto key = DeriveKeyblobKey(
@@ -848,16 +950,18 @@ void KeyManager::DeriveBase() {
848 SetKey(S128KeyType::Keyblob, key, i); 950 SetKey(S128KeyType::Keyblob, key, i);
849 951
850 // Derive keyblob MAC key 952 // Derive keyblob MAC key
851 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))) 953 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))) {
852 continue; 954 continue;
955 }
853 956
854 const auto mac_key = DeriveKeyblobMACKey( 957 const auto mac_key = DeriveKeyblobMACKey(
855 key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))); 958 key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)));
856 SetKey(S128KeyType::KeyblobMAC, mac_key, i); 959 SetKey(S128KeyType::KeyblobMAC, mac_key, i);
857 960
858 Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key); 961 Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key);
859 if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0) 962 if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0) {
860 continue; 963 continue;
964 }
861 965
862 // Decrypt keyblob 966 // Decrypt keyblob
863 if (keyblobs[i] == std::array<u8, 0x90>{}) { 967 if (keyblobs[i] == std::array<u8, 0x90>{}) {
@@ -881,16 +985,19 @@ void KeyManager::DeriveBase() {
881 985
882 revisions.set(); 986 revisions.set();
883 for (size_t i = 0; i < revisions.size(); ++i) { 987 for (size_t i = 0; i < revisions.size(); ++i) {
884 if (!HasKey(S128KeyType::Master, i)) 988 if (!HasKey(S128KeyType::Master, i)) {
885 revisions.reset(i); 989 revisions.reset(i);
990 }
886 } 991 }
887 992
888 if (!revisions.any()) 993 if (!revisions.any()) {
889 return; 994 return;
995 }
890 996
891 for (size_t i = 0; i < revisions.size(); ++i) { 997 for (size_t i = 0; i < revisions.size(); ++i) {
892 if (!revisions[i]) 998 if (!revisions[i]) {
893 continue; 999 continue;
1000 }
894 1001
895 // Derive general purpose keys 1002 // Derive general purpose keys
896 DeriveGeneralPurposeKeys(i); 1003 DeriveGeneralPurposeKeys(i);
@@ -920,16 +1027,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
920 const auto es = Core::System::GetInstance().GetContentProvider().GetEntry( 1027 const auto es = Core::System::GetInstance().GetContentProvider().GetEntry(
921 0x0100000000000033, FileSys::ContentRecordType::Program); 1028 0x0100000000000033, FileSys::ContentRecordType::Program);
922 1029
923 if (es == nullptr) 1030 if (es == nullptr) {
924 return; 1031 return;
1032 }
925 1033
926 const auto exefs = es->GetExeFS(); 1034 const auto exefs = es->GetExeFS();
927 if (exefs == nullptr) 1035 if (exefs == nullptr) {
928 return; 1036 return;
1037 }
929 1038
930 const auto main = exefs->GetFile("main"); 1039 const auto main = exefs->GetFile("main");
931 if (main == nullptr) 1040 if (main == nullptr) {
932 return; 1041 return;
1042 }
933 1043
934 const auto bytes = main->ReadAllBytes(); 1044 const auto bytes = main->ReadAllBytes();
935 1045
@@ -939,16 +1049,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
939 const auto seed3 = data.GetRSAKekSeed3(); 1049 const auto seed3 = data.GetRSAKekSeed3();
940 const auto mask0 = data.GetRSAKekMask0(); 1050 const auto mask0 = data.GetRSAKekMask0();
941 1051
942 if (eticket_kek != Key128{}) 1052 if (eticket_kek != Key128{}) {
943 SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek)); 1053 SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek));
1054 }
944 if (eticket_kekek != Key128{}) { 1055 if (eticket_kekek != Key128{}) {
945 SetKey(S128KeyType::Source, eticket_kekek, 1056 SetKey(S128KeyType::Source, eticket_kekek,
946 static_cast<size_t>(SourceKeyType::ETicketKekek)); 1057 static_cast<size_t>(SourceKeyType::ETicketKekek));
947 } 1058 }
948 if (seed3 != Key128{}) 1059 if (seed3 != Key128{}) {
949 SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3)); 1060 SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3));
950 if (mask0 != Key128{}) 1061 }
1062 if (mask0 != Key128{}) {
951 SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0)); 1063 SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0));
1064 }
952 if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} || 1065 if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} ||
953 mask0 == Key128{}) { 1066 mask0 == Key128{}) {
954 return; 1067 return;
@@ -974,8 +1087,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
974 AESCipher<Key128> es_kek(temp_kekek, Mode::ECB); 1087 AESCipher<Key128> es_kek(temp_kekek, Mode::ECB);
975 es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt); 1088 es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt);
976 1089
977 if (eticket_final == Key128{}) 1090 if (eticket_final == Key128{}) {
978 return; 1091 return;
1092 }
979 1093
980 SetKey(S128KeyType::ETicketRSAKek, eticket_final); 1094 SetKey(S128KeyType::ETicketRSAKek, eticket_final);
981 1095
@@ -990,18 +1104,20 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
990void KeyManager::PopulateTickets() { 1104void KeyManager::PopulateTickets() {
991 const auto rsa_key = GetETicketRSAKey(); 1105 const auto rsa_key = GetETicketRSAKey();
992 1106
993 if (rsa_key == RSAKeyPair<2048>{}) 1107 if (rsa_key == RSAKeyPair<2048>{}) {
994 return; 1108 return;
1109 }
995 1110
996 if (!common_tickets.empty() && !personal_tickets.empty()) 1111 if (!common_tickets.empty() && !personal_tickets.empty()) {
997 return; 1112 return;
1113 }
998 1114
999 const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1115 const Common::FS::IOFile save1(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1000 "/system/save/80000000000000e1", 1116 "/system/save/80000000000000e1",
1001 "rb+"); 1117 "rb+");
1002 const FileUtil::IOFile save2(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1118 const Common::FS::IOFile save2(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1003 "/system/save/80000000000000e2", 1119 "/system/save/80000000000000e2",
1004 "rb+"); 1120 "rb+");
1005 1121
1006 const auto blob2 = GetTicketblob(save2); 1122 const auto blob2 = GetTicketblob(save2);
1007 auto res = GetTicketblob(save1); 1123 auto res = GetTicketblob(save1);
@@ -1011,8 +1127,10 @@ void KeyManager::PopulateTickets() {
1011 for (std::size_t i = 0; i < res.size(); ++i) { 1127 for (std::size_t i = 0; i < res.size(); ++i) {
1012 const auto common = i < idx; 1128 const auto common = i < idx;
1013 const auto pair = ParseTicket(res[i], rsa_key); 1129 const auto pair = ParseTicket(res[i], rsa_key);
1014 if (!pair) 1130 if (!pair) {
1015 continue; 1131 continue;
1132 }
1133
1016 const auto& [rid, key] = *pair; 1134 const auto& [rid, key] = *pair;
1017 u128 rights_id; 1135 u128 rights_id;
1018 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1136 std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1041,27 +1159,33 @@ void KeyManager::SynthesizeTickets() {
1041} 1159}
1042 1160
1043void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) { 1161void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
1044 if (key == Key128{}) 1162 if (key == Key128{}) {
1045 return; 1163 return;
1164 }
1046 SetKey(id, key, field1, field2); 1165 SetKey(id, key, field1, field2);
1047} 1166}
1048 1167
1049void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) { 1168void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) {
1050 if (key == Key256{}) 1169 if (key == Key256{}) {
1051 return; 1170 return;
1171 }
1172
1052 SetKey(id, key, field1, field2); 1173 SetKey(id, key, field1, field2);
1053} 1174}
1054 1175
1055void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) { 1176void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1056 if (!BaseDeriveNecessary()) 1177 if (!BaseDeriveNecessary()) {
1057 return; 1178 return;
1179 }
1058 1180
1059 if (!data.HasBoot0()) 1181 if (!data.HasBoot0()) {
1060 return; 1182 return;
1183 }
1061 1184
1062 for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) { 1185 for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) {
1063 if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{}) 1186 if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{}) {
1064 continue; 1187 continue;
1188 }
1065 encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i); 1189 encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i);
1066 WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i), 1190 WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i),
1067 encrypted_keyblobs[i]); 1191 encrypted_keyblobs[i]);
@@ -1083,8 +1207,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1083 static_cast<u64>(SourceKeyType::Keyblob), i); 1207 static_cast<u64>(SourceKeyType::Keyblob), i);
1084 } 1208 }
1085 1209
1086 if (data.HasFuses()) 1210 if (data.HasFuses()) {
1087 SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey()); 1211 SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey());
1212 }
1088 1213
1089 DeriveBase(); 1214 DeriveBase();
1090 1215
@@ -1098,8 +1223,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1098 1223
1099 const auto masters = data.GetTZMasterKeys(latest_master); 1224 const auto masters = data.GetTZMasterKeys(latest_master);
1100 for (size_t i = 0; i < masters.size(); ++i) { 1225 for (size_t i = 0; i < masters.size(); ++i) {
1101 if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i)) 1226 if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i)) {
1102 SetKey(S128KeyType::Master, masters[i], i); 1227 SetKey(S128KeyType::Master, masters[i], i);
1228 }
1103 } 1229 }
1104 1230
1105 DeriveBase(); 1231 DeriveBase();
@@ -1109,8 +1235,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1109 1235
1110 std::array<Key128, 0x20> package2_keys{}; 1236 std::array<Key128, 0x20> package2_keys{};
1111 for (size_t i = 0; i < package2_keys.size(); ++i) { 1237 for (size_t i = 0; i < package2_keys.size(); ++i) {
1112 if (HasKey(S128KeyType::Package2, i)) 1238 if (HasKey(S128KeyType::Package2, i)) {
1113 package2_keys[i] = GetKey(S128KeyType::Package2, i); 1239 package2_keys[i] = GetKey(S128KeyType::Package2, i);
1240 }
1114 } 1241 }
1115 data.DecryptPackage2(package2_keys, Package2Type::NormalMain); 1242 data.DecryptPackage2(package2_keys, Package2Type::NormalMain);
1116 1243
@@ -1148,12 +1275,15 @@ const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const {
1148 1275
1149bool KeyManager::AddTicketCommon(Ticket raw) { 1276bool KeyManager::AddTicketCommon(Ticket raw) {
1150 const auto rsa_key = GetETicketRSAKey(); 1277 const auto rsa_key = GetETicketRSAKey();
1151 if (rsa_key == RSAKeyPair<2048>{}) 1278 if (rsa_key == RSAKeyPair<2048>{}) {
1152 return false; 1279 return false;
1280 }
1153 1281
1154 const auto pair = ParseTicket(raw, rsa_key); 1282 const auto pair = ParseTicket(raw, rsa_key);
1155 if (!pair) 1283 if (!pair) {
1156 return false; 1284 return false;
1285 }
1286
1157 const auto& [rid, key] = *pair; 1287 const auto& [rid, key] = *pair;
1158 u128 rights_id; 1288 u128 rights_id;
1159 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1289 std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1164,12 +1294,15 @@ bool KeyManager::AddTicketCommon(Ticket raw) {
1164 1294
1165bool KeyManager::AddTicketPersonalized(Ticket raw) { 1295bool KeyManager::AddTicketPersonalized(Ticket raw) {
1166 const auto rsa_key = GetETicketRSAKey(); 1296 const auto rsa_key = GetETicketRSAKey();
1167 if (rsa_key == RSAKeyPair<2048>{}) 1297 if (rsa_key == RSAKeyPair<2048>{}) {
1168 return false; 1298 return false;
1299 }
1169 1300
1170 const auto pair = ParseTicket(raw, rsa_key); 1301 const auto pair = ParseTicket(raw, rsa_key);
1171 if (!pair) 1302 if (!pair) {
1172 return false; 1303 return false;
1304 }
1305
1173 const auto& [rid, key] = *pair; 1306 const auto& [rid, key] = *pair;
1174 u128 rights_id; 1307 u128 rights_id;
1175 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1308 std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1177,58 +1310,4 @@ bool KeyManager::AddTicketPersonalized(Ticket raw) {
1177 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); 1310 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
1178 return true; 1311 return true;
1179} 1312}
1180
1181const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
1182 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
1183 {"eticket_rsa_kek_source",
1184 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
1185 {"eticket_rsa_kekek_source",
1186 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
1187 {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
1188 {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
1189 {"rsa_oaep_kek_generation_source",
1190 {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
1191 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
1192 {"aes_kek_generation_source",
1193 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
1194 {"aes_key_generation_source",
1195 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
1196 {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
1197 {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
1198 {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
1199 {"key_area_key_application_source",
1200 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1201 static_cast<u64>(KeyAreaKeyType::Application)}},
1202 {"key_area_key_ocean_source",
1203 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1204 static_cast<u64>(KeyAreaKeyType::Ocean)}},
1205 {"key_area_key_system_source",
1206 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1207 static_cast<u64>(KeyAreaKeyType::System)}},
1208 {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
1209 {"keyblob_mac_key_source",
1210 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},
1211 {"tsec_key", {S128KeyType::TSEC, 0, 0}},
1212 {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
1213 {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
1214 {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
1215 {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
1216 {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
1217 {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
1218 {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
1219 {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
1220 {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
1221 {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
1222 {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
1223 {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
1224};
1225
1226const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
1227 {"header_key", {S256KeyType::Header, 0, 0}},
1228 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
1229 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
1230 {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
1231 {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
1232 {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
1233};
1234} // namespace Core::Crypto 1313} // namespace Core::Crypto
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 9269a73f2..321b75323 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -10,14 +10,13 @@
10#include <string> 10#include <string>
11 11
12#include <variant> 12#include <variant>
13#include <boost/container/flat_map.hpp>
14#include <fmt/format.h> 13#include <fmt/format.h>
15#include "common/common_funcs.h" 14#include "common/common_funcs.h"
16#include "common/common_types.h" 15#include "common/common_types.h"
17#include "core/crypto/partition_data_manager.h" 16#include "core/crypto/partition_data_manager.h"
18#include "core/file_sys/vfs_types.h" 17#include "core/file_sys/vfs_types.h"
19 18
20namespace FileUtil { 19namespace Common::FS {
21class IOFile; 20class IOFile;
22} 21}
23 22
@@ -293,9 +292,6 @@ private:
293 292
294 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); 293 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
295 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); 294 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
296
297 static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
298 static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
299}; 295};
300 296
301Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); 297Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
@@ -308,7 +304,7 @@ std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblo
308std::optional<Key128> DeriveSDSeed(); 304std::optional<Key128> DeriveSDSeed();
309Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys); 305Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
310 306
311std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save); 307std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save);
312 308
313// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority 309// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority
314// (offset 0x140-0x144 is zero) 310// (offset 0x140-0x144 is zero)
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index b31a81560..46136d04a 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -27,7 +27,7 @@
27#include "core/file_sys/vfs_offset.h" 27#include "core/file_sys/vfs_offset.h"
28#include "core/file_sys/vfs_vector.h" 28#include "core/file_sys/vfs_vector.h"
29 29
30using namespace Common; 30using Common::AsArray;
31 31
32namespace Core::Crypto { 32namespace Core::Crypto {
33 33
@@ -47,105 +47,123 @@ struct Package2Header {
47}; 47};
48static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); 48static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
49 49
50const std::array<SHA256Hash, 0x10> source_hashes{ 50// clang-format off
51 "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source 51constexpr std::array source_hashes{
52 "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source 52 AsArray("B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"), // keyblob_mac_key_source
53 "21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source 53 AsArray("7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"), // master_key_source
54 "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source 54 AsArray("21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"), // package2_key_source
55 "FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source 55 AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // aes_kek_generation_source
56 "C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source 56 AsArray("FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"), // aes_key_generation_source
57 "04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source 57 AsArray("C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"), // titlekek_source
58 "FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source 58 AsArray("04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"), // key_area_key_application_source
59 "1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source 59 AsArray("FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"), // key_area_key_ocean_source
60 "6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source 60 AsArray("1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"), // key_area_key_system_source
61 "D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source 61 AsArray("6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"), // sd_card_kek_source
62 "2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source 62 AsArray("D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"), // sd_card_save_key_source
63 "1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source 63 AsArray("2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"), // sd_card_nca_key_source
64 "8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source 64 AsArray("1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"), // header_kek_source
65 "D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3 65 AsArray("8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"), // header_key_source
66 "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0 66 AsArray("D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"), // rsa_kek_seed3
67 AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // rsa_kek_mask0
67}; 68};
68 69// clang-format on
69const std::array<SHA256Hash, 0x20> keyblob_source_hashes{ 70
70 "8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00 71// clang-format off
71 "2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01 72constexpr std::array keyblob_source_hashes{
72 "61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02 73 AsArray("8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"), // keyblob_key_source_00
73 "8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03 74 AsArray("2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"), // keyblob_key_source_01
74 "95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04 75 AsArray("61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"), // keyblob_key_source_02
75 "3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05 76 AsArray("8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"), // keyblob_key_source_03
76 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06 77 AsArray("95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"), // keyblob_key_source_04
77 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07 78 AsArray("3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"), // keyblob_key_source_05
78 79 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_06
79 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08 80 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_07
80 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09 81
81 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A 82 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_08
82 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B 83 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_09
83 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C 84 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0A
84 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D 85 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0B
85 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E 86 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0C
86 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F 87 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0D
87 88 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0E
88 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10 89 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0F
89 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11 90
90 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12 91 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_10
91 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13 92 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_11
92 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14 93 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_12
93 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15 94 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_13
94 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16 95 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_14
95 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17 96 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_15
96 97 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_16
97 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18 98 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_17
98 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19 99
99 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A 100 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_18
100 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B 101 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_19
101 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C 102 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1A
102 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D 103 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1B
103 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E 104 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1C
104 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F 105 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1D
106 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1E
107 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1F
105}; 108};
106 109// clang-format on
107const std::array<SHA256Hash, 0x20> master_key_hashes{ 110
108 "0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00 111// clang-format off
109 "4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01 112constexpr std::array master_key_hashes{
110 "79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02 113 AsArray("0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"), // master_key_00
111 "4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03 114 AsArray("4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"), // master_key_01
112 "75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04 115 AsArray("79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"), // master_key_02
113 "EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05 116 AsArray("4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"), // master_key_03
114 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06 117 AsArray("75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"), // master_key_04
115 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07 118 AsArray("EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"), // master_key_05
116 119 AsArray("9497E6779F5D840F2BBA1DE4E95BA1D6F21EFC94717D5AE5CA37D7EC5BD37A19"), // master_key_06
117 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08 120 AsArray("4EC96B8CB01B8DCE382149443430B2B6EBCB2983348AFA04A25E53609DABEDF6"), // master_key_07
118 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09 121
119 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A 122 AsArray("2998E2E23609BC2675FF062A2D64AF5B1B78DFF463B24119D64A1B64F01B2D51"), // master_key_08
120 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B 123 AsArray("9D486A98067C44B37CF173D3BF577891EB6081FF6B4A166347D9DBBF7025076B"), // master_key_09
121 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C 124 AsArray("4EC5A237A75A083A9C5F6CF615601522A7F822D06BD4BA32612C9CEBBB29BD45"), // master_key_0A
122 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D 125 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0B
123 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E 126 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0C
124 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F 127 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0D
125 128 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0E
126 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10 129 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0F
127 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11 130
128 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12 131 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_10
129 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13 132 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_11
130 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14 133 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_12
131 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15 134 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_13
132 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16 135 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_14
133 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17 136 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_15
134 137 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_16
135 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18 138 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_17
136 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19 139
137 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A 140 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_18
138 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B 141 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_19
139 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C 142 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1A
140 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D 143 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1B
141 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E 144 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1C
142 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F 145 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1D
146 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1E
147 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1F
143}; 148};
149// clang-format on
150
151static constexpr u8 CalculateMaxKeyblobSourceHash() {
152 const auto is_zero = [](const auto& data) {
153 // TODO: Replace with std::all_of whenever mingw decides to update their
154 // libraries to include the constexpr variant of it.
155 for (const auto element : data) {
156 if (element != 0) {
157 return false;
158 }
159 }
160 return true;
161 };
144 162
145static u8 CalculateMaxKeyblobSourceHash() {
146 for (s8 i = 0x1F; i >= 0; --i) { 163 for (s8 i = 0x1F; i >= 0; --i) {
147 if (keyblob_source_hashes[i] != SHA256Hash{}) 164 if (!is_zero(keyblob_source_hashes[i])) {
148 return static_cast<u8>(i + 1); 165 return static_cast<u8>(i + 1);
166 }
149 } 167 }
150 168
151 return 0; 169 return 0;
@@ -349,8 +367,8 @@ static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header
349 Package2Header temp = header; 367 Package2Header temp = header;
350 AESCipher<Key128> cipher(key, Mode::CTR); 368 AESCipher<Key128> cipher(key, Mode::CTR);
351 cipher.SetIV(header.header_ctr); 369 cipher.SetIV(header.header_ctr);
352 cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr, 370 cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - sizeof(Package2Header::signature),
353 Op::Decrypt); 371 &temp.header_ctr, Op::Decrypt);
354 if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) { 372 if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
355 header = temp; 373 header = temp;
356 return true; 374 return true;
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 285277ef8..9ffda2e14 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -86,7 +86,7 @@ VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
86 auto& keys = Core::Crypto::KeyManager::Instance(); 86 auto& keys = Core::Crypto::KeyManager::Instance();
87 Core::Crypto::PartitionDataManager pdm{ 87 Core::Crypto::PartitionDataManager pdm{
88 Core::System::GetInstance().GetFilesystem()->OpenDirectory( 88 Core::System::GetInstance().GetFilesystem()->OpenDirectory(
89 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; 89 Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), Mode::Read)};
90 keys.PopulateFromPartitionData(pdm); 90 keys.PopulateFromPartitionData(pdm);
91 91
92 switch (id) { 92 switch (id) {
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index f831487dd..da01002d5 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -257,8 +257,7 @@ std::vector<NcaID> PlaceholderCache::List() const {
257 for (const auto& sdir : dir->GetSubdirectories()) { 257 for (const auto& sdir : dir->GetSubdirectories()) {
258 for (const auto& file : sdir->GetFiles()) { 258 for (const auto& file : sdir->GetFiles()) {
259 const auto name = file->GetName(); 259 const auto name = file->GetName();
260 if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' && 260 if (name.length() == 36 && name.ends_with(".nca")) {
261 name[35] == 'a') {
262 out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32))); 261 out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32)));
263 } 262 }
264 } 263 }
@@ -621,25 +620,25 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
621 620
622InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, 621InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
623 bool overwrite_if_exists, const VfsCopyFunction& copy) { 622 bool overwrite_if_exists, const VfsCopyFunction& copy) {
624 CNMTHeader header{ 623 const CNMTHeader header{
625 nca.GetTitleId(), // Title ID 624 .title_id = nca.GetTitleId(),
626 0, // Ignore/Default title version 625 .title_version = 0,
627 type, // Type 626 .type = type,
628 {}, // Padding 627 .reserved = {},
629 0x10, // Default table offset 628 .table_offset = 0x10,
630 1, // 1 Content Entry 629 .number_content_entries = 1,
631 0, // No Meta Entries 630 .number_meta_entries = 0,
632 {}, // Padding 631 .attributes = 0,
633 {}, // Reserved 1 632 .reserved2 = {},
634 0, // Is committed 633 .is_committed = 0,
635 0, // Required download system version 634 .required_download_system_version = 0,
636 {}, // Reserved 2 635 .reserved3 = {},
637 }; 636 };
638 OptionalHeader opt_header{0, 0}; 637 const OptionalHeader opt_header{0, 0};
639 ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}}; 638 ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}};
640 const auto& data = nca.GetBaseFile()->ReadBytes(0x100000); 639 const auto& data = nca.GetBaseFile()->ReadBytes(0x100000);
641 mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0); 640 mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0);
642 memcpy(&c_rec.nca_id, &c_rec.hash, 16); 641 std::memcpy(&c_rec.nca_id, &c_rec.hash, 16);
643 const CNMT new_cnmt(header, opt_header, {c_rec}, {}); 642 const CNMT new_cnmt(header, opt_header, {c_rec}, {});
644 if (!RawInstallYuzuMeta(new_cnmt)) { 643 if (!RawInstallYuzuMeta(new_cnmt)) {
645 return InstallResult::ErrorMetaFailed; 644 return InstallResult::ErrorMetaFailed;
@@ -728,7 +727,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
728 LOG_WARNING(Loader, "Overwriting existing NCA..."); 727 LOG_WARNING(Loader, "Overwriting existing NCA...");
729 VirtualDir c_dir; 728 VirtualDir c_dir;
730 { c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); } 729 { c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); }
731 c_dir->DeleteFile(FileUtil::GetFilename(path)); 730 c_dir->DeleteFile(Common::FS::GetFilename(path));
732 } 731 }
733 732
734 auto out = dir->CreateFileRelative(path); 733 auto out = dir->CreateFileRelative(path);
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index ec1d54f27..5b414b0f0 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -133,9 +133,9 @@ public:
133 // Parsing function defines the conversion from raw file to NCA. If there are other steps 133 // Parsing function defines the conversion from raw file to NCA. If there are other steps
134 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom 134 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
135 // parsing function. 135 // parsing function.
136 explicit RegisteredCache(VirtualDir dir, 136 explicit RegisteredCache(
137 ContentProviderParsingFunction parsing_function = 137 VirtualDir dir, ContentProviderParsingFunction parsing_function =
138 [](const VirtualFile& file, const NcaID& id) { return file; }); 138 [](const VirtualFile& file, const NcaID& id) { return file; });
139 ~RegisteredCache() override; 139 ~RegisteredCache() override;
140 140
141 void Refresh() override; 141 void Refresh() override;
diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp
index 61bb67945..d65c7d234 100644
--- a/src/core/file_sys/system_archive/mii_model.cpp
+++ b/src/core/file_sys/system_archive/mii_model.cpp
@@ -27,18 +27,12 @@ VirtualDir MiiModel() {
27 auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, 27 auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{},
28 std::vector<VirtualDir>{}, "data"); 28 std::vector<VirtualDir>{}, "data");
29 29
30 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_LINEAR.size()>>( 30 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat"));
31 MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat")); 31 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat"));
32 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_SRGB.size()>>( 32 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat"));
33 MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat")); 33 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat"));
34 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_LINEAR.size()>>( 34 out->AddFile(MakeArrayFile(MiiModelData::SHAPE_HIGH, "ShapeHigh.dat"));
35 MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat")); 35 out->AddFile(MakeArrayFile(MiiModelData::SHAPE_MID, "ShapeMid.dat"));
36 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_SRGB.size()>>(
37 MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat"));
38 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_HIGH.size()>>(
39 MiiModelData::SHAPE_HIGH, "ShapeHigh.dat"));
40 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>(
41 MiiModelData::SHAPE_MID, "ShapeMid.dat"));
42 36
43 return out; 37 return out;
44} 38}
diff --git a/src/core/file_sys/system_archive/ng_word.cpp b/src/core/file_sys/system_archive/ng_word.cpp
index f4443784d..100d3c5db 100644
--- a/src/core/file_sys/system_archive/ng_word.cpp
+++ b/src/core/file_sys/system_archive/ng_word.cpp
@@ -24,19 +24,18 @@ constexpr std::array<u8, 30> WORD_TXT{
24} // namespace NgWord1Data 24} // namespace NgWord1Data
25 25
26VirtualDir NgWord1() { 26VirtualDir NgWord1() {
27 std::vector<VirtualFile> files(NgWord1Data::NUMBER_WORD_TXT_FILES); 27 std::vector<VirtualFile> files;
28 files.reserve(NgWord1Data::NUMBER_WORD_TXT_FILES);
28 29
29 for (std::size_t i = 0; i < files.size(); ++i) { 30 for (std::size_t i = 0; i < files.size(); ++i) {
30 files[i] = std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( 31 files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, fmt::format("{}.txt", i)));
31 NgWord1Data::WORD_TXT, fmt::format("{}.txt", i));
32 } 32 }
33 33
34 files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( 34 files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, "common.txt"));
35 NgWord1Data::WORD_TXT, "common.txt")); 35 files.push_back(MakeArrayFile(NgWord1Data::VERSION_DAT, "version.dat"));
36 files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::VERSION_DAT.size()>>(
37 NgWord1Data::VERSION_DAT, "version.dat"));
38 36
39 return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); 37 return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{},
38 "data");
40} 39}
41 40
42namespace NgWord2Data { 41namespace NgWord2Data {
@@ -55,27 +54,22 @@ constexpr std::array<u8, 0x2C> AC_NX_DATA{
55} // namespace NgWord2Data 54} // namespace NgWord2Data
56 55
57VirtualDir NgWord2() { 56VirtualDir NgWord2() {
58 std::vector<VirtualFile> files(NgWord2Data::NUMBER_AC_NX_FILES * 3); 57 std::vector<VirtualFile> files;
58 files.reserve(NgWord2Data::NUMBER_AC_NX_FILES * 3);
59 59
60 for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) { 60 for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) {
61 files[3 * i] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 61 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i)));
62 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i)); 62 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i)));
63 files[3 * i + 1] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 63 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i)));
64 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i));
65 files[3 * i + 2] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
66 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i));
67 } 64 }
68 65
69 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 66 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b1_nx"));
70 NgWord2Data::AC_NX_DATA, "ac_common_b1_nx")); 67 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b2_nx"));
71 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 68 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx"));
72 NgWord2Data::AC_NX_DATA, "ac_common_b2_nx")); 69 files.push_back(MakeArrayFile(NgWord2Data::VERSION_DAT, "version.dat"));
73 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
74 NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx"));
75 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::VERSION_DAT.size()>>(
76 NgWord2Data::VERSION_DAT, "version.dat"));
77 70
78 return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); 71 return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{},
72 "data");
79} 73}
80 74
81} // namespace FileSys::SystemArchive 75} // namespace FileSys::SystemArchive
diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp
index d1de63f20..8fd005012 100644
--- a/src/core/file_sys/system_archive/time_zone_binary.cpp
+++ b/src/core/file_sys/system_archive/time_zone_binary.cpp
@@ -654,12 +654,13 @@ static VirtualFile GenerateDefaultTimeZoneFile() {
654} 654}
655 655
656VirtualDir TimeZoneBinary() { 656VirtualDir TimeZoneBinary() {
657 const std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>( 657 std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>(
658 std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{}, 658 std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{},
659 "zoneinfo")}; 659 "zoneinfo")};
660 const std::vector<VirtualFile> root_files{ 660 std::vector<VirtualFile> root_files{MakeArrayFile(LOCATION_NAMES, "binaryList.txt")};
661 std::make_shared<ArrayVfsFile<LOCATION_NAMES.size()>>(LOCATION_NAMES, "binaryList.txt")}; 661
662 return std::make_shared<VectorVfsDirectory>(root_files, root_dirs, "data"); 662 return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(root_dirs),
663 "data");
663} 664}
664 665
665} // namespace FileSys::SystemArchive 666} // namespace FileSys::SystemArchive
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index e33327ef0..a4c3f67c4 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -30,7 +30,7 @@ bool VfsFilesystem::IsWritable() const {
30} 30}
31 31
32VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { 32VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
33 const auto path = FileUtil::SanitizePath(path_); 33 const auto path = Common::FS::SanitizePath(path_);
34 if (root->GetFileRelative(path) != nullptr) 34 if (root->GetFileRelative(path) != nullptr)
35 return VfsEntryType::File; 35 return VfsEntryType::File;
36 if (root->GetDirectoryRelative(path) != nullptr) 36 if (root->GetDirectoryRelative(path) != nullptr)
@@ -40,22 +40,22 @@ VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
40} 40}
41 41
42VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { 42VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
43 const auto path = FileUtil::SanitizePath(path_); 43 const auto path = Common::FS::SanitizePath(path_);
44 return root->GetFileRelative(path); 44 return root->GetFileRelative(path);
45} 45}
46 46
47VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { 47VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
48 const auto path = FileUtil::SanitizePath(path_); 48 const auto path = Common::FS::SanitizePath(path_);
49 return root->CreateFileRelative(path); 49 return root->CreateFileRelative(path);
50} 50}
51 51
52VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { 52VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
53 const auto old_path = FileUtil::SanitizePath(old_path_); 53 const auto old_path = Common::FS::SanitizePath(old_path_);
54 const auto new_path = FileUtil::SanitizePath(new_path_); 54 const auto new_path = Common::FS::SanitizePath(new_path_);
55 55
56 // VfsDirectory impls are only required to implement copy across the current directory. 56 // VfsDirectory impls are only required to implement copy across the current directory.
57 if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) { 57 if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) {
58 if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path))) 58 if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path)))
59 return nullptr; 59 return nullptr;
60 return OpenFile(new_path, Mode::ReadWrite); 60 return OpenFile(new_path, Mode::ReadWrite);
61 } 61 }
@@ -76,8 +76,8 @@ VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view
76} 76}
77 77
78VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) { 78VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) {
79 const auto sanitized_old_path = FileUtil::SanitizePath(old_path); 79 const auto sanitized_old_path = Common::FS::SanitizePath(old_path);
80 const auto sanitized_new_path = FileUtil::SanitizePath(new_path); 80 const auto sanitized_new_path = Common::FS::SanitizePath(new_path);
81 81
82 // Again, non-default impls are highly encouraged to provide a more optimized version of this. 82 // Again, non-default impls are highly encouraged to provide a more optimized version of this.
83 auto out = CopyFile(sanitized_old_path, sanitized_new_path); 83 auto out = CopyFile(sanitized_old_path, sanitized_new_path);
@@ -89,26 +89,26 @@ VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view
89} 89}
90 90
91bool VfsFilesystem::DeleteFile(std::string_view path_) { 91bool VfsFilesystem::DeleteFile(std::string_view path_) {
92 const auto path = FileUtil::SanitizePath(path_); 92 const auto path = Common::FS::SanitizePath(path_);
93 auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); 93 auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write);
94 if (parent == nullptr) 94 if (parent == nullptr)
95 return false; 95 return false;
96 return parent->DeleteFile(FileUtil::GetFilename(path)); 96 return parent->DeleteFile(Common::FS::GetFilename(path));
97} 97}
98 98
99VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { 99VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
100 const auto path = FileUtil::SanitizePath(path_); 100 const auto path = Common::FS::SanitizePath(path_);
101 return root->GetDirectoryRelative(path); 101 return root->GetDirectoryRelative(path);
102} 102}
103 103
104VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { 104VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
105 const auto path = FileUtil::SanitizePath(path_); 105 const auto path = Common::FS::SanitizePath(path_);
106 return root->CreateDirectoryRelative(path); 106 return root->CreateDirectoryRelative(path);
107} 107}
108 108
109VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { 109VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) {
110 const auto old_path = FileUtil::SanitizePath(old_path_); 110 const auto old_path = Common::FS::SanitizePath(old_path_);
111 const auto new_path = FileUtil::SanitizePath(new_path_); 111 const auto new_path = Common::FS::SanitizePath(new_path_);
112 112
113 // Non-default impls are highly encouraged to provide a more optimized version of this. 113 // Non-default impls are highly encouraged to provide a more optimized version of this.
114 auto old_dir = OpenDirectory(old_path, Mode::Read); 114 auto old_dir = OpenDirectory(old_path, Mode::Read);
@@ -139,8 +139,8 @@ VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_
139} 139}
140 140
141VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) { 141VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) {
142 const auto sanitized_old_path = FileUtil::SanitizePath(old_path); 142 const auto sanitized_old_path = Common::FS::SanitizePath(old_path);
143 const auto sanitized_new_path = FileUtil::SanitizePath(new_path); 143 const auto sanitized_new_path = Common::FS::SanitizePath(new_path);
144 144
145 // Non-default impls are highly encouraged to provide a more optimized version of this. 145 // Non-default impls are highly encouraged to provide a more optimized version of this.
146 auto out = CopyDirectory(sanitized_old_path, sanitized_new_path); 146 auto out = CopyDirectory(sanitized_old_path, sanitized_new_path);
@@ -152,17 +152,17 @@ VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_v
152} 152}
153 153
154bool VfsFilesystem::DeleteDirectory(std::string_view path_) { 154bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
155 const auto path = FileUtil::SanitizePath(path_); 155 const auto path = Common::FS::SanitizePath(path_);
156 auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); 156 auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write);
157 if (parent == nullptr) 157 if (parent == nullptr)
158 return false; 158 return false;
159 return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path)); 159 return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path));
160} 160}
161 161
162VfsFile::~VfsFile() = default; 162VfsFile::~VfsFile() = default;
163 163
164std::string VfsFile::GetExtension() const { 164std::string VfsFile::GetExtension() const {
165 return std::string(FileUtil::GetExtensionFromFilename(GetName())); 165 return std::string(Common::FS::GetExtensionFromFilename(GetName()));
166} 166}
167 167
168VfsDirectory::~VfsDirectory() = default; 168VfsDirectory::~VfsDirectory() = default;
@@ -203,7 +203,7 @@ std::string VfsFile::GetFullPath() const {
203} 203}
204 204
205std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const { 205std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const {
206 auto vec = FileUtil::SplitPathComponents(path); 206 auto vec = Common::FS::SplitPathComponents(path);
207 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 207 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
208 vec.end()); 208 vec.end());
209 if (vec.empty()) { 209 if (vec.empty()) {
@@ -239,7 +239,7 @@ std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(std::string_view path) co
239} 239}
240 240
241std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const { 241std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const {
242 auto vec = FileUtil::SplitPathComponents(path); 242 auto vec = Common::FS::SplitPathComponents(path);
243 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 243 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
244 vec.end()); 244 vec.end());
245 if (vec.empty()) { 245 if (vec.empty()) {
@@ -301,7 +301,7 @@ std::size_t VfsDirectory::GetSize() const {
301} 301}
302 302
303std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) { 303std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) {
304 auto vec = FileUtil::SplitPathComponents(path); 304 auto vec = Common::FS::SplitPathComponents(path);
305 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 305 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
306 vec.end()); 306 vec.end());
307 if (vec.empty()) { 307 if (vec.empty()) {
@@ -320,7 +320,7 @@ std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path)
320 } 320 }
321 } 321 }
322 322
323 return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path)); 323 return dir->CreateFileRelative(Common::FS::GetPathWithoutTop(path));
324} 324}
325 325
326std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) { 326std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) {
@@ -332,7 +332,7 @@ std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path)
332} 332}
333 333
334std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) { 334std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) {
335 auto vec = FileUtil::SplitPathComponents(path); 335 auto vec = Common::FS::SplitPathComponents(path);
336 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 336 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
337 vec.end()); 337 vec.end());
338 if (vec.empty()) { 338 if (vec.empty()) {
@@ -351,7 +351,7 @@ std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_
351 } 351 }
352 } 352 }
353 353
354 return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path)); 354 return dir->CreateDirectoryRelative(Common::FS::GetPathWithoutTop(path));
355} 355}
356 356
357std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(std::string_view path) { 357std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(std::string_view path) {
diff --git a/src/core/file_sys/vfs_libzip.cpp b/src/core/file_sys/vfs_libzip.cpp
index d69952940..429d7bc8b 100644
--- a/src/core/file_sys/vfs_libzip.cpp
+++ b/src/core/file_sys/vfs_libzip.cpp
@@ -49,7 +49,7 @@ VirtualDir ExtractZIP(VirtualFile file) {
49 if (zip_fread(file2.get(), buf.data(), buf.size()) != s64(buf.size())) 49 if (zip_fread(file2.get(), buf.data(), buf.size()) != s64(buf.size()))
50 return nullptr; 50 return nullptr;
51 51
52 const auto parts = FileUtil::SplitPathComponents(stat.name); 52 const auto parts = Common::FS::SplitPathComponents(stat.name);
53 const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back()); 53 const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back());
54 54
55 std::shared_ptr<VectorVfsDirectory> dtrv = out; 55 std::shared_ptr<VectorVfsDirectory> dtrv = out;
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 0db0091f6..488687ba9 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -14,6 +14,8 @@
14 14
15namespace FileSys { 15namespace FileSys {
16 16
17namespace FS = Common::FS;
18
17static std::string ModeFlagsToString(Mode mode) { 19static std::string ModeFlagsToString(Mode mode) {
18 std::string mode_str; 20 std::string mode_str;
19 21
@@ -57,79 +59,82 @@ bool RealVfsFilesystem::IsWritable() const {
57} 59}
58 60
59VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { 61VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
60 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 62 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
61 if (!FileUtil::Exists(path)) 63 if (!FS::Exists(path)) {
62 return VfsEntryType::None; 64 return VfsEntryType::None;
63 if (FileUtil::IsDirectory(path)) 65 }
66 if (FS::IsDirectory(path)) {
64 return VfsEntryType::Directory; 67 return VfsEntryType::Directory;
68 }
65 69
66 return VfsEntryType::File; 70 return VfsEntryType::File;
67} 71}
68 72
69VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { 73VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
70 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 74 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
71 if (cache.find(path) != cache.end()) { 75
72 auto weak = cache[path]; 76 if (const auto weak_iter = cache.find(path); weak_iter != cache.cend()) {
77 const auto& weak = weak_iter->second;
78
73 if (!weak.expired()) { 79 if (!weak.expired()) {
74 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms)); 80 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms));
75 } 81 }
76 } 82 }
77 83
78 if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) { 84 if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) {
79 FileUtil::CreateEmptyFile(path); 85 FS::CreateEmptyFile(path);
80 } 86 }
81 87
82 auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str()); 88 auto backing = std::make_shared<FS::IOFile>(path, ModeFlagsToString(perms).c_str());
83 cache[path] = backing; 89 cache.insert_or_assign(path, backing);
84 90
85 // Cannot use make_shared as RealVfsFile constructor is private 91 // Cannot use make_shared as RealVfsFile constructor is private
86 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); 92 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms));
87} 93}
88 94
89VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { 95VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
90 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 96 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
91 const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash); 97 const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash);
92 if (!FileUtil::Exists(path)) { 98 if (!FS::Exists(path)) {
93 FileUtil::CreateFullPath(path_fwd); 99 FS::CreateFullPath(path_fwd);
94 if (!FileUtil::CreateEmptyFile(path)) 100 if (!FS::CreateEmptyFile(path)) {
95 return nullptr; 101 return nullptr;
102 }
96 } 103 }
97 return OpenFile(path, perms); 104 return OpenFile(path, perms);
98} 105}
99 106
100VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { 107VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
101 const auto old_path = 108 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
102 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 109 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
103 const auto new_path =
104 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
105 110
106 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || 111 if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
107 FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path)) 112 !FS::Copy(old_path, new_path)) {
108 return nullptr; 113 return nullptr;
114 }
109 return OpenFile(new_path, Mode::ReadWrite); 115 return OpenFile(new_path, Mode::ReadWrite);
110} 116}
111 117
112VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { 118VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
113 const auto old_path = 119 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
114 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 120 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
115 const auto new_path = 121 const auto cached_file_iter = cache.find(old_path);
116 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
117 122
118 if (cache.find(old_path) != cache.end()) { 123 if (cached_file_iter != cache.cend()) {
119 auto file = cache[old_path].lock(); 124 auto file = cached_file_iter->second.lock();
120 125
121 if (!cache[old_path].expired()) { 126 if (!cached_file_iter->second.expired()) {
122 file->Close(); 127 file->Close();
123 } 128 }
124 129
125 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || 130 if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
126 FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) { 131 !FS::Rename(old_path, new_path)) {
127 return nullptr; 132 return nullptr;
128 } 133 }
129 134
130 cache.erase(old_path); 135 cache.erase(old_path);
131 file->Open(new_path, "r+b"); 136 file->Open(new_path, "r+b");
132 cache[new_path] = file; 137 cache.insert_or_assign(new_path, std::move(file));
133 } else { 138 } else {
134 UNREACHABLE(); 139 UNREACHABLE();
135 return nullptr; 140 return nullptr;
@@ -139,28 +144,33 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
139} 144}
140 145
141bool RealVfsFilesystem::DeleteFile(std::string_view path_) { 146bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
142 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 147 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
143 if (cache.find(path) != cache.end()) { 148 const auto cached_iter = cache.find(path);
144 if (!cache[path].expired()) 149
145 cache[path].lock()->Close(); 150 if (cached_iter != cache.cend()) {
151 if (!cached_iter->second.expired()) {
152 cached_iter->second.lock()->Close();
153 }
146 cache.erase(path); 154 cache.erase(path);
147 } 155 }
148 return FileUtil::Delete(path); 156
157 return FS::Delete(path);
149} 158}
150 159
151VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { 160VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
152 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 161 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
153 // Cannot use make_shared as RealVfsDirectory constructor is private 162 // Cannot use make_shared as RealVfsDirectory constructor is private
154 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); 163 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
155} 164}
156 165
157VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { 166VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
158 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 167 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
159 const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash); 168 const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash);
160 if (!FileUtil::Exists(path)) { 169 if (!FS::Exists(path)) {
161 FileUtil::CreateFullPath(path_fwd); 170 FS::CreateFullPath(path_fwd);
162 if (!FileUtil::CreateDir(path)) 171 if (!FS::CreateDir(path)) {
163 return nullptr; 172 return nullptr;
173 }
164 } 174 }
165 // Cannot use make_shared as RealVfsDirectory constructor is private 175 // Cannot use make_shared as RealVfsDirectory constructor is private
166 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); 176 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
@@ -168,67 +178,75 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms
168 178
169VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, 179VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
170 std::string_view new_path_) { 180 std::string_view new_path_) {
171 const auto old_path = 181 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
172 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 182 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
173 const auto new_path = 183 if (!FS::Exists(old_path) || FS::Exists(new_path) || !FS::IsDirectory(old_path)) {
174 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
175 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
176 !FileUtil::IsDirectory(old_path))
177 return nullptr; 184 return nullptr;
178 FileUtil::CopyDir(old_path, new_path); 185 }
186 FS::CopyDir(old_path, new_path);
179 return OpenDirectory(new_path, Mode::ReadWrite); 187 return OpenDirectory(new_path, Mode::ReadWrite);
180} 188}
181 189
182VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, 190VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
183 std::string_view new_path_) { 191 std::string_view new_path_) {
184 const auto old_path = 192 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
185 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 193 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
186 const auto new_path = 194
187 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); 195 if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
188 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || 196 !FS::Rename(old_path, new_path)) {
189 FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
190 return nullptr; 197 return nullptr;
198 }
191 199
192 for (auto& kv : cache) { 200 for (auto& kv : cache) {
193 // Path in cache starts with old_path 201 // If the path in the cache doesn't start with old_path, then bail on this file.
194 if (kv.first.rfind(old_path, 0) == 0) { 202 if (kv.first.rfind(old_path, 0) != 0) {
195 const auto file_old_path = 203 continue;
196 FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault); 204 }
197 const auto file_new_path = 205
198 FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), 206 const auto file_old_path =
199 FileUtil::DirectorySeparator::PlatformDefault); 207 FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault);
200 auto cached = cache[file_old_path]; 208 auto file_new_path = FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
201 if (!cached.expired()) { 209 FS::DirectorySeparator::PlatformDefault);
202 auto file = cached.lock(); 210 const auto& cached = cache[file_old_path];
203 file->Open(file_new_path, "r+b"); 211
204 cache.erase(file_old_path); 212 if (cached.expired()) {
205 cache[file_new_path] = file; 213 continue;
206 }
207 } 214 }
215
216 auto file = cached.lock();
217 file->Open(file_new_path, "r+b");
218 cache.erase(file_old_path);
219 cache.insert_or_assign(std::move(file_new_path), std::move(file));
208 } 220 }
209 221
210 return OpenDirectory(new_path, Mode::ReadWrite); 222 return OpenDirectory(new_path, Mode::ReadWrite);
211} 223}
212 224
213bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { 225bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
214 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 226 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
227
215 for (auto& kv : cache) { 228 for (auto& kv : cache) {
216 // Path in cache starts with old_path 229 // If the path in the cache doesn't start with path, then bail on this file.
217 if (kv.first.rfind(path, 0) == 0) { 230 if (kv.first.rfind(path, 0) != 0) {
218 if (!cache[kv.first].expired()) 231 continue;
219 cache[kv.first].lock()->Close(); 232 }
220 cache.erase(kv.first); 233
234 const auto& entry = cache[kv.first];
235 if (!entry.expired()) {
236 entry.lock()->Close();
221 } 237 }
238
239 cache.erase(kv.first);
222 } 240 }
223 return FileUtil::DeleteDirRecursively(path); 241
242 return FS::DeleteDirRecursively(path);
224} 243}
225 244
226RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_, 245RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FS::IOFile> backing_,
227 const std::string& path_, Mode perms_) 246 const std::string& path_, Mode perms_)
228 : base(base_), backing(std::move(backing_)), path(path_), 247 : base(base_), backing(std::move(backing_)), path(path_), parent_path(FS::GetParentPath(path_)),
229 parent_path(FileUtil::GetParentPath(path_)), 248 path_components(FS::SplitPathComponents(path_)),
230 path_components(FileUtil::SplitPathComponents(path_)), 249 parent_components(FS::SliceVector(path_components, 0, path_components.size() - 1)),
231 parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
232 perms(perms_) {} 250 perms(perms_) {}
233 251
234RealVfsFile::~RealVfsFile() = default; 252RealVfsFile::~RealVfsFile() = default;
@@ -258,14 +276,16 @@ bool RealVfsFile::IsReadable() const {
258} 276}
259 277
260std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { 278std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
261 if (!backing->Seek(offset, SEEK_SET)) 279 if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) {
262 return 0; 280 return 0;
281 }
263 return backing->ReadBytes(data, length); 282 return backing->ReadBytes(data, length);
264} 283}
265 284
266std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { 285std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
267 if (!backing->Seek(offset, SEEK_SET)) 286 if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) {
268 return 0; 287 return 0;
288 }
269 return backing->WriteBytes(data, length); 289 return backing->WriteBytes(data, length);
270} 290}
271 291
@@ -282,16 +302,18 @@ bool RealVfsFile::Close() {
282 302
283template <> 303template <>
284std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const { 304std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const {
285 if (perms == Mode::Append) 305 if (perms == Mode::Append) {
286 return {}; 306 return {};
307 }
287 308
288 std::vector<VirtualFile> out; 309 std::vector<VirtualFile> out;
289 FileUtil::ForeachDirectoryEntry( 310 FS::ForeachDirectoryEntry(
290 nullptr, path, 311 nullptr, path,
291 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { 312 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
292 const std::string full_path = directory + DIR_SEP + filename; 313 const std::string full_path = directory + DIR_SEP + filename;
293 if (!FileUtil::IsDirectory(full_path)) 314 if (!FS::IsDirectory(full_path)) {
294 out.emplace_back(base.OpenFile(full_path, perms)); 315 out.emplace_back(base.OpenFile(full_path, perms));
316 }
295 return true; 317 return true;
296 }); 318 });
297 319
@@ -300,16 +322,18 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>(
300 322
301template <> 323template <>
302std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const { 324std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const {
303 if (perms == Mode::Append) 325 if (perms == Mode::Append) {
304 return {}; 326 return {};
327 }
305 328
306 std::vector<VirtualDir> out; 329 std::vector<VirtualDir> out;
307 FileUtil::ForeachDirectoryEntry( 330 FS::ForeachDirectoryEntry(
308 nullptr, path, 331 nullptr, path,
309 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { 332 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
310 const std::string full_path = directory + DIR_SEP + filename; 333 const std::string full_path = directory + DIR_SEP + filename;
311 if (FileUtil::IsDirectory(full_path)) 334 if (FS::IsDirectory(full_path)) {
312 out.emplace_back(base.OpenDirectory(full_path, perms)); 335 out.emplace_back(base.OpenDirectory(full_path, perms));
336 }
313 return true; 337 return true;
314 }); 338 });
315 339
@@ -317,29 +341,30 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi
317} 341}
318 342
319RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) 343RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
320 : base(base_), path(FileUtil::RemoveTrailingSlash(path_)), 344 : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)),
321 parent_path(FileUtil::GetParentPath(path)), 345 path_components(FS::SplitPathComponents(path)),
322 path_components(FileUtil::SplitPathComponents(path)), 346 parent_components(FS::SliceVector(path_components, 0, path_components.size() - 1)),
323 parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
324 perms(perms_) { 347 perms(perms_) {
325 if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) { 348 if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) {
326 FileUtil::CreateDir(path); 349 FS::CreateDir(path);
327 } 350 }
328} 351}
329 352
330RealVfsDirectory::~RealVfsDirectory() = default; 353RealVfsDirectory::~RealVfsDirectory() = default;
331 354
332std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const { 355std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
333 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 356 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
334 if (!FileUtil::Exists(full_path) || FileUtil::IsDirectory(full_path)) 357 if (!FS::Exists(full_path) || FS::IsDirectory(full_path)) {
335 return nullptr; 358 return nullptr;
359 }
336 return base.OpenFile(full_path, perms); 360 return base.OpenFile(full_path, perms);
337} 361}
338 362
339std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const { 363std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const {
340 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 364 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
341 if (!FileUtil::Exists(full_path) || !FileUtil::IsDirectory(full_path)) 365 if (!FS::Exists(full_path) || !FS::IsDirectory(full_path)) {
342 return nullptr; 366 return nullptr;
367 }
343 return base.OpenDirectory(full_path, perms); 368 return base.OpenDirectory(full_path, perms);
344} 369}
345 370
@@ -352,17 +377,17 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view
352} 377}
353 378
354std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) { 379std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) {
355 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 380 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
356 return base.CreateFile(full_path, perms); 381 return base.CreateFile(full_path, perms);
357} 382}
358 383
359std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) { 384std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) {
360 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 385 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
361 return base.CreateDirectory(full_path, perms); 386 return base.CreateDirectory(full_path, perms);
362} 387}
363 388
364bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { 389bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
365 auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name)); 390 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(name));
366 return base.DeleteDirectory(full_path); 391 return base.DeleteDirectory(full_path);
367} 392}
368 393
@@ -387,8 +412,9 @@ std::string RealVfsDirectory::GetName() const {
387} 412}
388 413
389std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const { 414std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
390 if (path_components.size() <= 1) 415 if (path_components.size() <= 1) {
391 return nullptr; 416 return nullptr;
417 }
392 418
393 return base.OpenDirectory(parent_path, perms); 419 return base.OpenDirectory(parent_path, perms);
394} 420}
@@ -425,16 +451,17 @@ std::string RealVfsDirectory::GetFullPath() const {
425} 451}
426 452
427std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const { 453std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const {
428 if (perms == Mode::Append) 454 if (perms == Mode::Append) {
429 return {}; 455 return {};
456 }
430 457
431 std::map<std::string, VfsEntryType, std::less<>> out; 458 std::map<std::string, VfsEntryType, std::less<>> out;
432 FileUtil::ForeachDirectoryEntry( 459 FS::ForeachDirectoryEntry(
433 nullptr, path, 460 nullptr, path,
434 [&out](u64* entries_out, const std::string& directory, const std::string& filename) { 461 [&out](u64* entries_out, const std::string& directory, const std::string& filename) {
435 const std::string full_path = directory + DIR_SEP + filename; 462 const std::string full_path = directory + DIR_SEP + filename;
436 out.emplace(filename, FileUtil::IsDirectory(full_path) ? VfsEntryType::Directory 463 out.emplace(filename,
437 : VfsEntryType::File); 464 FS::IsDirectory(full_path) ? VfsEntryType::Directory : VfsEntryType::File);
438 return true; 465 return true;
439 }); 466 });
440 467
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index a0a857a31..0b537b22c 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -9,7 +9,7 @@
9#include "core/file_sys/mode.h" 9#include "core/file_sys/mode.h"
10#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs.h"
11 11
12namespace FileUtil { 12namespace Common::FS {
13class IOFile; 13class IOFile;
14} 14}
15 15
@@ -36,7 +36,7 @@ public:
36 bool DeleteDirectory(std::string_view path) override; 36 bool DeleteDirectory(std::string_view path) override;
37 37
38private: 38private:
39 boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache; 39 boost::container::flat_map<std::string, std::weak_ptr<Common::FS::IOFile>> cache;
40}; 40};
41 41
42// An implmentation of VfsFile that represents a file on the user's computer. 42// An implmentation of VfsFile that represents a file on the user's computer.
@@ -58,13 +58,13 @@ public:
58 bool Rename(std::string_view name) override; 58 bool Rename(std::string_view name) override;
59 59
60private: 60private:
61 RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing, 61 RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<Common::FS::IOFile> backing,
62 const std::string& path, Mode perms = Mode::Read); 62 const std::string& path, Mode perms = Mode::Read);
63 63
64 bool Close(); 64 bool Close();
65 65
66 RealVfsFilesystem& base; 66 RealVfsFilesystem& base;
67 std::shared_ptr<FileUtil::IOFile> backing; 67 std::shared_ptr<Common::FS::IOFile> backing;
68 std::string path; 68 std::string path;
69 std::string parent_path; 69 std::string parent_path;
70 std::vector<std::string> path_components; 70 std::vector<std::string> path_components;
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
index ac36cb2ee..95d3da2f2 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs_vector.h
@@ -4,7 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <cstring> 8#include <cstring>
9#include <memory>
10#include <string>
11#include <vector>
8#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
9 13
10namespace FileSys { 14namespace FileSys {
@@ -13,7 +17,8 @@ namespace FileSys {
13template <std::size_t size> 17template <std::size_t size>
14class ArrayVfsFile : public VfsFile { 18class ArrayVfsFile : public VfsFile {
15public: 19public:
16 ArrayVfsFile(std::array<u8, size> data, std::string name = "", VirtualDir parent = nullptr) 20 explicit ArrayVfsFile(const std::array<u8, size>& data, std::string name = "",
21 VirtualDir parent = nullptr)
17 : data(data), name(std::move(name)), parent(std::move(parent)) {} 22 : data(data), name(std::move(name)), parent(std::move(parent)) {}
18 23
19 std::string GetName() const override { 24 std::string GetName() const override {
@@ -61,6 +66,12 @@ private:
61 VirtualDir parent; 66 VirtualDir parent;
62}; 67};
63 68
69template <std::size_t Size, typename... Args>
70std::shared_ptr<ArrayVfsFile<Size>> MakeArrayFile(const std::array<u8, Size>& data,
71 Args&&... args) {
72 return std::make_shared<ArrayVfsFile<Size>>(data, std::forward<Args>(args)...);
73}
74
64// An implementation of VfsFile that is backed by a vector optionally supplied upon construction 75// An implementation of VfsFile that is backed by a vector optionally supplied upon construction
65class VectorVfsFile : public VfsFile { 76class VectorVfsFile : public VfsFile {
66public: 77public:
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index 81413c684..ccf5966d0 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -44,7 +44,7 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t
44} 44}
45 45
46NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) { 46NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
47 std::string path = FileUtil::SanitizePath(file->GetFullPath()); 47 std::string path = Common::FS::SanitizePath(file->GetFullPath());
48 static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca", 48 static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca",
49 std::regex_constants::ECMAScript | 49 std::regex_constants::ECMAScript |
50 std::regex_constants::icase); 50 std::regex_constants::icase);
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 13aa14934..3e8780243 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -39,7 +39,7 @@ public:
39 39
40 class Scoped { 40 class Scoped {
41 public: 41 public:
42 explicit Scoped(GraphicsContext& context_) : context(context_) { 42 [[nodiscard]] explicit Scoped(GraphicsContext& context_) : context(context_) {
43 context.MakeCurrent(); 43 context.MakeCurrent();
44 } 44 }
45 ~Scoped() { 45 ~Scoped() {
@@ -52,7 +52,7 @@ public:
52 52
53 /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value 53 /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
54 /// ends 54 /// ends
55 Scoped Acquire() { 55 [[nodiscard]] Scoped Acquire() {
56 return Scoped{*this}; 56 return Scoped{*this};
57 } 57 }
58}; 58};
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index df0debe1b..b882eaa0f 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -81,7 +81,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32
81 do { 81 do {
82 current_value = monitor.ExclusiveRead32(current_core, address); 82 current_value = monitor.ExclusiveRead32(current_core, address);
83 83
84 if (current_value != value) { 84 if (current_value != static_cast<u32>(value)) {
85 return ERR_INVALID_STATE; 85 return ERR_INVALID_STATE;
86 } 86 }
87 current_value++; 87 current_value++;
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 9277b5d08..81f85643b 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -293,13 +293,15 @@ std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
293 BufferDescriptorA()[buffer_index].Size()}; 293 BufferDescriptorA()[buffer_index].Size()};
294 294
295 if (is_buffer_a) { 295 if (is_buffer_a) {
296 ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return buffer; }, 296 ASSERT_OR_EXECUTE_MSG(
297 "BufferDescriptorA invalid buffer_index {}", buffer_index); 297 BufferDescriptorA().size() > buffer_index, { return buffer; },
298 "BufferDescriptorA invalid buffer_index {}", buffer_index);
298 buffer.resize(BufferDescriptorA()[buffer_index].Size()); 299 buffer.resize(BufferDescriptorA()[buffer_index].Size());
299 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); 300 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
300 } else { 301 } else {
301 ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return buffer; }, 302 ASSERT_OR_EXECUTE_MSG(
302 "BufferDescriptorX invalid buffer_index {}", buffer_index); 303 BufferDescriptorX().size() > buffer_index, { return buffer; },
304 "BufferDescriptorX invalid buffer_index {}", buffer_index);
303 buffer.resize(BufferDescriptorX()[buffer_index].Size()); 305 buffer.resize(BufferDescriptorX()[buffer_index].Size());
304 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); 306 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
305 } 307 }
@@ -324,16 +326,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
324 } 326 }
325 327
326 if (is_buffer_b) { 328 if (is_buffer_b) {
327 ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index && 329 ASSERT_OR_EXECUTE_MSG(
328 BufferDescriptorB()[buffer_index].Size() >= size, 330 BufferDescriptorB().size() > buffer_index &&
329 { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", 331 BufferDescriptorB()[buffer_index].Size() >= size,
330 buffer_index, size); 332 { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size);
331 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); 333 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
332 } else { 334 } else {
333 ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index && 335 ASSERT_OR_EXECUTE_MSG(
334 BufferDescriptorC()[buffer_index].Size() >= size, 336 BufferDescriptorC().size() > buffer_index &&
335 { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", 337 BufferDescriptorC()[buffer_index].Size() >= size,
336 buffer_index, size); 338 { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size);
337 memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); 339 memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
338 } 340 }
339 341
@@ -344,12 +346,14 @@ std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const
344 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && 346 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
345 BufferDescriptorA()[buffer_index].Size()}; 347 BufferDescriptorA()[buffer_index].Size()};
346 if (is_buffer_a) { 348 if (is_buffer_a) {
347 ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return 0; }, 349 ASSERT_OR_EXECUTE_MSG(
348 "BufferDescriptorA invalid buffer_index {}", buffer_index); 350 BufferDescriptorA().size() > buffer_index, { return 0; },
351 "BufferDescriptorA invalid buffer_index {}", buffer_index);
349 return BufferDescriptorA()[buffer_index].Size(); 352 return BufferDescriptorA()[buffer_index].Size();
350 } else { 353 } else {
351 ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return 0; }, 354 ASSERT_OR_EXECUTE_MSG(
352 "BufferDescriptorX invalid buffer_index {}", buffer_index); 355 BufferDescriptorX().size() > buffer_index, { return 0; },
356 "BufferDescriptorX invalid buffer_index {}", buffer_index);
353 return BufferDescriptorX()[buffer_index].Size(); 357 return BufferDescriptorX()[buffer_index].Size();
354 } 358 }
355} 359}
@@ -358,12 +362,14 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons
358 const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && 362 const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
359 BufferDescriptorB()[buffer_index].Size()}; 363 BufferDescriptorB()[buffer_index].Size()};
360 if (is_buffer_b) { 364 if (is_buffer_b) {
361 ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index, { return 0; }, 365 ASSERT_OR_EXECUTE_MSG(
362 "BufferDescriptorB invalid buffer_index {}", buffer_index); 366 BufferDescriptorB().size() > buffer_index, { return 0; },
367 "BufferDescriptorB invalid buffer_index {}", buffer_index);
363 return BufferDescriptorB()[buffer_index].Size(); 368 return BufferDescriptorB()[buffer_index].Size();
364 } else { 369 } else {
365 ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index, { return 0; }, 370 ASSERT_OR_EXECUTE_MSG(
366 "BufferDescriptorC invalid buffer_index {}", buffer_index); 371 BufferDescriptorC().size() > buffer_index, { return 0; },
372 "BufferDescriptorC invalid buffer_index {}", buffer_index);
367 return BufferDescriptorC()[buffer_index].Size(); 373 return BufferDescriptorC()[buffer_index].Size();
368 } 374 }
369 return 0; 375 return 0;
diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp
index 5d6aac00f..a3fadb533 100644
--- a/src/core/hle/kernel/memory/page_table.cpp
+++ b/src/core/hle/kernel/memory/page_table.cpp
@@ -604,7 +604,6 @@ ResultCode PageTable::MapPages(VAddr addr, const PageLinkedList& page_linked_lis
604 if (const auto result{ 604 if (const auto result{
605 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; 605 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())};
606 result.IsError()) { 606 result.IsError()) {
607 const MemoryInfo info{block_manager->FindBlock(cur_addr).GetMemoryInfo()};
608 const std::size_t num_pages{(addr - cur_addr) / PageSize}; 607 const std::size_t num_pages{(addr - cur_addr) / PageSize};
609 608
610 ASSERT( 609 ASSERT(
@@ -852,11 +851,12 @@ ResultCode PageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
852 return result; 851 return result;
853 } 852 }
854 853
855 block_manager->UpdateLock(addr, size / PageSize, 854 block_manager->UpdateLock(
856 [](MemoryBlockManager::iterator block, MemoryPermission perm) { 855 addr, size / PageSize,
857 block->ShareToDevice(perm); 856 [](MemoryBlockManager::iterator block, MemoryPermission perm) {
858 }, 857 block->ShareToDevice(perm);
859 perm); 858 },
859 perm);
860 860
861 return RESULT_SUCCESS; 861 return RESULT_SUCCESS;
862} 862}
@@ -874,11 +874,12 @@ ResultCode PageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
874 return result; 874 return result;
875 } 875 }
876 876
877 block_manager->UpdateLock(addr, size / PageSize, 877 block_manager->UpdateLock(
878 [](MemoryBlockManager::iterator block, MemoryPermission perm) { 878 addr, size / PageSize,
879 block->UnshareToDevice(perm); 879 [](MemoryBlockManager::iterator block, MemoryPermission perm) {
880 }, 880 block->UnshareToDevice(perm);
881 perm); 881 },
882 perm);
882 883
883 return RESULT_SUCCESS; 884 return RESULT_SUCCESS;
884} 885}
diff --git a/src/core/hle/kernel/memory/system_control.cpp b/src/core/hle/kernel/memory/system_control.cpp
index 2f98e9c4c..11d204bc2 100644
--- a/src/core/hle/kernel/memory/system_control.cpp
+++ b/src/core/hle/kernel/memory/system_control.cpp
@@ -7,22 +7,15 @@
7#include "core/hle/kernel/memory/system_control.h" 7#include "core/hle/kernel/memory/system_control.h"
8 8
9namespace Kernel::Memory::SystemControl { 9namespace Kernel::Memory::SystemControl {
10 10namespace {
11u64 GenerateRandomU64ForInit() {
12 static std::random_device device;
13 static std::mt19937 gen(device());
14 static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
15 return distribution(gen);
16}
17
18template <typename F> 11template <typename F>
19u64 GenerateUniformRange(u64 min, u64 max, F f) { 12u64 GenerateUniformRange(u64 min, u64 max, F f) {
20 /* Handle the case where the difference is too large to represent. */ 13 // Handle the case where the difference is too large to represent.
21 if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) { 14 if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
22 return f(); 15 return f();
23 } 16 }
24 17
25 /* Iterate until we get a value in range. */ 18 // Iterate until we get a value in range.
26 const u64 range_size = ((max + 1) - min); 19 const u64 range_size = ((max + 1) - min);
27 const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size; 20 const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
28 while (true) { 21 while (true) {
@@ -32,6 +25,14 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) {
32 } 25 }
33} 26}
34 27
28u64 GenerateRandomU64ForInit() {
29 static std::random_device device;
30 static std::mt19937 gen(device());
31 static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
32 return distribution(gen);
33}
34} // Anonymous namespace
35
35u64 GenerateRandomRange(u64 min, u64 max) { 36u64 GenerateRandomRange(u64 min, u64 max) {
36 return GenerateUniformRange(min, max, GenerateRandomU64ForInit); 37 return GenerateUniformRange(min, max, GenerateRandomU64ForInit);
37} 38}
diff --git a/src/core/hle/kernel/memory/system_control.h b/src/core/hle/kernel/memory/system_control.h
index 3fa93111d..19cab8cbc 100644
--- a/src/core/hle/kernel/memory/system_control.h
+++ b/src/core/hle/kernel/memory/system_control.h
@@ -8,11 +8,6 @@
8 8
9namespace Kernel::Memory::SystemControl { 9namespace Kernel::Memory::SystemControl {
10 10
11u64 GenerateRandomU64ForInit();
12
13template <typename F>
14u64 GenerateUniformRange(u64 min, u64 max, F f);
15
16u64 GenerateRandomRange(u64 min, u64 max); 11u64 GenerateRandomRange(u64 min, u64 max);
17 12
18} // namespace Kernel::Memory::SystemControl 13} // namespace Kernel::Memory::SystemControl
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index f93e5e4b0..a4b234424 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -131,7 +131,8 @@ u32 GlobalScheduler::SelectThreads() {
131 u32 cores_needing_context_switch{}; 131 u32 cores_needing_context_switch{};
132 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 132 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
133 Scheduler& sched = kernel.Scheduler(core); 133 Scheduler& sched = kernel.Scheduler(core);
134 ASSERT(top_threads[core] == nullptr || top_threads[core]->GetProcessorID() == core); 134 ASSERT(top_threads[core] == nullptr ||
135 static_cast<u32>(top_threads[core]->GetProcessorID()) == core);
135 if (update_thread(top_threads[core], sched)) { 136 if (update_thread(top_threads[core], sched)) {
136 cores_needing_context_switch |= (1ul << core); 137 cores_needing_context_switch |= (1ul << core);
137 } 138 }
@@ -663,32 +664,26 @@ void Scheduler::Reload() {
663} 664}
664 665
665void Scheduler::SwitchContextStep2() { 666void Scheduler::SwitchContextStep2() {
666 Thread* previous_thread = current_thread_prev.get();
667 Thread* new_thread = selected_thread.get();
668
669 // Load context of new thread 667 // Load context of new thread
670 Process* const previous_process = 668 if (selected_thread) {
671 previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr; 669 ASSERT_MSG(selected_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
672
673 if (new_thread) {
674 ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
675 "Thread must be runnable."); 670 "Thread must be runnable.");
676 671
677 // Cancel any outstanding wakeup events for this thread 672 // Cancel any outstanding wakeup events for this thread
678 new_thread->SetIsRunning(true); 673 selected_thread->SetIsRunning(true);
679 new_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); 674 selected_thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
680 new_thread->SetWasRunning(false); 675 selected_thread->SetWasRunning(false);
681 676
682 auto* const thread_owner_process = current_thread->GetOwnerProcess(); 677 auto* const thread_owner_process = current_thread->GetOwnerProcess();
683 if (thread_owner_process != nullptr) { 678 if (thread_owner_process != nullptr) {
684 system.Kernel().MakeCurrentProcess(thread_owner_process); 679 system.Kernel().MakeCurrentProcess(thread_owner_process);
685 } 680 }
686 if (!new_thread->IsHLEThread()) { 681 if (!selected_thread->IsHLEThread()) {
687 Core::ARM_Interface& cpu_core = new_thread->ArmInterface(); 682 Core::ARM_Interface& cpu_core = selected_thread->ArmInterface();
688 cpu_core.LoadContext(new_thread->GetContext32()); 683 cpu_core.LoadContext(selected_thread->GetContext32());
689 cpu_core.LoadContext(new_thread->GetContext64()); 684 cpu_core.LoadContext(selected_thread->GetContext64());
690 cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); 685 cpu_core.SetTlsAddress(selected_thread->GetTLSAddress());
691 cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); 686 cpu_core.SetTPIDR_EL0(selected_thread->GetTPIDR_EL0());
692 cpu_core.ChangeProcessorID(this->core_id); 687 cpu_core.ChangeProcessorID(this->core_id);
693 cpu_core.ClearExclusiveState(); 688 cpu_core.ClearExclusiveState();
694 } 689 }
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index b3b4b5169..36e3c26fb 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -289,7 +289,7 @@ private:
289 289
290class SchedulerLock { 290class SchedulerLock {
291public: 291public:
292 explicit SchedulerLock(KernelCore& kernel); 292 [[nodiscard]] explicit SchedulerLock(KernelCore& kernel);
293 ~SchedulerLock(); 293 ~SchedulerLock();
294 294
295protected: 295protected:
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 450f61fea..b6bdbd988 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -342,8 +342,9 @@ ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) {
342 */ 342 */
343#define CASCADE_RESULT(target, source) \ 343#define CASCADE_RESULT(target, source) \
344 auto CONCAT2(check_result_L, __LINE__) = source; \ 344 auto CONCAT2(check_result_L, __LINE__) = source; \
345 if (CONCAT2(check_result_L, __LINE__).Failed()) \ 345 if (CONCAT2(check_result_L, __LINE__).Failed()) { \
346 return CONCAT2(check_result_L, __LINE__).Code(); \ 346 return CONCAT2(check_result_L, __LINE__).Code(); \
347 } \
347 target = std::move(*CONCAT2(check_result_L, __LINE__)) 348 target = std::move(*CONCAT2(check_result_L, __LINE__))
348 349
349/** 350/**
@@ -351,6 +352,9 @@ ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) {
351 * non-success, or discarded otherwise. 352 * non-success, or discarded otherwise.
352 */ 353 */
353#define CASCADE_CODE(source) \ 354#define CASCADE_CODE(source) \
354 auto CONCAT2(check_result_L, __LINE__) = source; \ 355 do { \
355 if (CONCAT2(check_result_L, __LINE__).IsError()) \ 356 auto CONCAT2(check_result_L, __LINE__) = source; \
356 return CONCAT2(check_result_L, __LINE__); 357 if (CONCAT2(check_result_L, __LINE__).IsError()) { \
358 return CONCAT2(check_result_L, __LINE__); \
359 } \
360 } while (false)
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 63e4aeca0..eb54cb123 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -35,7 +35,7 @@ constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 30};
35constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; 35constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
36 36
37static std::string GetImagePath(Common::UUID uuid) { 37static std::string GetImagePath(Common::UUID uuid) {
38 return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 38 return Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
39 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; 39 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
40} 40}
41 41
@@ -318,7 +318,7 @@ protected:
318 IPC::ResponseBuilder rb{ctx, 3}; 318 IPC::ResponseBuilder rb{ctx, 3};
319 rb.Push(RESULT_SUCCESS); 319 rb.Push(RESULT_SUCCESS);
320 320
321 const FileUtil::IOFile image(GetImagePath(user_id), "rb"); 321 const Common::FS::IOFile image(GetImagePath(user_id), "rb");
322 if (!image.IsOpen()) { 322 if (!image.IsOpen()) {
323 LOG_WARNING(Service_ACC, 323 LOG_WARNING(Service_ACC,
324 "Failed to load user provided image! Falling back to built-in backup..."); 324 "Failed to load user provided image! Falling back to built-in backup...");
@@ -340,7 +340,7 @@ protected:
340 IPC::ResponseBuilder rb{ctx, 3}; 340 IPC::ResponseBuilder rb{ctx, 3};
341 rb.Push(RESULT_SUCCESS); 341 rb.Push(RESULT_SUCCESS);
342 342
343 const FileUtil::IOFile image(GetImagePath(user_id), "rb"); 343 const Common::FS::IOFile image(GetImagePath(user_id), "rb");
344 344
345 if (!image.IsOpen()) { 345 if (!image.IsOpen()) {
346 LOG_WARNING(Service_ACC, 346 LOG_WARNING(Service_ACC,
@@ -405,7 +405,7 @@ protected:
405 ProfileData data; 405 ProfileData data;
406 std::memcpy(&data, user_data.data(), sizeof(ProfileData)); 406 std::memcpy(&data, user_data.data(), sizeof(ProfileData));
407 407
408 FileUtil::IOFile image(GetImagePath(user_id), "wb"); 408 Common::FS::IOFile image(GetImagePath(user_id), "wb");
409 409
410 if (!image.IsOpen() || !image.Resize(image_data.size()) || 410 if (!image.IsOpen() || !image.Resize(image_data.size()) ||
411 image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() || 411 image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() ||
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index a98d57b5c..9b829e957 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -13,6 +13,7 @@
13 13
14namespace Service::Account { 14namespace Service::Account {
15 15
16namespace FS = Common::FS;
16using Common::UUID; 17using Common::UUID;
17 18
18struct UserRaw { 19struct UserRaw {
@@ -318,9 +319,8 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase&
318} 319}
319 320
320void ProfileManager::ParseUserSaveFile() { 321void ProfileManager::ParseUserSaveFile() {
321 FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 322 const FS::IOFile save(
322 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", 323 FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", "rb");
323 "rb");
324 324
325 if (!save.IsOpen()) { 325 if (!save.IsOpen()) {
326 LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new " 326 LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
@@ -366,22 +366,22 @@ void ProfileManager::WriteUserSaveFile() {
366 }; 366 };
367 } 367 }
368 368
369 const auto raw_path = 369 const auto raw_path = FS::GetUserPath(FS::UserPath::NANDDir) + "/system/save/8000000000000010";
370 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; 370 if (FS::Exists(raw_path) && !FS::IsDirectory(raw_path)) {
371 if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) 371 FS::Delete(raw_path);
372 FileUtil::Delete(raw_path); 372 }
373 373
374 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 374 const auto path =
375 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat"; 375 FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
376 376
377 if (!FileUtil::CreateFullPath(path)) { 377 if (!FS::CreateFullPath(path)) {
378 LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory " 378 LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory "
379 "nand/system/save/8000000000000010/su/avators to mitigate this " 379 "nand/system/save/8000000000000010/su/avators to mitigate this "
380 "issue."); 380 "issue.");
381 return; 381 return;
382 } 382 }
383 383
384 FileUtil::IOFile save(path, "wb"); 384 FS::IOFile save(path, "wb");
385 385
386 if (!save.IsOpen()) { 386 if (!save.IsOpen()) {
387 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " 387 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 55a1edf1a..7d92b25a3 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -378,7 +378,11 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext&
378} 378}
379 379
380void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { 380void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
381 LOG_WARNING(Service_AM, "(STUBBED) called"); 381 IPC::RequestParser rp{ctx};
382 const auto permission = rp.PopEnum<ScreenshotPermission>();
383 LOG_DEBUG(Service_AM, "called, permission={}", permission);
384
385 screenshot_permission = permission;
382 386
383 IPC::ResponseBuilder rb{ctx, 2}; 387 IPC::ResponseBuilder rb{ctx, 2};
384 rb.Push(RESULT_SUCCESS); 388 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 6cfb11b48..6e69796ec 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -149,6 +149,12 @@ private:
149 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); 149 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
150 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); 150 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
151 151
152 enum class ScreenshotPermission : u32 {
153 Inherit = 0,
154 Enable = 1,
155 Disable = 2,
156 };
157
152 Core::System& system; 158 Core::System& system;
153 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 159 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
154 Kernel::EventPair launchable_event; 160 Kernel::EventPair launchable_event;
@@ -157,6 +163,7 @@ private:
157 u32 idle_time_detection_extension = 0; 163 u32 idle_time_detection_extension = 0;
158 u64 num_fatal_sections_entered = 0; 164 u64 num_fatal_sections_entered = 0;
159 bool is_auto_sleep_disabled = false; 165 bool is_auto_sleep_disabled = false;
166 ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit;
160}; 167};
161 168
162class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { 169class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index 289da2619..bdeb0737a 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -122,8 +122,7 @@ void SoftwareKeyboard::ExecuteInteractive() {
122 122
123 switch (request) { 123 switch (request) {
124 case Request::Calc: { 124 case Request::Calc: {
125 broker.PushNormalDataFromApplet( 125 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{1}));
126 std::make_shared<IStorage>(std::move(std::vector<u8>{1})));
127 broker.SignalStateChanged(); 126 broker.SignalStateChanged();
128 break; 127 break;
129 } 128 }
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 9f30e167d..efe595c4f 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -293,8 +293,8 @@ void WebBrowser::Finalize() {
293 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data))); 293 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data)));
294 broker.SignalStateChanged(); 294 broker.SignalStateChanged();
295 295
296 if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) { 296 if (!temporary_dir.empty() && Common::FS::IsDirectory(temporary_dir)) {
297 FileUtil::DeleteDirRecursively(temporary_dir); 297 Common::FS::DeleteDirRecursively(temporary_dir);
298 } 298 }
299} 299}
300 300
@@ -452,10 +452,10 @@ void WebBrowser::InitializeOffline() {
452 }; 452 };
453 453
454 temporary_dir = 454 temporary_dir =
455 FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + "web_applet_" + 455 Common::FS::SanitizePath(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
456 WEB_SOURCE_NAMES[static_cast<u32>(source) - 1], 456 "web_applet_" + WEB_SOURCE_NAMES[static_cast<u32>(source) - 1],
457 FileUtil::DirectorySeparator::PlatformDefault); 457 Common::FS::DirectorySeparator::PlatformDefault);
458 FileUtil::DeleteDirRecursively(temporary_dir); 458 Common::FS::DeleteDirRecursively(temporary_dir);
459 459
460 u64 title_id = 0; // 0 corresponds to current process 460 u64 title_id = 0; // 0 corresponds to current process
461 ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8); 461 ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8);
@@ -492,8 +492,8 @@ void WebBrowser::InitializeOffline() {
492 } 492 }
493 493
494 filename = 494 filename =
495 FileUtil::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename, 495 Common::FS::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename,
496 FileUtil::DirectorySeparator::PlatformDefault); 496 Common::FS::DirectorySeparator::PlatformDefault);
497} 497}
498 498
499void WebBrowser::ExecuteShop() { 499void WebBrowser::ExecuteShop() {
@@ -551,7 +551,8 @@ void WebBrowser::ExecuteShop() {
551} 551}
552 552
553void WebBrowser::ExecuteOffline() { 553void WebBrowser::ExecuteOffline() {
554 frontend.OpenPageLocal(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); 554 frontend.OpenPageLocal(
555 filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
555} 556}
556 557
557} // namespace Service::AM::Applets 558} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index dd80dd1dc..9b4910e53 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -206,7 +206,7 @@ private:
206 AudioCore::StreamPtr stream; 206 AudioCore::StreamPtr stream;
207 std::string device_name; 207 std::string device_name;
208 208
209 [[maybe_unused]] AudoutParams audio_params {}; 209 [[maybe_unused]] AudoutParams audio_params{};
210 210
211 /// This is the event handle used to check if the audio buffer was released 211 /// This is the event handle used to check if the audio buffer was released
212 Kernel::EventPair buffer_event; 212 Kernel::EventPair buffer_event;
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp
index d29e78d7e..ca021a99f 100644
--- a/src/core/hle/service/bcat/backend/boxcat.cpp
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -89,12 +89,12 @@ constexpr u32 TIMEOUT_SECONDS = 30;
89 89
90std::string GetBINFilePath(u64 title_id) { 90std::string GetBINFilePath(u64 title_id) {
91 return fmt::format("{}bcat/{:016X}/launchparam.bin", 91 return fmt::format("{}bcat/{:016X}/launchparam.bin",
92 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); 92 Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id);
93} 93}
94 94
95std::string GetZIPFilePath(u64 title_id) { 95std::string GetZIPFilePath(u64 title_id) {
96 return fmt::format("{}bcat/{:016X}/data.zip", 96 return fmt::format("{}bcat/{:016X}/data.zip",
97 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); 97 Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id);
98} 98}
99 99
100// If the error is something the user should know about (build ID mismatch, bad client version), 100// If the error is something the user should know about (build ID mismatch, bad client version),
@@ -205,8 +205,8 @@ private:
205 {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)}, 205 {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)},
206 }; 206 };
207 207
208 if (FileUtil::Exists(path)) { 208 if (Common::FS::Exists(path)) {
209 FileUtil::IOFile file{path, "rb"}; 209 Common::FS::IOFile file{path, "rb"};
210 if (file.IsOpen()) { 210 if (file.IsOpen()) {
211 std::vector<u8> bytes(file.GetSize()); 211 std::vector<u8> bytes(file.GetSize());
212 file.ReadBytes(bytes.data(), bytes.size()); 212 file.ReadBytes(bytes.data(), bytes.size());
@@ -236,8 +236,8 @@ private:
236 return DownloadResult::InvalidContentType; 236 return DownloadResult::InvalidContentType;
237 } 237 }
238 238
239 FileUtil::CreateFullPath(path); 239 Common::FS::CreateFullPath(path);
240 FileUtil::IOFile file{path, "wb"}; 240 Common::FS::IOFile file{path, "wb"};
241 if (!file.IsOpen()) 241 if (!file.IsOpen())
242 return DownloadResult::GeneralFSError; 242 return DownloadResult::GeneralFSError;
243 if (!file.Resize(response->body.size())) 243 if (!file.Resize(response->body.size()))
@@ -290,7 +290,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
290 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); 290 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
291 291
292 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { 292 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
293 FileUtil::Delete(zip_path); 293 Common::FS::Delete(zip_path);
294 } 294 }
295 295
296 HandleDownloadDisplayResult(applet_manager, res); 296 HandleDownloadDisplayResult(applet_manager, res);
@@ -300,7 +300,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
300 300
301 progress.StartProcessingDataList(); 301 progress.StartProcessingDataList();
302 302
303 FileUtil::IOFile zip{zip_path, "rb"}; 303 Common::FS::IOFile zip{zip_path, "rb"};
304 const auto size = zip.GetSize(); 304 const auto size = zip.GetSize();
305 std::vector<u8> bytes(size); 305 std::vector<u8> bytes(size);
306 if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { 306 if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
@@ -365,8 +365,7 @@ bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress)
365 365
366 std::thread([this, title, &progress] { 366 std::thread([this, title, &progress] {
367 SynchronizeInternal(applet_manager, dir_getter, title, progress); 367 SynchronizeInternal(applet_manager, dir_getter, title, progress);
368 }) 368 }).detach();
369 .detach();
370 369
371 return true; 370 return true;
372} 371}
@@ -377,8 +376,7 @@ bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name,
377 376
378 std::thread([this, title, name, &progress] { 377 std::thread([this, title, name, &progress] {
379 SynchronizeInternal(applet_manager, dir_getter, title, progress, name); 378 SynchronizeInternal(applet_manager, dir_getter, title, progress, name);
380 }) 379 }).detach();
381 .detach();
382 380
383 return true; 381 return true;
384} 382}
@@ -422,7 +420,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
422 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); 420 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
423 421
424 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { 422 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
425 FileUtil::Delete(path); 423 Common::FS::Delete(path);
426 } 424 }
427 425
428 HandleDownloadDisplayResult(applet_manager, res); 426 HandleDownloadDisplayResult(applet_manager, res);
@@ -430,7 +428,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
430 } 428 }
431 } 429 }
432 430
433 FileUtil::IOFile bin{path, "rb"}; 431 Common::FS::IOFile bin{path, "rb"};
434 const auto size = bin.GetSize(); 432 const auto size = bin.GetSize();
435 std::vector<u8> bytes(size); 433 std::vector<u8> bytes(size);
436 if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { 434 if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 4490f8e4c..2cee1193c 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -36,7 +36,7 @@ constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
36 36
37static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, 37static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
38 std::string_view dir_name_) { 38 std::string_view dir_name_) {
39 std::string dir_name(FileUtil::SanitizePath(dir_name_)); 39 std::string dir_name(Common::FS::SanitizePath(dir_name_));
40 if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\") 40 if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
41 return base; 41 return base;
42 42
@@ -53,13 +53,13 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
53} 53}
54 54
55ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const { 55ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
56 std::string path(FileUtil::SanitizePath(path_)); 56 std::string path(Common::FS::SanitizePath(path_));
57 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 57 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
58 // dir can be nullptr if path contains subdirectories, create those prior to creating the file. 58 // dir can be nullptr if path contains subdirectories, create those prior to creating the file.
59 if (dir == nullptr) { 59 if (dir == nullptr) {
60 dir = backing->CreateSubdirectory(FileUtil::GetParentPath(path)); 60 dir = backing->CreateSubdirectory(Common::FS::GetParentPath(path));
61 } 61 }
62 auto file = dir->CreateFile(FileUtil::GetFilename(path)); 62 auto file = dir->CreateFile(Common::FS::GetFilename(path));
63 if (file == nullptr) { 63 if (file == nullptr) {
64 // TODO(DarkLordZach): Find a better error code for this 64 // TODO(DarkLordZach): Find a better error code for this
65 return RESULT_UNKNOWN; 65 return RESULT_UNKNOWN;
@@ -72,17 +72,17 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
72} 72}
73 73
74ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { 74ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
75 std::string path(FileUtil::SanitizePath(path_)); 75 std::string path(Common::FS::SanitizePath(path_));
76 if (path.empty()) { 76 if (path.empty()) {
77 // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but... 77 // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
78 return RESULT_SUCCESS; 78 return RESULT_SUCCESS;
79 } 79 }
80 80
81 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 81 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
82 if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) { 82 if (dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
83 return FileSys::ERROR_PATH_NOT_FOUND; 83 return FileSys::ERROR_PATH_NOT_FOUND;
84 } 84 }
85 if (!dir->DeleteFile(FileUtil::GetFilename(path))) { 85 if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
86 // TODO(DarkLordZach): Find a better error code for this 86 // TODO(DarkLordZach): Find a better error code for this
87 return RESULT_UNKNOWN; 87 return RESULT_UNKNOWN;
88 } 88 }
@@ -91,11 +91,11 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
91} 91}
92 92
93ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const { 93ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
94 std::string path(FileUtil::SanitizePath(path_)); 94 std::string path(Common::FS::SanitizePath(path_));
95 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 95 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
96 if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty()) 96 if (dir == nullptr && Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty())
97 dir = backing; 97 dir = backing;
98 auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path)); 98 auto new_dir = dir->CreateSubdirectory(Common::FS::GetFilename(path));
99 if (new_dir == nullptr) { 99 if (new_dir == nullptr) {
100 // TODO(DarkLordZach): Find a better error code for this 100 // TODO(DarkLordZach): Find a better error code for this
101 return RESULT_UNKNOWN; 101 return RESULT_UNKNOWN;
@@ -104,9 +104,9 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_)
104} 104}
105 105
106ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const { 106ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
107 std::string path(FileUtil::SanitizePath(path_)); 107 std::string path(Common::FS::SanitizePath(path_));
108 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 108 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
109 if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) { 109 if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
110 // TODO(DarkLordZach): Find a better error code for this 110 // TODO(DarkLordZach): Find a better error code for this
111 return RESULT_UNKNOWN; 111 return RESULT_UNKNOWN;
112 } 112 }
@@ -114,9 +114,9 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_)
114} 114}
115 115
116ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const { 116ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
117 std::string path(FileUtil::SanitizePath(path_)); 117 std::string path(Common::FS::SanitizePath(path_));
118 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 118 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
119 if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) { 119 if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
120 // TODO(DarkLordZach): Find a better error code for this 120 // TODO(DarkLordZach): Find a better error code for this
121 return RESULT_UNKNOWN; 121 return RESULT_UNKNOWN;
122 } 122 }
@@ -124,10 +124,10 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
124} 124}
125 125
126ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const { 126ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
127 const std::string sanitized_path(FileUtil::SanitizePath(path)); 127 const std::string sanitized_path(Common::FS::SanitizePath(path));
128 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(sanitized_path)); 128 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));
129 129
130 if (!dir->CleanSubdirectoryRecursive(FileUtil::GetFilename(sanitized_path))) { 130 if (!dir->CleanSubdirectoryRecursive(Common::FS::GetFilename(sanitized_path))) {
131 // TODO(DarkLordZach): Find a better error code for this 131 // TODO(DarkLordZach): Find a better error code for this
132 return RESULT_UNKNOWN; 132 return RESULT_UNKNOWN;
133 } 133 }
@@ -137,14 +137,14 @@ ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::stri
137 137
138ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, 138ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
139 const std::string& dest_path_) const { 139 const std::string& dest_path_) const {
140 std::string src_path(FileUtil::SanitizePath(src_path_)); 140 std::string src_path(Common::FS::SanitizePath(src_path_));
141 std::string dest_path(FileUtil::SanitizePath(dest_path_)); 141 std::string dest_path(Common::FS::SanitizePath(dest_path_));
142 auto src = backing->GetFileRelative(src_path); 142 auto src = backing->GetFileRelative(src_path);
143 if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { 143 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
144 // Use more-optimized vfs implementation rename. 144 // Use more-optimized vfs implementation rename.
145 if (src == nullptr) 145 if (src == nullptr)
146 return FileSys::ERROR_PATH_NOT_FOUND; 146 return FileSys::ERROR_PATH_NOT_FOUND;
147 if (!src->Rename(FileUtil::GetFilename(dest_path))) { 147 if (!src->Rename(Common::FS::GetFilename(dest_path))) {
148 // TODO(DarkLordZach): Find a better error code for this 148 // TODO(DarkLordZach): Find a better error code for this
149 return RESULT_UNKNOWN; 149 return RESULT_UNKNOWN;
150 } 150 }
@@ -162,7 +162,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
162 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), 162 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
163 "Could not write all of the bytes but everything else has succeded."); 163 "Could not write all of the bytes but everything else has succeded.");
164 164
165 if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) { 165 if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
166 // TODO(DarkLordZach): Find a better error code for this 166 // TODO(DarkLordZach): Find a better error code for this
167 return RESULT_UNKNOWN; 167 return RESULT_UNKNOWN;
168 } 168 }
@@ -172,14 +172,14 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
172 172
173ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, 173ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
174 const std::string& dest_path_) const { 174 const std::string& dest_path_) const {
175 std::string src_path(FileUtil::SanitizePath(src_path_)); 175 std::string src_path(Common::FS::SanitizePath(src_path_));
176 std::string dest_path(FileUtil::SanitizePath(dest_path_)); 176 std::string dest_path(Common::FS::SanitizePath(dest_path_));
177 auto src = GetDirectoryRelativeWrapped(backing, src_path); 177 auto src = GetDirectoryRelativeWrapped(backing, src_path);
178 if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { 178 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
179 // Use more-optimized vfs implementation rename. 179 // Use more-optimized vfs implementation rename.
180 if (src == nullptr) 180 if (src == nullptr)
181 return FileSys::ERROR_PATH_NOT_FOUND; 181 return FileSys::ERROR_PATH_NOT_FOUND;
182 if (!src->Rename(FileUtil::GetFilename(dest_path))) { 182 if (!src->Rename(Common::FS::GetFilename(dest_path))) {
183 // TODO(DarkLordZach): Find a better error code for this 183 // TODO(DarkLordZach): Find a better error code for this
184 return RESULT_UNKNOWN; 184 return RESULT_UNKNOWN;
185 } 185 }
@@ -198,7 +198,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
198 198
199ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_, 199ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
200 FileSys::Mode mode) const { 200 FileSys::Mode mode) const {
201 const std::string path(FileUtil::SanitizePath(path_)); 201 const std::string path(Common::FS::SanitizePath(path_));
202 std::string_view npath = path; 202 std::string_view npath = path;
203 while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) { 203 while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
204 npath.remove_prefix(1); 204 npath.remove_prefix(1);
@@ -218,7 +218,7 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::
218} 218}
219 219
220ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) { 220ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) {
221 std::string path(FileUtil::SanitizePath(path_)); 221 std::string path(Common::FS::SanitizePath(path_));
222 auto dir = GetDirectoryRelativeWrapped(backing, path); 222 auto dir = GetDirectoryRelativeWrapped(backing, path);
223 if (dir == nullptr) { 223 if (dir == nullptr) {
224 // TODO(DarkLordZach): Find a better error code for this 224 // TODO(DarkLordZach): Find a better error code for this
@@ -229,11 +229,11 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s
229 229
230ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( 230ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
231 const std::string& path_) const { 231 const std::string& path_) const {
232 std::string path(FileUtil::SanitizePath(path_)); 232 std::string path(Common::FS::SanitizePath(path_));
233 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 233 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
234 if (dir == nullptr) 234 if (dir == nullptr)
235 return FileSys::ERROR_PATH_NOT_FOUND; 235 return FileSys::ERROR_PATH_NOT_FOUND;
236 auto filename = FileUtil::GetFilename(path); 236 auto filename = Common::FS::GetFilename(path);
237 // TODO(Subv): Some games use the '/' path, find out what this means. 237 // TODO(Subv): Some games use the '/' path, find out what this means.
238 if (filename.empty()) 238 if (filename.empty())
239 return MakeResult(FileSys::EntryType::Directory); 239 return MakeResult(FileSys::EntryType::Directory);
@@ -695,13 +695,13 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
695 sdmc_factory = nullptr; 695 sdmc_factory = nullptr;
696 } 696 }
697 697
698 auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), 698 auto nand_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir),
699 FileSys::Mode::ReadWrite); 699 FileSys::Mode::ReadWrite);
700 auto sd_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), 700 auto sd_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir),
701 FileSys::Mode::ReadWrite); 701 FileSys::Mode::ReadWrite);
702 auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), 702 auto load_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
703 FileSys::Mode::ReadWrite); 703 FileSys::Mode::ReadWrite);
704 auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), 704 auto dump_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir),
705 FileSys::Mode::ReadWrite); 705 FileSys::Mode::ReadWrite);
706 706
707 if (bis_factory == nullptr) { 707 if (bis_factory == nullptr) {
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index ef67ad690..45fde8df2 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -24,6 +24,7 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
24constexpr std::size_t NPAD_OFFSET = 0x9A00; 24constexpr std::size_t NPAD_OFFSET = 0x9A00;
25constexpr u32 BATTERY_FULL = 2; 25constexpr u32 BATTERY_FULL = 2;
26constexpr u32 MAX_NPAD_ID = 7; 26constexpr u32 MAX_NPAD_ID = 7;
27constexpr std::size_t HANDHELD_INDEX = 8;
27constexpr std::array<u32, 10> npad_id_list{ 28constexpr std::array<u32, 10> npad_id_list{
28 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, 29 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
29}; 30};
@@ -33,19 +34,41 @@ enum class JoystickId : std::size_t {
33 Joystick_Right, 34 Joystick_Right,
34}; 35};
35 36
36static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) { 37Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
38 Settings::ControllerType type) {
37 switch (type) { 39 switch (type) {
38 case Settings::ControllerType::ProController: 40 case Settings::ControllerType::ProController:
39 return Controller_NPad::NPadControllerType::ProController; 41 return NPadControllerType::ProController;
40 case Settings::ControllerType::DualJoycon: 42 case Settings::ControllerType::DualJoyconDetached:
41 return Controller_NPad::NPadControllerType::JoyDual; 43 return NPadControllerType::JoyDual;
42 case Settings::ControllerType::LeftJoycon: 44 case Settings::ControllerType::LeftJoycon:
43 return Controller_NPad::NPadControllerType::JoyLeft; 45 return NPadControllerType::JoyLeft;
44 case Settings::ControllerType::RightJoycon: 46 case Settings::ControllerType::RightJoycon:
45 return Controller_NPad::NPadControllerType::JoyRight; 47 return NPadControllerType::JoyRight;
48 case Settings::ControllerType::Handheld:
49 return NPadControllerType::Handheld;
46 default: 50 default:
47 UNREACHABLE(); 51 UNREACHABLE();
48 return Controller_NPad::NPadControllerType::JoyDual; 52 return NPadControllerType::ProController;
53 }
54}
55
56Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
57 Controller_NPad::NPadControllerType type) {
58 switch (type) {
59 case NPadControllerType::ProController:
60 return Settings::ControllerType::ProController;
61 case NPadControllerType::JoyDual:
62 return Settings::ControllerType::DualJoyconDetached;
63 case NPadControllerType::JoyLeft:
64 return Settings::ControllerType::LeftJoycon;
65 case NPadControllerType::JoyRight:
66 return Settings::ControllerType::RightJoycon;
67 case NPadControllerType::Handheld:
68 return Settings::ControllerType::Handheld;
69 default:
70 UNREACHABLE();
71 return Settings::ControllerType::ProController;
49 } 72 }
50} 73}
51 74
@@ -60,9 +83,9 @@ std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
60 case 6: 83 case 6:
61 case 7: 84 case 7:
62 return npad_id; 85 return npad_id;
63 case 8: 86 case HANDHELD_INDEX:
64 case NPAD_HANDHELD: 87 case NPAD_HANDHELD:
65 return 8; 88 return HANDHELD_INDEX;
66 case 9: 89 case 9:
67 case NPAD_UNKNOWN: 90 case NPAD_UNKNOWN:
68 return 9; 91 return 9;
@@ -83,38 +106,48 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
83 case 6: 106 case 6:
84 case 7: 107 case 7:
85 return static_cast<u32>(index); 108 return static_cast<u32>(index);
86 case 8: 109 case HANDHELD_INDEX:
87 return NPAD_HANDHELD; 110 return NPAD_HANDHELD;
88 case 9: 111 case 9:
89 return NPAD_UNKNOWN; 112 return NPAD_UNKNOWN;
90 default: 113 default:
91 UNIMPLEMENTED_MSG("Unknown npad index {}", index); 114 UNIMPLEMENTED_MSG("Unknown npad index {}", index);
92 return 0; 115 return 0;
93 }; 116 }
94} 117}
95 118
96Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} 119Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
97Controller_NPad::~Controller_NPad() = default; 120Controller_NPad::~Controller_NPad() = default;
98 121
99void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { 122void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
100 const auto controller_type = connected_controllers[controller_idx].type; 123 const auto controller_type = connected_controllers[controller_idx].type;
101 auto& controller = shared_memory_entries[controller_idx]; 124 auto& controller = shared_memory_entries[controller_idx];
102 if (controller_type == NPadControllerType::None) { 125 if (controller_type == NPadControllerType::None) {
126 styleset_changed_events[controller_idx].writable->Signal();
103 return; 127 return;
104 } 128 }
105 controller.joy_styles.raw = 0; // Zero out 129 controller.joy_styles.raw = 0; // Zero out
106 controller.device_type.raw = 0; 130 controller.device_type.raw = 0;
131 controller.properties.raw = 0;
107 switch (controller_type) { 132 switch (controller_type) {
108 case NPadControllerType::None: 133 case NPadControllerType::None:
109 UNREACHABLE(); 134 UNREACHABLE();
110 break; 135 break;
136 case NPadControllerType::ProController:
137 controller.joy_styles.pro_controller.Assign(1);
138 controller.device_type.pro_controller.Assign(1);
139 controller.properties.is_vertical.Assign(1);
140 controller.properties.use_plus.Assign(1);
141 controller.properties.use_minus.Assign(1);
142 controller.pad_assignment = NPadAssignments::Single;
143 break;
111 case NPadControllerType::Handheld: 144 case NPadControllerType::Handheld:
112 controller.joy_styles.handheld.Assign(1); 145 controller.joy_styles.handheld.Assign(1);
113 controller.device_type.handheld.Assign(1); 146 controller.device_type.handheld.Assign(1);
114 controller.pad_assignment = NPadAssignments::Dual;
115 controller.properties.is_vertical.Assign(1); 147 controller.properties.is_vertical.Assign(1);
116 controller.properties.use_plus.Assign(1); 148 controller.properties.use_plus.Assign(1);
117 controller.properties.use_minus.Assign(1); 149 controller.properties.use_minus.Assign(1);
150 controller.pad_assignment = NPadAssignments::Dual;
118 break; 151 break;
119 case NPadControllerType::JoyDual: 152 case NPadControllerType::JoyDual:
120 controller.joy_styles.joycon_dual.Assign(1); 153 controller.joy_styles.joycon_dual.Assign(1);
@@ -144,14 +177,6 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
144 controller.device_type.pokeball.Assign(1); 177 controller.device_type.pokeball.Assign(1);
145 controller.pad_assignment = NPadAssignments::Single; 178 controller.pad_assignment = NPadAssignments::Single;
146 break; 179 break;
147 case NPadControllerType::ProController:
148 controller.joy_styles.pro_controller.Assign(1);
149 controller.device_type.pro_controller.Assign(1);
150 controller.properties.is_vertical.Assign(1);
151 controller.properties.use_plus.Assign(1);
152 controller.properties.use_minus.Assign(1);
153 controller.pad_assignment = NPadAssignments::Single;
154 break;
155 } 180 }
156 181
157 controller.single_color_error = ColorReadError::ReadOk; 182 controller.single_color_error = ColorReadError::ReadOk;
@@ -192,36 +217,25 @@ void Controller_NPad::OnInit() {
192 style.pokeball.Assign(1); 217 style.pokeball.Assign(1);
193 } 218 }
194 219
195 std::transform( 220 std::transform(Settings::values.players.begin(), Settings::values.players.end(),
196 Settings::values.players.begin(), Settings::values.players.end(), 221 connected_controllers.begin(), [](const Settings::PlayerInput& player) {
197 connected_controllers.begin(), [](const Settings::PlayerInput& player) { 222 return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
198 return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected}; 223 player.connected};
199 }); 224 });
200
201 std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
202 [](const ControllerHolder& holder) { return holder.is_connected; });
203 225
204 // Account for handheld 226 // Account for handheld
205 if (connected_controllers[8].is_connected) 227 if (connected_controllers[HANDHELD_INDEX].is_connected) {
206 connected_controllers[8].type = NPadControllerType::Handheld; 228 connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
229 }
207 230
208 supported_npad_id_types.resize(npad_id_list.size()); 231 supported_npad_id_types.resize(npad_id_list.size());
209 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 232 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
210 npad_id_list.size() * sizeof(u32)); 233 npad_id_list.size() * sizeof(u32));
211 234
212 // Add a default dual joycon controller if none are present.
213 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
214 [](const ControllerHolder& controller) { return controller.is_connected; })) {
215 supported_npad_id_types.resize(npad_id_list.size());
216 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
217 npad_id_list.size() * sizeof(u32));
218 AddNewController(NPadControllerType::JoyDual);
219 }
220
221 for (std::size_t i = 0; i < connected_controllers.size(); ++i) { 235 for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
222 const auto& controller = connected_controllers[i]; 236 const auto& controller = connected_controllers[i];
223 if (controller.is_connected) { 237 if (controller.is_connected) {
224 AddNewControllerAt(controller.type, IndexToNPad(i)); 238 AddNewControllerAt(controller.type, i);
225 } 239 }
226 } 240 }
227} 241}
@@ -309,8 +323,9 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
309 323
310void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 324void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
311 std::size_t data_len) { 325 std::size_t data_len) {
312 if (!IsControllerActivated()) 326 if (!IsControllerActivated()) {
313 return; 327 return;
328 }
314 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { 329 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
315 auto& npad = shared_memory_entries[i]; 330 auto& npad = shared_memory_entries[i];
316 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states, 331 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
@@ -360,13 +375,25 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
360 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; 375 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
361 376
362 libnx_entry.connection_status.raw = 0; 377 libnx_entry.connection_status.raw = 0;
378 libnx_entry.connection_status.IsConnected.Assign(1);
363 379
364 switch (controller_type) { 380 switch (controller_type) {
365 case NPadControllerType::None: 381 case NPadControllerType::None:
366 UNREACHABLE(); 382 UNREACHABLE();
367 break; 383 break;
384 case NPadControllerType::ProController:
385 main_controller.connection_status.raw = 0;
386 main_controller.connection_status.IsConnected.Assign(1);
387 main_controller.connection_status.IsWired.Assign(1);
388 main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
389 main_controller.pad.l_stick = pad_state.l_stick;
390 main_controller.pad.r_stick = pad_state.r_stick;
391
392 libnx_entry.connection_status.IsWired.Assign(1);
393 break;
368 case NPadControllerType::Handheld: 394 case NPadControllerType::Handheld:
369 handheld_entry.connection_status.raw = 0; 395 handheld_entry.connection_status.raw = 0;
396 handheld_entry.connection_status.IsConnected.Assign(1);
370 handheld_entry.connection_status.IsWired.Assign(1); 397 handheld_entry.connection_status.IsWired.Assign(1);
371 handheld_entry.connection_status.IsLeftJoyConnected.Assign(1); 398 handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
372 handheld_entry.connection_status.IsRightJoyConnected.Assign(1); 399 handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
@@ -375,57 +402,52 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
375 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw; 402 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
376 handheld_entry.pad.l_stick = pad_state.l_stick; 403 handheld_entry.pad.l_stick = pad_state.l_stick;
377 handheld_entry.pad.r_stick = pad_state.r_stick; 404 handheld_entry.pad.r_stick = pad_state.r_stick;
405
406 libnx_entry.connection_status.IsWired.Assign(1);
407 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
408 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
409 libnx_entry.connection_status.IsLeftJoyWired.Assign(1);
410 libnx_entry.connection_status.IsRightJoyWired.Assign(1);
378 break; 411 break;
379 case NPadControllerType::JoyDual: 412 case NPadControllerType::JoyDual:
380 dual_entry.connection_status.raw = 0; 413 dual_entry.connection_status.raw = 0;
381 414 dual_entry.connection_status.IsConnected.Assign(1);
382 dual_entry.connection_status.IsLeftJoyConnected.Assign(1); 415 dual_entry.connection_status.IsLeftJoyConnected.Assign(1);
383 dual_entry.connection_status.IsRightJoyConnected.Assign(1); 416 dual_entry.connection_status.IsRightJoyConnected.Assign(1);
384 dual_entry.connection_status.IsConnected.Assign(1);
385
386 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
387 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
388 libnx_entry.connection_status.IsConnected.Assign(1);
389
390 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; 417 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
391 dual_entry.pad.l_stick = pad_state.l_stick; 418 dual_entry.pad.l_stick = pad_state.l_stick;
392 dual_entry.pad.r_stick = pad_state.r_stick; 419 dual_entry.pad.r_stick = pad_state.r_stick;
420
421 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
422 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
393 break; 423 break;
394 case NPadControllerType::JoyLeft: 424 case NPadControllerType::JoyLeft:
395 left_entry.connection_status.raw = 0; 425 left_entry.connection_status.raw = 0;
396
397 left_entry.connection_status.IsConnected.Assign(1); 426 left_entry.connection_status.IsConnected.Assign(1);
427 left_entry.connection_status.IsLeftJoyConnected.Assign(1);
398 left_entry.pad.pad_states.raw = pad_state.pad_states.raw; 428 left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
399 left_entry.pad.l_stick = pad_state.l_stick; 429 left_entry.pad.l_stick = pad_state.l_stick;
400 left_entry.pad.r_stick = pad_state.r_stick; 430 left_entry.pad.r_stick = pad_state.r_stick;
431
432 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
401 break; 433 break;
402 case NPadControllerType::JoyRight: 434 case NPadControllerType::JoyRight:
403 right_entry.connection_status.raw = 0; 435 right_entry.connection_status.raw = 0;
404
405 right_entry.connection_status.IsConnected.Assign(1); 436 right_entry.connection_status.IsConnected.Assign(1);
437 right_entry.connection_status.IsRightJoyConnected.Assign(1);
406 right_entry.pad.pad_states.raw = pad_state.pad_states.raw; 438 right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
407 right_entry.pad.l_stick = pad_state.l_stick; 439 right_entry.pad.l_stick = pad_state.l_stick;
408 right_entry.pad.r_stick = pad_state.r_stick; 440 right_entry.pad.r_stick = pad_state.r_stick;
441
442 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
409 break; 443 break;
410 case NPadControllerType::Pokeball: 444 case NPadControllerType::Pokeball:
411 pokeball_entry.connection_status.raw = 0; 445 pokeball_entry.connection_status.raw = 0;
412
413 pokeball_entry.connection_status.IsConnected.Assign(1); 446 pokeball_entry.connection_status.IsConnected.Assign(1);
414 pokeball_entry.connection_status.IsWired.Assign(1);
415
416 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw; 447 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
417 pokeball_entry.pad.l_stick = pad_state.l_stick; 448 pokeball_entry.pad.l_stick = pad_state.l_stick;
418 pokeball_entry.pad.r_stick = pad_state.r_stick; 449 pokeball_entry.pad.r_stick = pad_state.r_stick;
419 break; 450 break;
420 case NPadControllerType::ProController:
421 main_controller.connection_status.raw = 0;
422
423 main_controller.connection_status.IsConnected.Assign(1);
424 main_controller.connection_status.IsWired.Assign(1);
425 main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
426 main_controller.pad.l_stick = pad_state.l_stick;
427 main_controller.pad.r_stick = pad_state.r_stick;
428 break;
429 } 451 }
430 452
431 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate 453 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
@@ -453,26 +475,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
453 supported_npad_id_types.clear(); 475 supported_npad_id_types.clear();
454 supported_npad_id_types.resize(length / sizeof(u32)); 476 supported_npad_id_types.resize(length / sizeof(u32));
455 std::memcpy(supported_npad_id_types.data(), data, length); 477 std::memcpy(supported_npad_id_types.data(), data, length);
456 for (std::size_t i = 0; i < connected_controllers.size(); i++) {
457 auto& controller = connected_controllers[i];
458 if (!controller.is_connected) {
459 continue;
460 }
461 const auto requested_controller =
462 i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
463 : NPadControllerType::Handheld;
464 if (!IsControllerSupported(requested_controller)) {
465 const auto is_handheld = requested_controller == NPadControllerType::Handheld;
466 if (is_handheld) {
467 controller.type = NPadControllerType::None;
468 controller.is_connected = false;
469 AddNewController(requested_controller);
470 } else {
471 controller.type = requested_controller;
472 InitNewlyAddedControler(i);
473 }
474 }
475 }
476} 478}
477 479
478void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 480void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
@@ -504,7 +506,7 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
504 const std::vector<Vibration>& vibrations) { 506 const std::vector<Vibration>& vibrations) {
505 LOG_DEBUG(Service_HID, "(STUBBED) called"); 507 LOG_DEBUG(Service_HID, "(STUBBED) called");
506 508
507 if (!can_controllers_vibrate) { 509 if (!Settings::values.vibration_enabled || !can_controllers_vibrate) {
508 return; 510 return;
509 } 511 }
510 for (std::size_t i = 0; i < controller_ids.size(); i++) { 512 for (std::size_t i = 0; i < controller_ids.size(); i++) {
@@ -517,8 +519,6 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
517} 519}
518 520
519std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { 521std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
520 // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
521 // be signalled at least once, and signaled after a new controller is connected?
522 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; 522 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
523 return styleset_event.readable; 523 return styleset_event.readable;
524} 524}
@@ -527,43 +527,43 @@ Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
527 return last_processed_vibration; 527 return last_processed_vibration;
528} 528}
529 529
530void Controller_NPad::AddNewController(NPadControllerType controller) { 530void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
531 controller = DecideBestController(controller); 531 UpdateControllerAt(controller, npad_index, true);
532 if (controller == NPadControllerType::Handheld) { 532}
533 connected_controllers[8] = {controller, true}; 533
534 InitNewlyAddedControler(8); 534void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
535 return; 535 bool connected) {
536 } 536 if (!connected) {
537 const auto pos = 537 DisconnectNPad(IndexToNPad(npad_index));
538 std::find_if(connected_controllers.begin(), connected_controllers.end() - 2,
539 [](const ControllerHolder& holder) { return !holder.is_connected; });
540 if (pos == connected_controllers.end() - 2) {
541 LOG_ERROR(Service_HID, "Cannot connect any more controllers!");
542 return; 538 return;
543 } 539 }
544 const auto controller_id = std::distance(connected_controllers.begin(), pos);
545 connected_controllers[controller_id] = {controller, true};
546 InitNewlyAddedControler(controller_id);
547}
548 540
549void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
550 controller = DecideBestController(controller);
551 if (controller == NPadControllerType::Handheld) { 541 if (controller == NPadControllerType::Handheld) {
552 connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true}; 542 Settings::values.players[HANDHELD_INDEX].controller_type =
553 InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD)); 543 MapNPadToSettingsType(controller);
544 Settings::values.players[HANDHELD_INDEX].connected = true;
545 connected_controllers[HANDHELD_INDEX] = {controller, true};
546 InitNewlyAddedController(HANDHELD_INDEX);
554 return; 547 return;
555 } 548 }
556 549
557 connected_controllers[NPadIdToIndex(npad_id)] = {controller, true}; 550 Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller);
558 InitNewlyAddedControler(NPadIdToIndex(npad_id)); 551 Settings::values.players[npad_index].connected = true;
559} 552 connected_controllers[npad_index] = {controller, true};
560 553 InitNewlyAddedController(npad_index);
561void Controller_NPad::ConnectNPad(u32 npad_id) {
562 connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
563} 554}
564 555
565void Controller_NPad::DisconnectNPad(u32 npad_id) { 556void Controller_NPad::DisconnectNPad(u32 npad_id) {
566 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; 557 const auto npad_index = NPadIdToIndex(npad_id);
558 connected_controllers[npad_index].is_connected = false;
559 Settings::values.players[npad_index].connected = false;
560
561 auto& controller = shared_memory_entries[npad_index];
562 controller.joy_styles.raw = 0; // Zero out
563 controller.device_type.raw = 0;
564 controller.properties.raw = 0;
565
566 styleset_changed_events[npad_index].writable->Signal();
567} 567}
568 568
569void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { 569void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
@@ -599,8 +599,8 @@ bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) {
599 599
600 std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); 600 std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type);
601 601
602 InitNewlyAddedControler(npad_index_1); 602 AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1);
603 InitNewlyAddedControler(npad_index_2); 603 AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2);
604 604
605 return true; 605 return true;
606} 606}
@@ -614,11 +614,11 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
614 case 0: 614 case 0:
615 return LedPattern{1, 0, 0, 0}; 615 return LedPattern{1, 0, 0, 0};
616 case 1: 616 case 1:
617 return LedPattern{0, 1, 0, 0}; 617 return LedPattern{1, 1, 0, 0};
618 case 2: 618 case 2:
619 return LedPattern{0, 0, 1, 0}; 619 return LedPattern{1, 1, 1, 0};
620 case 3: 620 case 3:
621 return LedPattern{0, 0, 0, 1}; 621 return LedPattern{1, 1, 1, 1};
622 case 4: 622 case 4:
623 return LedPattern{1, 0, 0, 1}; 623 return LedPattern{1, 0, 0, 1};
624 case 5: 624 case 5:
@@ -628,9 +628,8 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
628 case 7: 628 case 7:
629 return LedPattern{0, 1, 1, 0}; 629 return LedPattern{0, 1, 1, 0};
630 default: 630 default:
631 UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
632 return LedPattern{0, 0, 0, 0}; 631 return LedPattern{0, 0, 0, 0};
633 }; 632 }
634} 633}
635 634
636void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { 635void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 5d4c58a43..75ce5b731 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -118,10 +118,11 @@ public:
118 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const; 118 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
119 Vibration GetLastVibration() const; 119 Vibration GetLastVibration() const;
120 120
121 void AddNewController(NPadControllerType controller); 121 // Adds a new controller at an index.
122 void AddNewControllerAt(NPadControllerType controller, u32 npad_id); 122 void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index);
123 // Adds a new controller at an index with connection status.
124 void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
123 125
124 void ConnectNPad(u32 npad_id);
125 void DisconnectNPad(u32 npad_id); 126 void DisconnectNPad(u32 npad_id);
126 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); 127 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
127 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; 128 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
@@ -141,6 +142,8 @@ public:
141 // Specifically for cheat engine and other features. 142 // Specifically for cheat engine and other features.
142 u32 GetAndResetPressState(); 143 u32 GetAndResetPressState();
143 144
145 static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type);
146 static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type);
144 static std::size_t NPadIdToIndex(u32 npad_id); 147 static std::size_t NPadIdToIndex(u32 npad_id);
145 static u32 IndexToNPad(std::size_t index); 148 static u32 IndexToNPad(std::size_t index);
146 149
@@ -309,7 +312,7 @@ private:
309 bool is_connected; 312 bool is_connected;
310 }; 313 };
311 314
312 void InitNewlyAddedControler(std::size_t controller_idx); 315 void InitNewlyAddedController(std::size_t controller_idx);
313 bool IsControllerSupported(NPadControllerType controller) const; 316 bool IsControllerSupported(NPadControllerType controller) const;
314 NPadControllerType DecideBestController(NPadControllerType priority) const; 317 NPadControllerType DecideBestController(NPadControllerType priority) const;
315 void RequestPadStateUpdate(u32 npad_id); 318 void RequestPadStateUpdate(u32 npad_id);
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 1e95b7580..33416b5dd 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -38,11 +38,9 @@
38namespace Service::HID { 38namespace Service::HID {
39 39
40// Updating period for each HID device. 40// Updating period for each HID device.
41// TODO(ogniK): Find actual polling rate of hid 41// HID is polled every 15ms, this value was derived from
42constexpr auto pad_update_ns = std::chrono::nanoseconds{1000000000 / 66}; 42// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
43[[maybe_unused]] constexpr auto accelerometer_update_ns = 43constexpr auto pad_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.6Hz)
44 std::chrono::nanoseconds{1000000000 / 100};
45[[maybe_unused]] constexpr auto gyroscope_update_ticks = std::chrono::nanoseconds{1000000000 / 100};
46constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 44constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
47 45
48IAppletResource::IAppletResource(Core::System& system) 46IAppletResource::IAppletResource(Core::System& system)
@@ -845,8 +843,7 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
845void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { 843void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
846 IPC::RequestParser rp{ctx}; 844 IPC::RequestParser rp{ctx};
847 const auto can_vibrate{rp.Pop<bool>()}; 845 const auto can_vibrate{rp.Pop<bool>()};
848 applet_resource->GetController<Controller_NPad>(HidController::NPad) 846 Settings::values.vibration_enabled = can_vibrate;
849 .SetVibrationEnabled(can_vibrate);
850 847
851 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); 848 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
852 849
@@ -859,8 +856,7 @@ void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
859 856
860 IPC::ResponseBuilder rb{ctx, 3}; 857 IPC::ResponseBuilder rb{ctx, 3};
861 rb.Push(RESULT_SUCCESS); 858 rb.Push(RESULT_SUCCESS);
862 rb.Push( 859 rb.Push(Settings::values.vibration_enabled);
863 applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled());
864} 860}
865 861
866void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 862void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 1b52511a5..0240d6643 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -21,8 +21,9 @@ namespace Service::Nvidia::Devices {
21/// implement the ioctl interface. 21/// implement the ioctl interface.
22class nvdevice { 22class nvdevice {
23public: 23public:
24 explicit nvdevice(Core::System& system) : system{system} {}; 24 explicit nvdevice(Core::System& system) : system{system} {}
25 virtual ~nvdevice() = default; 25 virtual ~nvdevice() = default;
26
26 union Ioctl { 27 union Ioctl {
27 u32_le raw; 28 u32_le raw;
28 BitField<0, 8, u32> cmd; 29 BitField<0, 8, u32> cmd;
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index ff85cbba6..1ebe949c0 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -86,11 +86,13 @@ public:
86 86
87 [[nodiscard]] s64 GetNextTicks() const; 87 [[nodiscard]] s64 GetNextTicks() const;
88 88
89 [[nodiscard]] std::unique_lock<std::mutex> Lock() const { return std::unique_lock{*guard}; } 89 [[nodiscard]] std::unique_lock<std::mutex> Lock() const {
90 return std::unique_lock{*guard};
91 }
90 92
91 private : 93private:
92 /// Finds the display identified by the specified ID. 94 /// Finds the display identified by the specified ID.
93 [[nodiscard]] VI::Display* FindDisplay(u64 display_id); 95 [[nodiscard]] VI::Display* FindDisplay(u64 display_id);
94 96
95 /// Finds the display identified by the specified ID. 97 /// Finds the display identified by the specified ID.
96 [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const; 98 [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const;
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index b526a94fe..aabf166b7 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -57,7 +57,7 @@ public:
57 ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name); 57 ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name);
58 ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name); 58 ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name);
59 59
60 template <Common::IsBaseOf<Kernel::SessionRequestHandler> T> 60 template <Common::DerivedFrom<Kernel::SessionRequestHandler> T>
61 std::shared_ptr<T> GetService(const std::string& service_name) const { 61 std::shared_ptr<T> GetService(const std::string& service_name) const {
62 auto service = registered_services.find(service_name); 62 auto service = registered_services.find(service_name);
63 if (service == registered_services.end()) { 63 if (service == registered_services.end()) {
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
index c070d6e97..320672add 100644
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -73,10 +73,8 @@ TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::
73 73
74 std::string location_name; 74 std::string location_name;
75 const auto timezone_setting = Settings::GetTimeZoneString(); 75 const auto timezone_setting = Settings::GetTimeZoneString();
76 if (timezone_setting == "auto") { 76 if (timezone_setting == "auto" || timezone_setting == "default") {
77 location_name = Common::TimeZone::GetDefaultTimeZone(); 77 location_name = Common::TimeZone::GetDefaultTimeZone();
78 } else if (timezone_setting == "default") {
79 location_name = location_name;
80 } else { 78 } else {
81 location_name = timezone_setting; 79 location_name = timezone_setting;
82 } 80 }
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index b8f8f1448..9bc3a8840 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -25,7 +25,7 @@ namespace Loader {
25 25
26namespace { 26namespace {
27 27
28template <Common::IsBaseOf<AppLoader> T> 28template <Common::DerivedFrom<AppLoader> T>
29std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) { 29std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
30 const auto file_type = T::IdentifyType(file); 30 const auto file_type = T::IdentifyType(file);
31 if (file_type != FileType::Error) { 31 if (file_type != FileType::Error) {
@@ -67,7 +67,7 @@ FileType GuessFromFilename(const std::string& name) {
67 return FileType::NCA; 67 return FileType::NCA;
68 68
69 const std::string extension = 69 const std::string extension =
70 Common::ToLower(std::string(FileUtil::GetExtensionFromFilename(name))); 70 Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
71 71
72 if (extension == "elf") 72 if (extension == "elf")
73 return FileType::ELF; 73 return FileType::ELF;
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index eeebdf02e..e503118dd 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -42,7 +42,7 @@ u64 StandardVmCallbacks::HidKeysDown() {
42 if (applet_resource == nullptr) { 42 if (applet_resource == nullptr) {
43 LOG_WARNING(CheatEngine, 43 LOG_WARNING(CheatEngine,
44 "Attempted to read input state, but applet resource is not initialized!"); 44 "Attempted to read input state, but applet resource is not initialized!");
45 return false; 45 return 0;
46 } 46 }
47 47
48 const auto press_state = 48 const auto press_state =
@@ -199,17 +199,29 @@ void CheatEngine::Initialize() {
199 metadata.title_id = system.CurrentProcess()->GetTitleID(); 199 metadata.title_id = system.CurrentProcess()->GetTitleID();
200 200
201 const auto& page_table = system.CurrentProcess()->PageTable(); 201 const auto& page_table = system.CurrentProcess()->PageTable();
202 metadata.heap_extents = {page_table.GetHeapRegionStart(), page_table.GetHeapRegionSize()}; 202 metadata.heap_extents = {
203 metadata.address_space_extents = {page_table.GetAddressSpaceStart(), 203 .base = page_table.GetHeapRegionStart(),
204 page_table.GetAddressSpaceSize()}; 204 .size = page_table.GetHeapRegionSize(),
205 metadata.alias_extents = {page_table.GetAliasCodeRegionStart(), 205 };
206 page_table.GetAliasCodeRegionSize()}; 206
207 metadata.address_space_extents = {
208 .base = page_table.GetAddressSpaceStart(),
209 .size = page_table.GetAddressSpaceSize(),
210 };
211
212 metadata.alias_extents = {
213 .base = page_table.GetAliasCodeRegionStart(),
214 .size = page_table.GetAliasCodeRegionSize(),
215 };
207 216
208 is_pending_reload.exchange(true); 217 is_pending_reload.exchange(true);
209} 218}
210 219
211void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) { 220void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
212 metadata.main_nso_extents = {main_region_begin, main_region_size}; 221 metadata.main_nso_extents = {
222 .base = main_region_begin,
223 .size = main_region_size,
224 };
213} 225}
214 226
215void CheatEngine::Reload(std::vector<CheatEntry> cheats) { 227void CheatEngine::Reload(std::vector<CheatEntry> cheats) {
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index b899ac884..b93396a80 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -38,11 +38,11 @@ PerfStats::~PerfStats() {
38 std::ostringstream stream; 38 std::ostringstream stream;
39 std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index, 39 std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index,
40 std::ostream_iterator<double>(stream, "\n")); 40 std::ostream_iterator<double>(stream, "\n"));
41 const std::string& path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 41 const std::string& path = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
42 // %F Date format expanded is "%Y-%m-%d" 42 // %F Date format expanded is "%Y-%m-%d"
43 const std::string filename = 43 const std::string filename =
44 fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id); 44 fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id);
45 FileUtil::IOFile file(filename, "w"); 45 Common::FS::IOFile file(filename, "w");
46 file.WriteString(stream.str()); 46 file.WriteString(stream.str());
47} 47}
48 48
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index 76cfa5a17..0becdf642 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -28,8 +28,9 @@
28namespace { 28namespace {
29 29
30std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) { 30std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) {
31 return fmt::format("{}{}/{:016X}_{}.json", FileUtil::GetUserPath(FileUtil::UserPath::LogDir), 31 return fmt::format("{}{}/{:016X}_{}.json",
32 type, title_id, timestamp); 32 Common::FS::GetUserPath(Common::FS::UserPath::LogDir), type, title_id,
33 timestamp);
33} 34}
34 35
35std::string GetTimestamp() { 36std::string GetTimestamp() {
@@ -40,13 +41,13 @@ std::string GetTimestamp() {
40using namespace nlohmann; 41using namespace nlohmann;
41 42
42void SaveToFile(json json, const std::string& filename) { 43void SaveToFile(json json, const std::string& filename) {
43 if (!FileUtil::CreateFullPath(filename)) { 44 if (!Common::FS::CreateFullPath(filename)) {
44 LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename); 45 LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename);
45 return; 46 return;
46 } 47 }
47 48
48 std::ofstream file( 49 std::ofstream file(
49 FileUtil::SanitizePath(filename, FileUtil::DirectorySeparator::PlatformDefault)); 50 Common::FS::SanitizePath(filename, Common::FS::DirectorySeparator::PlatformDefault));
50 file << std::setw(4) << json << std::endl; 51 file << std::setw(4) << json << std::endl;
51} 52}
52 53
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 416b2d866..28d3f9099 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -13,56 +13,6 @@
13 13
14namespace Settings { 14namespace Settings {
15 15
16namespace NativeButton {
17const std::array<const char*, NumButtons> mapping = {{
18 "button_a",
19 "button_b",
20 "button_x",
21 "button_y",
22 "button_lstick",
23 "button_rstick",
24 "button_l",
25 "button_r",
26 "button_zl",
27 "button_zr",
28 "button_plus",
29 "button_minus",
30 "button_dleft",
31 "button_dup",
32 "button_dright",
33 "button_ddown",
34 "button_lstick_left",
35 "button_lstick_up",
36 "button_lstick_right",
37 "button_lstick_down",
38 "button_rstick_left",
39 "button_rstick_up",
40 "button_rstick_right",
41 "button_rstick_down",
42 "button_sl",
43 "button_sr",
44 "button_home",
45 "button_screenshot",
46}};
47}
48
49namespace NativeAnalog {
50const std::array<const char*, NumAnalogs> mapping = {{
51 "lstick",
52 "rstick",
53}};
54}
55
56namespace NativeMouseButton {
57const std::array<const char*, NumMouseButtons> mapping = {{
58 "left",
59 "right",
60 "middle",
61 "forward",
62 "back",
63}};
64}
65
66Values values = {}; 16Values values = {};
67bool configuring_global = true; 17bool configuring_global = true;
68 18
@@ -121,8 +71,8 @@ void LogSettings() {
121 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); 71 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
122 log_setting("Audio_OutputDevice", values.audio_device_id); 72 log_setting("Audio_OutputDevice", values.audio_device_id);
123 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd); 73 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd);
124 log_setting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); 74 log_setting("DataStorage_NandDir", Common::FS::GetUserPath(Common::FS::UserPath::NANDDir));
125 log_setting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)); 75 log_setting("DataStorage_SdmcDir", Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir));
126 log_setting("Debugging_UseGdbstub", values.use_gdbstub); 76 log_setting("Debugging_UseGdbstub", values.use_gdbstub);
127 log_setting("Debugging_GdbstubPort", values.gdbstub_port); 77 log_setting("Debugging_GdbstubPort", values.gdbstub_port);
128 log_setting("Debugging_ProgramArgs", values.program_args); 78 log_setting("Debugging_ProgramArgs", values.program_args);
diff --git a/src/core/settings.h b/src/core/settings.h
index bb145f193..732c6a894 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -12,340 +12,10 @@
12#include <string> 12#include <string>
13#include <vector> 13#include <vector>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "input_common/settings.h"
15 16
16namespace Settings { 17namespace Settings {
17 18
18namespace NativeButton {
19enum Values {
20 A,
21 B,
22 X,
23 Y,
24 LStick,
25 RStick,
26 L,
27 R,
28 ZL,
29 ZR,
30 Plus,
31 Minus,
32
33 DLeft,
34 DUp,
35 DRight,
36 DDown,
37
38 LStick_Left,
39 LStick_Up,
40 LStick_Right,
41 LStick_Down,
42
43 RStick_Left,
44 RStick_Up,
45 RStick_Right,
46 RStick_Down,
47
48 SL,
49 SR,
50
51 Home,
52 Screenshot,
53
54 NumButtons,
55};
56
57constexpr int BUTTON_HID_BEGIN = A;
58constexpr int BUTTON_NS_BEGIN = Home;
59
60constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
61constexpr int BUTTON_NS_END = NumButtons;
62
63constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
64constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
65
66extern const std::array<const char*, NumButtons> mapping;
67
68} // namespace NativeButton
69
70namespace NativeAnalog {
71enum Values {
72 LStick,
73 RStick,
74
75 NumAnalogs,
76};
77
78constexpr int STICK_HID_BEGIN = LStick;
79constexpr int STICK_HID_END = NumAnalogs;
80constexpr int NUM_STICKS_HID = NumAnalogs;
81
82extern const std::array<const char*, NumAnalogs> mapping;
83} // namespace NativeAnalog
84
85namespace NativeMouseButton {
86enum Values {
87 Left,
88 Right,
89 Middle,
90 Forward,
91 Back,
92
93 NumMouseButtons,
94};
95
96constexpr int MOUSE_HID_BEGIN = Left;
97constexpr int MOUSE_HID_END = NumMouseButtons;
98constexpr int NUM_MOUSE_HID = NumMouseButtons;
99
100extern const std::array<const char*, NumMouseButtons> mapping;
101} // namespace NativeMouseButton
102
103namespace NativeKeyboard {
104enum Keys {
105 None,
106 Error,
107
108 A = 4,
109 B,
110 C,
111 D,
112 E,
113 F,
114 G,
115 H,
116 I,
117 J,
118 K,
119 L,
120 M,
121 N,
122 O,
123 P,
124 Q,
125 R,
126 S,
127 T,
128 U,
129 V,
130 W,
131 X,
132 Y,
133 Z,
134 N1,
135 N2,
136 N3,
137 N4,
138 N5,
139 N6,
140 N7,
141 N8,
142 N9,
143 N0,
144 Enter,
145 Escape,
146 Backspace,
147 Tab,
148 Space,
149 Minus,
150 Equal,
151 LeftBrace,
152 RightBrace,
153 Backslash,
154 Tilde,
155 Semicolon,
156 Apostrophe,
157 Grave,
158 Comma,
159 Dot,
160 Slash,
161 CapsLockKey,
162
163 F1,
164 F2,
165 F3,
166 F4,
167 F5,
168 F6,
169 F7,
170 F8,
171 F9,
172 F10,
173 F11,
174 F12,
175
176 SystemRequest,
177 ScrollLockKey,
178 Pause,
179 Insert,
180 Home,
181 PageUp,
182 Delete,
183 End,
184 PageDown,
185 Right,
186 Left,
187 Down,
188 Up,
189
190 NumLockKey,
191 KPSlash,
192 KPAsterisk,
193 KPMinus,
194 KPPlus,
195 KPEnter,
196 KP1,
197 KP2,
198 KP3,
199 KP4,
200 KP5,
201 KP6,
202 KP7,
203 KP8,
204 KP9,
205 KP0,
206 KPDot,
207
208 Key102,
209 Compose,
210 Power,
211 KPEqual,
212
213 F13,
214 F14,
215 F15,
216 F16,
217 F17,
218 F18,
219 F19,
220 F20,
221 F21,
222 F22,
223 F23,
224 F24,
225
226 Open,
227 Help,
228 Properties,
229 Front,
230 Stop,
231 Repeat,
232 Undo,
233 Cut,
234 Copy,
235 Paste,
236 Find,
237 Mute,
238 VolumeUp,
239 VolumeDown,
240 CapsLockActive,
241 NumLockActive,
242 ScrollLockActive,
243 KPComma,
244
245 KPLeftParenthesis,
246 KPRightParenthesis,
247
248 LeftControlKey = 0xE0,
249 LeftShiftKey,
250 LeftAltKey,
251 LeftMetaKey,
252 RightControlKey,
253 RightShiftKey,
254 RightAltKey,
255 RightMetaKey,
256
257 MediaPlayPause,
258 MediaStopCD,
259 MediaPrevious,
260 MediaNext,
261 MediaEject,
262 MediaVolumeUp,
263 MediaVolumeDown,
264 MediaMute,
265 MediaWebsite,
266 MediaBack,
267 MediaForward,
268 MediaStop,
269 MediaFind,
270 MediaScrollUp,
271 MediaScrollDown,
272 MediaEdit,
273 MediaSleep,
274 MediaCoffee,
275 MediaRefresh,
276 MediaCalculator,
277
278 NumKeyboardKeys,
279};
280
281static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
282
283enum Modifiers {
284 LeftControl,
285 LeftShift,
286 LeftAlt,
287 LeftMeta,
288 RightControl,
289 RightShift,
290 RightAlt,
291 RightMeta,
292 CapsLock,
293 ScrollLock,
294 NumLock,
295
296 NumKeyboardMods,
297};
298
299constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
300constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
301constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
302
303constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
304constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
305constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
306
307} // namespace NativeKeyboard
308
309using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
310using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
311using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
312using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
313using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
314
315constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
316constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
317constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
318constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
319
320enum class ControllerType {
321 ProController,
322 DualJoycon,
323 RightJoycon,
324 LeftJoycon,
325};
326
327struct PlayerInput {
328 bool connected;
329 ControllerType type;
330 ButtonsRaw buttons;
331 AnalogsRaw analogs;
332
333 u32 body_color_right;
334 u32 button_color_right;
335 u32 body_color_left;
336 u32 button_color_left;
337};
338
339struct TouchscreenInput {
340 bool enabled;
341 std::string device;
342
343 u32 finger;
344 u32 diameter_x;
345 u32 diameter_y;
346 u32 rotation_angle;
347};
348
349enum class RendererBackend { 19enum class RendererBackend {
350 OpenGL = 0, 20 OpenGL = 0,
351 Vulkan = 1, 21 Vulkan = 1,
@@ -359,7 +29,8 @@ enum class GPUAccuracy : u32 {
359 29
360enum class CPUAccuracy { 30enum class CPUAccuracy {
361 Accurate = 0, 31 Accurate = 0,
362 DebugMode = 1, 32 Unsafe = 1,
33 DebugMode = 2,
363}; 34};
364 35
365extern bool configuring_global; 36extern bool configuring_global;
@@ -419,6 +90,9 @@ struct Values {
419 bool cpuopt_misc_ir; 90 bool cpuopt_misc_ir;
420 bool cpuopt_reduce_misalign_checks; 91 bool cpuopt_reduce_misalign_checks;
421 92
93 bool cpuopt_unsafe_unfuse_fma;
94 bool cpuopt_unsafe_reduce_fp_error;
95
422 // Renderer 96 // Renderer
423 Setting<RendererBackend> renderer_backend; 97 Setting<RendererBackend> renderer_backend;
424 bool renderer_debug; 98 bool renderer_debug;
@@ -457,6 +131,8 @@ struct Values {
457 // Controls 131 // Controls
458 std::array<PlayerInput, 10> players; 132 std::array<PlayerInput, 10> players;
459 133
134 bool use_docked_mode;
135
460 bool mouse_enabled; 136 bool mouse_enabled;
461 std::string mouse_device; 137 std::string mouse_device;
462 MouseButtonsRaw mouse_buttons; 138 MouseButtonsRaw mouse_buttons;
@@ -470,14 +146,15 @@ struct Values {
470 AnalogsRaw debug_pad_analogs; 146 AnalogsRaw debug_pad_analogs;
471 147
472 std::string motion_device; 148 std::string motion_device;
149
150 bool vibration_enabled;
151
473 TouchscreenInput touchscreen; 152 TouchscreenInput touchscreen;
474 std::atomic_bool is_device_reload_pending{true}; 153 std::atomic_bool is_device_reload_pending{true};
475 std::string udp_input_address; 154 std::string udp_input_address;
476 u16 udp_input_port; 155 u16 udp_input_port;
477 u8 udp_pad_index; 156 u8 udp_pad_index;
478 157
479 bool use_docked_mode;
480
481 // Data Storage 158 // Data Storage
482 bool use_virtual_sd; 159 bool use_virtual_sd;
483 bool gamecard_inserted; 160 bool gamecard_inserted;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 5a30c75da..da09c0dbc 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -25,6 +25,8 @@
25 25
26namespace Core { 26namespace Core {
27 27
28namespace Telemetry = Common::Telemetry;
29
28static u64 GenerateTelemetryId() { 30static u64 GenerateTelemetryId() {
29 u64 telemetry_id{}; 31 u64 telemetry_id{};
30 32
@@ -70,12 +72,12 @@ static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) {
70 72
71u64 GetTelemetryId() { 73u64 GetTelemetryId() {
72 u64 telemetry_id{}; 74 u64 telemetry_id{};
73 const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + 75 const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) +
74 "telemetry_id"}; 76 "telemetry_id"};
75 77
76 bool generate_new_id = !FileUtil::Exists(filename); 78 bool generate_new_id = !Common::FS::Exists(filename);
77 if (!generate_new_id) { 79 if (!generate_new_id) {
78 FileUtil::IOFile file(filename, "rb"); 80 Common::FS::IOFile file(filename, "rb");
79 if (!file.IsOpen()) { 81 if (!file.IsOpen()) {
80 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 82 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
81 return {}; 83 return {};
@@ -88,7 +90,7 @@ u64 GetTelemetryId() {
88 } 90 }
89 91
90 if (generate_new_id) { 92 if (generate_new_id) {
91 FileUtil::IOFile file(filename, "wb"); 93 Common::FS::IOFile file(filename, "wb");
92 if (!file.IsOpen()) { 94 if (!file.IsOpen()) {
93 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 95 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
94 return {}; 96 return {};
@@ -102,10 +104,10 @@ u64 GetTelemetryId() {
102 104
103u64 RegenerateTelemetryId() { 105u64 RegenerateTelemetryId() {
104 const u64 new_telemetry_id{GenerateTelemetryId()}; 106 const u64 new_telemetry_id{GenerateTelemetryId()};
105 const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + 107 const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) +
106 "telemetry_id"}; 108 "telemetry_id"};
107 109
108 FileUtil::IOFile file(filename, "wb"); 110 Common::FS::IOFile file(filename, "wb");
109 if (!file.IsOpen()) { 111 if (!file.IsOpen()) {
110 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 112 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
111 return {}; 113 return {};
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index 17ac22377..66789d4bd 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -52,7 +52,7 @@ public:
52 * @param value Value for the field to add. 52 * @param value Value for the field to add.
53 */ 53 */
54 template <typename T> 54 template <typename T>
55 void AddField(Telemetry::FieldType type, const char* name, T value) { 55 void AddField(Common::Telemetry::FieldType type, const char* name, T value) {
56 field_collection.AddField(type, name, std::move(value)); 56 field_collection.AddField(type, name, std::move(value));
57 } 57 }
58 58
@@ -63,7 +63,8 @@ public:
63 bool SubmitTestcase(); 63 bool SubmitTestcase();
64 64
65private: 65private:
66 Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session 66 /// Tracks all added fields for the session
67 Common::Telemetry::FieldCollection field_collection;
67}; 68};
68 69
69/** 70/**
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 2003e096f..5c674a099 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -107,28 +107,21 @@ void Freezer::Unfreeze(VAddr address) {
107 107
108 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address); 108 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
109 109
110 entries.erase( 110 std::erase_if(entries, [address](const Entry& entry) { return entry.address == address; });
111 std::remove_if(entries.begin(), entries.end(),
112 [&address](const Entry& entry) { return entry.address == address; }),
113 entries.end());
114} 111}
115 112
116bool Freezer::IsFrozen(VAddr address) const { 113bool Freezer::IsFrozen(VAddr address) const {
117 std::lock_guard lock{entries_mutex}; 114 std::lock_guard lock{entries_mutex};
118 115
119 return std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { 116 return FindEntry(address) != entries.cend();
120 return entry.address == address;
121 }) != entries.end();
122} 117}
123 118
124void Freezer::SetFrozenValue(VAddr address, u64 value) { 119void Freezer::SetFrozenValue(VAddr address, u64 value) {
125 std::lock_guard lock{entries_mutex}; 120 std::lock_guard lock{entries_mutex};
126 121
127 const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { 122 const auto iter = FindEntry(address);
128 return entry.address == address;
129 });
130 123
131 if (iter == entries.end()) { 124 if (iter == entries.cend()) {
132 LOG_ERROR(Common_Memory, 125 LOG_ERROR(Common_Memory,
133 "Tried to set freeze value for address={:016X} that is not frozen!", address); 126 "Tried to set freeze value for address={:016X} that is not frozen!", address);
134 return; 127 return;
@@ -143,11 +136,9 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) {
143std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { 136std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
144 std::lock_guard lock{entries_mutex}; 137 std::lock_guard lock{entries_mutex};
145 138
146 const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { 139 const auto iter = FindEntry(address);
147 return entry.address == address;
148 });
149 140
150 if (iter == entries.end()) { 141 if (iter == entries.cend()) {
151 return std::nullopt; 142 return std::nullopt;
152 } 143 }
153 144
@@ -160,6 +151,16 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const {
160 return entries; 151 return entries;
161} 152}
162 153
154Freezer::Entries::iterator Freezer::FindEntry(VAddr address) {
155 return std::find_if(entries.begin(), entries.end(),
156 [address](const Entry& entry) { return entry.address == address; });
157}
158
159Freezer::Entries::const_iterator Freezer::FindEntry(VAddr address) const {
160 return std::find_if(entries.begin(), entries.end(),
161 [address](const Entry& entry) { return entry.address == address; });
162}
163
163void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { 164void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
164 if (!IsActive()) { 165 if (!IsActive()) {
165 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); 166 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h
index 2b2326bc4..0fdb701a7 100644
--- a/src/core/tools/freezer.h
+++ b/src/core/tools/freezer.h
@@ -73,13 +73,18 @@ public:
73 std::vector<Entry> GetEntries() const; 73 std::vector<Entry> GetEntries() const;
74 74
75private: 75private:
76 using Entries = std::vector<Entry>;
77
78 Entries::iterator FindEntry(VAddr address);
79 Entries::const_iterator FindEntry(VAddr address) const;
80
76 void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 81 void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
77 void FillEntryReads(); 82 void FillEntryReads();
78 83
79 std::atomic_bool active{false}; 84 std::atomic_bool active{false};
80 85
81 mutable std::mutex entries_mutex; 86 mutable std::mutex entries_mutex;
82 std::vector<Entry> entries; 87 Entries entries;
83 88
84 std::shared_ptr<Core::Timing::EventType> event; 89 std::shared_ptr<Core::Timing::EventType> event;
85 Core::Timing::CoreTiming& core_timing; 90 Core::Timing::CoreTiming& core_timing;