summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/algorithm.h3
-rw-r--r--src/common/alignment.h14
-rw-r--r--src/common/atomic_ops.h10
-rw-r--r--src/common/bit_field.h19
-rw-r--r--src/common/bit_util.h34
-rw-r--r--src/common/cityhash.h23
-rw-r--r--src/common/color.h38
-rw-r--r--src/common/common_funcs.h14
-rw-r--r--src/common/concepts.h4
-rw-r--r--src/common/dynamic_library.cpp2
-rw-r--r--src/common/dynamic_library.h13
-rw-r--r--src/common/fiber.h2
-rw-r--r--src/common/file_util.cpp68
-rw-r--r--src/common/file_util.h85
-rw-r--r--src/common/hash.h25
-rw-r--r--src/common/hex_util.h12
-rw-r--r--src/common/logging/backend.h2
-rw-r--r--src/common/lz4_compression.cpp16
-rw-r--r--src/common/lz4_compression.h24
-rw-r--r--src/common/math_util.h10
-rw-r--r--src/common/memory_detect.h2
-rw-r--r--src/common/multi_level_queue.h37
-rw-r--r--src/common/page_table.h4
-rw-r--r--src/common/param_package.h10
-rw-r--r--src/common/quaternion.h15
-rw-r--r--src/common/ring_buffer.h4
-rw-r--r--src/common/spin_lock.h2
-rw-r--r--src/common/string_util.h34
-rw-r--r--src/common/telemetry.cpp4
-rw-r--r--src/common/telemetry.h16
-rw-r--r--src/common/thread_queue_list.h10
-rw-r--r--src/common/threadsafe_queue.h12
-rw-r--r--src/common/time_zone.h4
-rw-r--r--src/common/timer.h16
-rw-r--r--src/common/uint128.h6
-rw-r--r--src/common/uuid.h14
-rw-r--r--src/common/vector_math.h204
-rw-r--r--src/common/virtual_buffer.h10
-rw-r--r--src/common/wall_clock.h16
-rw-r--r--src/common/zstd_compression.cpp1
-rw-r--r--src/common/zstd_compression.h17
-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.cpp439
-rw-r--r--src/core/crypto/key_manager.h8
-rw-r--r--src/core/crypto/partition_data_manager.cpp4
-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/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/xts_archive.cpp2
-rw-r--r--src/core/frontend/emu_window.h4
-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.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/applets/software_keyboard.cpp3
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp16
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp20
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp80
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h3
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp4
-rw-r--r--src/core/loader/loader.cpp2
-rw-r--r--src/core/perf_stats.cpp4
-rw-r--r--src/core/reporter.cpp9
-rw-r--r--src/core/settings.cpp4
-rw-r--r--src/core/settings.h6
-rw-r--r--src/core/telemetry_session.cpp14
-rw-r--r--src/core/telemetry_session.h5
-rw-r--r--src/video_core/CMakeLists.txt5
-rw-r--r--src/video_core/engines/fermi_2d.cpp10
-rw-r--r--src/video_core/engines/fermi_2d.h9
-rw-r--r--src/video_core/engines/kepler_compute.cpp17
-rw-r--r--src/video_core/engines/kepler_compute.h16
-rw-r--r--src/video_core/engines/maxwell_3d.cpp35
-rw-r--r--src/video_core/engines/maxwell_3d.h23
-rw-r--r--src/video_core/engines/maxwell_dma.cpp21
-rw-r--r--src/video_core/fence_manager.h5
-rw-r--r--src/video_core/gpu.cpp31
-rw-r--r--src/video_core/gpu.h13
-rw-r--r--src/video_core/gpu_asynch.cpp10
-rw-r--r--src/video_core/gpu_asynch.h4
-rw-r--r--src/video_core/gpu_synch.cpp8
-rw-r--r--src/video_core/gpu_synch.h6
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt43
-rw-r--r--src/video_core/host_shaders/StringShaderHeader.cmake11
-rw-r--r--src/video_core/host_shaders/opengl_present.frag10
-rw-r--r--src/video_core/host_shaders/opengl_present.vert24
-rw-r--r--src/video_core/host_shaders/source_shader.h.in9
-rw-r--r--src/video_core/macro/macro_hle.cpp6
-rw-r--r--src/video_core/macro/macro_interpreter.cpp1
-rw-r--r--src/video_core/memory_manager.cpp16
-rw-r--r--src/video_core/memory_manager.h41
-rw-r--r--src/video_core/renderer_base.cpp4
-rw-r--r--src/video_core/renderer_base.h17
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.h1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp9
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp57
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.h10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.h2
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp64
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h11
-rw-r--r--src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp14
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp10
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_device.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h28
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp37
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h31
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp30
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h16
-rw-r--r--src/video_core/renderer_vulkan/wrapper.cpp4
-rw-r--r--src/video_core/renderer_vulkan/wrapper.h2
-rw-r--r--src/video_core/shader/async_shaders.cpp94
-rw-r--r--src/video_core/shader/async_shaders.h39
-rw-r--r--src/video_core/textures/decoders.cpp42
-rw-r--r--src/video_core/textures/decoders.h5
-rw-r--r--src/video_core/video_core.cpp32
-rw-r--r--src/web_service/CMakeLists.txt1
-rw-r--r--src/web_service/telemetry_json.cpp6
-rw-r--r--src/web_service/telemetry_json.h30
-rw-r--r--src/web_service/verify_login.cpp2
-rw-r--r--src/web_service/web_backend.cpp56
-rw-r--r--src/web_service/web_backend.h20
-rw-r--r--src/web_service/web_result.h (renamed from src/common/web_result.h)4
-rw-r--r--src/yuzu/applets/profile_select.cpp2
-rw-r--r--src/yuzu/compatdb.cpp3
-rw-r--r--src/yuzu/configuration/config.cpp111
-rw-r--r--src/yuzu/configuration/configuration_shared.cpp60
-rw-r--r--src/yuzu/configuration/configuration_shared.h13
-rw-r--r--src/yuzu/configuration/configure_audio.cpp13
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp17
-rw-r--r--src/yuzu/configuration/configure_cpu.h1
-rw-r--r--src/yuzu/configuration/configure_cpu.ui52
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_filesystem.cpp37
-rw-r--r--src/yuzu/configuration/configure_general.cpp6
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp32
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp22
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui2
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp4
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp4
-rw-r--r--src/yuzu/configuration/configure_profile_manager.cpp4
-rw-r--r--src/yuzu/configuration/configure_system.cpp18
-rw-r--r--src/yuzu/configuration/configure_ui.cpp12
-rw-r--r--src/yuzu/debugger/profiler.cpp3
-rw-r--r--src/yuzu/game_list.cpp6
-rw-r--r--src/yuzu/game_list.h3
-rw-r--r--src/yuzu/game_list_worker.cpp28
-rw-r--r--src/yuzu/main.cpp146
-rw-r--r--src/yuzu/main.h5
-rw-r--r--src/yuzu/main.ui9
-rw-r--r--src/yuzu/uisettings.h8
-rw-r--r--src/yuzu_cmd/config.cpp38
-rw-r--r--src/yuzu_cmd/yuzu.cpp6
-rw-r--r--src/yuzu_tester/config.cpp21
-rw-r--r--src/yuzu_tester/yuzu.cpp7
181 files changed, 2142 insertions, 1676 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 78c3bfb3b..5d54516eb 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -172,7 +172,6 @@ add_library(common STATIC
172 virtual_buffer.h 172 virtual_buffer.h
173 wall_clock.cpp 173 wall_clock.cpp
174 wall_clock.h 174 wall_clock.h
175 web_result.h
176 zstd_compression.cpp 175 zstd_compression.cpp
177 zstd_compression.h 176 zstd_compression.h
178) 177)
diff --git a/src/common/algorithm.h b/src/common/algorithm.h
index e21b1373c..4804a3421 100644
--- a/src/common/algorithm.h
+++ b/src/common/algorithm.h
@@ -15,7 +15,8 @@
15namespace Common { 15namespace Common {
16 16
17template <class ForwardIt, class T, class Compare = std::less<>> 17template <class ForwardIt, class T, class Compare = std::less<>>
18ForwardIt BinaryFind(ForwardIt first, ForwardIt last, const T& value, Compare comp = {}) { 18[[nodiscard]] ForwardIt BinaryFind(ForwardIt first, ForwardIt last, const T& value,
19 Compare comp = {}) {
19 // Note: BOTH type T and the type after ForwardIt is dereferenced 20 // Note: BOTH type T and the type after ForwardIt is dereferenced
20 // must be implicitly convertible to BOTH Type1 and Type2, used in Compare. 21 // must be implicitly convertible to BOTH Type1 and Type2, used in Compare.
21 // This is stricter than lower_bound requirement (see above) 22 // This is stricter than lower_bound requirement (see above)
diff --git a/src/common/alignment.h b/src/common/alignment.h
index ef4d6f896..5040043de 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -9,7 +9,7 @@
9namespace Common { 9namespace Common {
10 10
11template <typename T> 11template <typename T>
12constexpr T AlignUp(T value, std::size_t size) { 12[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) {
13 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 13 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
14 auto mod{static_cast<T>(value % size)}; 14 auto mod{static_cast<T>(value % size)};
15 value -= mod; 15 value -= mod;
@@ -17,31 +17,31 @@ constexpr T AlignUp(T value, std::size_t size) {
17} 17}
18 18
19template <typename T> 19template <typename T>
20constexpr T AlignDown(T value, std::size_t size) { 20[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) {
21 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 21 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
22 return static_cast<T>(value - value % size); 22 return static_cast<T>(value - value % size);
23} 23}
24 24
25template <typename T> 25template <typename T>
26constexpr T AlignBits(T value, std::size_t align) { 26[[nodiscard]] constexpr T AlignBits(T value, std::size_t align) {
27 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 27 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
28 return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align); 28 return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align);
29} 29}
30 30
31template <typename T> 31template <typename T>
32constexpr bool Is4KBAligned(T value) { 32[[nodiscard]] constexpr bool Is4KBAligned(T value) {
33 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 33 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
34 return (value & 0xFFF) == 0; 34 return (value & 0xFFF) == 0;
35} 35}
36 36
37template <typename T> 37template <typename T>
38constexpr bool IsWordAligned(T value) { 38[[nodiscard]] constexpr bool IsWordAligned(T value) {
39 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 39 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
40 return (value & 0b11) == 0; 40 return (value & 0b11) == 0;
41} 41}
42 42
43template <typename T> 43template <typename T>
44constexpr bool IsAligned(T value, std::size_t alignment) { 44[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) {
45 using U = typename std::make_unsigned<T>::type; 45 using U = typename std::make_unsigned<T>::type;
46 const U mask = static_cast<U>(alignment - 1); 46 const U mask = static_cast<U>(alignment - 1);
47 return (value & mask) == 0; 47 return (value & mask) == 0;
@@ -64,7 +64,7 @@ public:
64 template <typename T2> 64 template <typename T2>
65 constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {} 65 constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {}
66 66
67 T* allocate(size_type n) { 67 [[nodiscard]] T* allocate(size_type n) {
68 return static_cast<T*>(::operator new (n * sizeof(T), std::align_val_t{Align})); 68 return static_cast<T*>(::operator new (n * sizeof(T), std::align_val_t{Align}));
69 } 69 }
70 70
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h
index 8d6b73c00..b46888589 100644
--- a/src/common/atomic_ops.h
+++ b/src/common/atomic_ops.h
@@ -8,10 +8,10 @@
8 8
9namespace Common { 9namespace Common {
10 10
11bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected); 11[[nodiscard]] bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected);
12bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected); 12[[nodiscard]] bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected);
13bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected); 13[[nodiscard]] bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected);
14bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected); 14[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected);
15bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected); 15[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected);
16 16
17} // namespace Common 17} // namespace Common
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 26ae6c7fc..0f0661172 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -36,13 +36,6 @@
36#include "common/common_funcs.h" 36#include "common/common_funcs.h"
37#include "common/swap.h" 37#include "common/swap.h"
38 38
39// Inlining
40#ifdef _WIN32
41#define FORCE_INLINE __forceinline
42#else
43#define FORCE_INLINE inline __attribute__((always_inline))
44#endif
45
46/* 39/*
47 * Abstract bitfield class 40 * Abstract bitfield class
48 * 41 *
@@ -142,8 +135,8 @@ public:
142 * containing several bitfields can be assembled by formatting each of their values and ORing 135 * containing several bitfields can be assembled by formatting each of their values and ORing
143 * the results together. 136 * the results together.
144 */ 137 */
145 static constexpr FORCE_INLINE StorageType FormatValue(const T& value) { 138 [[nodiscard]] static constexpr StorageType FormatValue(const T& value) {
146 return ((StorageType)value << position) & mask; 139 return (static_cast<StorageType>(value) << position) & mask;
147 } 140 }
148 141
149 /** 142 /**
@@ -151,7 +144,7 @@ public:
151 * (such as Value() or operator T), but this can be used to extract a value from a bitfield 144 * (such as Value() or operator T), but this can be used to extract a value from a bitfield
152 * union in a constexpr context. 145 * union in a constexpr context.
153 */ 146 */
154 static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) { 147 [[nodiscard]] static constexpr T ExtractValue(const StorageType& storage) {
155 if constexpr (std::numeric_limits<UnderlyingType>::is_signed) { 148 if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
156 std::size_t shift = 8 * sizeof(T) - bits; 149 std::size_t shift = 8 * sizeof(T) - bits;
157 return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >> 150 return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
@@ -175,7 +168,7 @@ public:
175 constexpr BitField(BitField&&) noexcept = default; 168 constexpr BitField(BitField&&) noexcept = default;
176 constexpr BitField& operator=(BitField&&) noexcept = default; 169 constexpr BitField& operator=(BitField&&) noexcept = default;
177 170
178 constexpr operator T() const { 171 [[nodiscard]] constexpr operator T() const {
179 return Value(); 172 return Value();
180 } 173 }
181 174
@@ -183,11 +176,11 @@ public:
183 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); 176 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
184 } 177 }
185 178
186 constexpr T Value() const { 179 [[nodiscard]] constexpr T Value() const {
187 return ExtractValue(storage); 180 return ExtractValue(storage);
188 } 181 }
189 182
190 constexpr explicit operator bool() const { 183 [[nodiscard]] constexpr explicit operator bool() const {
191 return Value() != 0; 184 return Value() != 0;
192 } 185 }
193 186
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index 6f7d5a947..29f59a9a3 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -17,12 +17,12 @@ namespace Common {
17 17
18/// Gets the size of a specified type T in bits. 18/// Gets the size of a specified type T in bits.
19template <typename T> 19template <typename T>
20constexpr std::size_t BitSize() { 20[[nodiscard]] constexpr std::size_t BitSize() {
21 return sizeof(T) * CHAR_BIT; 21 return sizeof(T) * CHAR_BIT;
22} 22}
23 23
24#ifdef _MSC_VER 24#ifdef _MSC_VER
25inline u32 CountLeadingZeroes32(u32 value) { 25[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
26 unsigned long leading_zero = 0; 26 unsigned long leading_zero = 0;
27 27
28 if (_BitScanReverse(&leading_zero, value) != 0) { 28 if (_BitScanReverse(&leading_zero, value) != 0) {
@@ -32,7 +32,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
32 return 32; 32 return 32;
33} 33}
34 34
35inline u32 CountLeadingZeroes64(u64 value) { 35[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
36 unsigned long leading_zero = 0; 36 unsigned long leading_zero = 0;
37 37
38 if (_BitScanReverse64(&leading_zero, value) != 0) { 38 if (_BitScanReverse64(&leading_zero, value) != 0) {
@@ -42,7 +42,7 @@ inline u32 CountLeadingZeroes64(u64 value) {
42 return 64; 42 return 64;
43} 43}
44#else 44#else
45inline u32 CountLeadingZeroes32(u32 value) { 45[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
46 if (value == 0) { 46 if (value == 0) {
47 return 32; 47 return 32;
48 } 48 }
@@ -50,7 +50,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
50 return static_cast<u32>(__builtin_clz(value)); 50 return static_cast<u32>(__builtin_clz(value));
51} 51}
52 52
53inline u32 CountLeadingZeroes64(u64 value) { 53[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
54 if (value == 0) { 54 if (value == 0) {
55 return 64; 55 return 64;
56 } 56 }
@@ -60,7 +60,7 @@ inline u32 CountLeadingZeroes64(u64 value) {
60#endif 60#endif
61 61
62#ifdef _MSC_VER 62#ifdef _MSC_VER
63inline u32 CountTrailingZeroes32(u32 value) { 63[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
64 unsigned long trailing_zero = 0; 64 unsigned long trailing_zero = 0;
65 65
66 if (_BitScanForward(&trailing_zero, value) != 0) { 66 if (_BitScanForward(&trailing_zero, value) != 0) {
@@ -70,7 +70,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
70 return 32; 70 return 32;
71} 71}
72 72
73inline u32 CountTrailingZeroes64(u64 value) { 73[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
74 unsigned long trailing_zero = 0; 74 unsigned long trailing_zero = 0;
75 75
76 if (_BitScanForward64(&trailing_zero, value) != 0) { 76 if (_BitScanForward64(&trailing_zero, value) != 0) {
@@ -80,7 +80,7 @@ inline u32 CountTrailingZeroes64(u64 value) {
80 return 64; 80 return 64;
81} 81}
82#else 82#else
83inline u32 CountTrailingZeroes32(u32 value) { 83[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
84 if (value == 0) { 84 if (value == 0) {
85 return 32; 85 return 32;
86 } 86 }
@@ -88,7 +88,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
88 return static_cast<u32>(__builtin_ctz(value)); 88 return static_cast<u32>(__builtin_ctz(value));
89} 89}
90 90
91inline u32 CountTrailingZeroes64(u64 value) { 91[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
92 if (value == 0) { 92 if (value == 0) {
93 return 64; 93 return 64;
94 } 94 }
@@ -99,13 +99,13 @@ inline u32 CountTrailingZeroes64(u64 value) {
99 99
100#ifdef _MSC_VER 100#ifdef _MSC_VER
101 101
102inline u32 MostSignificantBit32(const u32 value) { 102[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
103 unsigned long result; 103 unsigned long result;
104 _BitScanReverse(&result, value); 104 _BitScanReverse(&result, value);
105 return static_cast<u32>(result); 105 return static_cast<u32>(result);
106} 106}
107 107
108inline u32 MostSignificantBit64(const u64 value) { 108[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
109 unsigned long result; 109 unsigned long result;
110 _BitScanReverse64(&result, value); 110 _BitScanReverse64(&result, value);
111 return static_cast<u32>(result); 111 return static_cast<u32>(result);
@@ -113,30 +113,30 @@ inline u32 MostSignificantBit64(const u64 value) {
113 113
114#else 114#else
115 115
116inline u32 MostSignificantBit32(const u32 value) { 116[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
117 return 31U - static_cast<u32>(__builtin_clz(value)); 117 return 31U - static_cast<u32>(__builtin_clz(value));
118} 118}
119 119
120inline u32 MostSignificantBit64(const u64 value) { 120[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
121 return 63U - static_cast<u32>(__builtin_clzll(value)); 121 return 63U - static_cast<u32>(__builtin_clzll(value));
122} 122}
123 123
124#endif 124#endif
125 125
126inline u32 Log2Floor32(const u32 value) { 126[[nodiscard]] inline u32 Log2Floor32(const u32 value) {
127 return MostSignificantBit32(value); 127 return MostSignificantBit32(value);
128} 128}
129 129
130inline u32 Log2Ceil32(const u32 value) { 130[[nodiscard]] inline u32 Log2Ceil32(const u32 value) {
131 const u32 log2_f = Log2Floor32(value); 131 const u32 log2_f = Log2Floor32(value);
132 return log2_f + ((value ^ (1U << log2_f)) != 0U); 132 return log2_f + ((value ^ (1U << log2_f)) != 0U);
133} 133}
134 134
135inline u32 Log2Floor64(const u64 value) { 135[[nodiscard]] inline u32 Log2Floor64(const u64 value) {
136 return MostSignificantBit64(value); 136 return MostSignificantBit64(value);
137} 137}
138 138
139inline u32 Log2Ceil64(const u64 value) { 139[[nodiscard]] inline u32 Log2Ceil64(const u64 value) {
140 const u64 log2_f = static_cast<u64>(Log2Floor64(value)); 140 const u64 log2_f = static_cast<u64>(Log2Floor64(value));
141 return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL)); 141 return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL));
142} 142}
diff --git a/src/common/cityhash.h b/src/common/cityhash.h
index 4b94f8e18..a00804e01 100644
--- a/src/common/cityhash.h
+++ b/src/common/cityhash.h
@@ -61,42 +61,43 @@
61 61
62#pragma once 62#pragma once
63 63
64#include <cstddef>
65#include <cstdint>
64#include <utility> 66#include <utility>
65#include <stdint.h>
66#include <stdlib.h> // for std::size_t.
67 67
68namespace Common { 68namespace Common {
69 69
70typedef std::pair<uint64_t, uint64_t> uint128; 70using uint128 = std::pair<uint64_t, uint64_t>;
71 71
72inline uint64_t Uint128Low64(const uint128& x) { 72[[nodiscard]] inline uint64_t Uint128Low64(const uint128& x) {
73 return x.first; 73 return x.first;
74} 74}
75inline uint64_t Uint128High64(const uint128& x) { 75[[nodiscard]] inline uint64_t Uint128High64(const uint128& x) {
76 return x.second; 76 return x.second;
77} 77}
78 78
79// Hash function for a byte array. 79// Hash function for a byte array.
80uint64_t CityHash64(const char* buf, std::size_t len); 80[[nodiscard]] uint64_t CityHash64(const char* buf, std::size_t len);
81 81
82// Hash function for a byte array. For convenience, a 64-bit seed is also 82// Hash function for a byte array. For convenience, a 64-bit seed is also
83// hashed into the result. 83// hashed into the result.
84uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed); 84[[nodiscard]] uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed);
85 85
86// Hash function for a byte array. For convenience, two seeds are also 86// Hash function for a byte array. For convenience, two seeds are also
87// hashed into the result. 87// hashed into the result.
88uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0, uint64_t seed1); 88[[nodiscard]] uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0,
89 uint64_t seed1);
89 90
90// Hash function for a byte array. 91// Hash function for a byte array.
91uint128 CityHash128(const char* s, std::size_t len); 92[[nodiscard]] uint128 CityHash128(const char* s, std::size_t len);
92 93
93// Hash function for a byte array. For convenience, a 128-bit seed is also 94// Hash function for a byte array. For convenience, a 128-bit seed is also
94// hashed into the result. 95// hashed into the result.
95uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed); 96[[nodiscard]] uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed);
96 97
97// Hash 128 input bits down to 64 bits of output. 98// Hash 128 input bits down to 64 bits of output.
98// This is intended to be a reasonably good hash function. 99// This is intended to be a reasonably good hash function.
99inline uint64_t Hash128to64(const uint128& x) { 100[[nodiscard]] inline uint64_t Hash128to64(const uint128& x) {
100 // Murmur-inspired hashing. 101 // Murmur-inspired hashing.
101 const uint64_t kMul = 0x9ddfea08eb382d69ULL; 102 const uint64_t kMul = 0x9ddfea08eb382d69ULL;
102 uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; 103 uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
diff --git a/src/common/color.h b/src/common/color.h
index 3a2222077..bbcac858e 100644
--- a/src/common/color.h
+++ b/src/common/color.h
@@ -10,45 +10,45 @@
10#include "common/swap.h" 10#include "common/swap.h"
11#include "common/vector_math.h" 11#include "common/vector_math.h"
12 12
13namespace Color { 13namespace Common::Color {
14 14
15/// Convert a 1-bit color component to 8 bit 15/// Convert a 1-bit color component to 8 bit
16constexpr u8 Convert1To8(u8 value) { 16[[nodiscard]] constexpr u8 Convert1To8(u8 value) {
17 return value * 255; 17 return value * 255;
18} 18}
19 19
20/// Convert a 4-bit color component to 8 bit 20/// Convert a 4-bit color component to 8 bit
21constexpr u8 Convert4To8(u8 value) { 21[[nodiscard]] constexpr u8 Convert4To8(u8 value) {
22 return (value << 4) | value; 22 return (value << 4) | value;
23} 23}
24 24
25/// Convert a 5-bit color component to 8 bit 25/// Convert a 5-bit color component to 8 bit
26constexpr u8 Convert5To8(u8 value) { 26[[nodiscard]] constexpr u8 Convert5To8(u8 value) {
27 return (value << 3) | (value >> 2); 27 return (value << 3) | (value >> 2);
28} 28}
29 29
30/// Convert a 6-bit color component to 8 bit 30/// Convert a 6-bit color component to 8 bit
31constexpr u8 Convert6To8(u8 value) { 31[[nodiscard]] constexpr u8 Convert6To8(u8 value) {
32 return (value << 2) | (value >> 4); 32 return (value << 2) | (value >> 4);
33} 33}
34 34
35/// Convert a 8-bit color component to 1 bit 35/// Convert a 8-bit color component to 1 bit
36constexpr u8 Convert8To1(u8 value) { 36[[nodiscard]] constexpr u8 Convert8To1(u8 value) {
37 return value >> 7; 37 return value >> 7;
38} 38}
39 39
40/// Convert a 8-bit color component to 4 bit 40/// Convert a 8-bit color component to 4 bit
41constexpr u8 Convert8To4(u8 value) { 41[[nodiscard]] constexpr u8 Convert8To4(u8 value) {
42 return value >> 4; 42 return value >> 4;
43} 43}
44 44
45/// Convert a 8-bit color component to 5 bit 45/// Convert a 8-bit color component to 5 bit
46constexpr u8 Convert8To5(u8 value) { 46[[nodiscard]] constexpr u8 Convert8To5(u8 value) {
47 return value >> 3; 47 return value >> 3;
48} 48}
49 49
50/// Convert a 8-bit color component to 6 bit 50/// Convert a 8-bit color component to 6 bit
51constexpr u8 Convert8To6(u8 value) { 51[[nodiscard]] constexpr u8 Convert8To6(u8 value) {
52 return value >> 2; 52 return value >> 2;
53} 53}
54 54
@@ -57,7 +57,7 @@ constexpr u8 Convert8To6(u8 value) {
57 * @param bytes Pointer to encoded source color 57 * @param bytes Pointer to encoded source color
58 * @return Result color decoded as Common::Vec4<u8> 58 * @return Result color decoded as Common::Vec4<u8>
59 */ 59 */
60inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) { 60[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
61 return {bytes[3], bytes[2], bytes[1], bytes[0]}; 61 return {bytes[3], bytes[2], bytes[1], bytes[0]};
62} 62}
63 63
@@ -66,7 +66,7 @@ inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
66 * @param bytes Pointer to encoded source color 66 * @param bytes Pointer to encoded source color
67 * @return Result color decoded as Common::Vec4<u8> 67 * @return Result color decoded as Common::Vec4<u8>
68 */ 68 */
69inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) { 69[[nodiscard]] inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
70 return {bytes[2], bytes[1], bytes[0], 255}; 70 return {bytes[2], bytes[1], bytes[0], 255};
71} 71}
72 72
@@ -75,7 +75,7 @@ inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
75 * @param bytes Pointer to encoded source color 75 * @param bytes Pointer to encoded source color
76 * @return Result color decoded as Common::Vec4<u8> 76 * @return Result color decoded as Common::Vec4<u8>
77 */ 77 */
78inline Common::Vec4<u8> DecodeRG8(const u8* bytes) { 78[[nodiscard]] inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
79 return {bytes[1], bytes[0], 0, 255}; 79 return {bytes[1], bytes[0], 0, 255};
80} 80}
81 81
@@ -84,7 +84,7 @@ inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
84 * @param bytes Pointer to encoded source color 84 * @param bytes Pointer to encoded source color
85 * @return Result color decoded as Common::Vec4<u8> 85 * @return Result color decoded as Common::Vec4<u8>
86 */ 86 */
87inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) { 87[[nodiscard]] inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
88 u16_le pixel; 88 u16_le pixel;
89 std::memcpy(&pixel, bytes, sizeof(pixel)); 89 std::memcpy(&pixel, bytes, sizeof(pixel));
90 return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F), 90 return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
@@ -96,7 +96,7 @@ inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
96 * @param bytes Pointer to encoded source color 96 * @param bytes Pointer to encoded source color
97 * @return Result color decoded as Common::Vec4<u8> 97 * @return Result color decoded as Common::Vec4<u8>
98 */ 98 */
99inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) { 99[[nodiscard]] inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
100 u16_le pixel; 100 u16_le pixel;
101 std::memcpy(&pixel, bytes, sizeof(pixel)); 101 std::memcpy(&pixel, bytes, sizeof(pixel));
102 return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F), 102 return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
@@ -108,7 +108,7 @@ inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
108 * @param bytes Pointer to encoded source color 108 * @param bytes Pointer to encoded source color
109 * @return Result color decoded as Common::Vec4<u8> 109 * @return Result color decoded as Common::Vec4<u8>
110 */ 110 */
111inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) { 111[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
112 u16_le pixel; 112 u16_le pixel;
113 std::memcpy(&pixel, bytes, sizeof(pixel)); 113 std::memcpy(&pixel, bytes, sizeof(pixel));
114 return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF), 114 return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
@@ -120,7 +120,7 @@ inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
120 * @param bytes Pointer to encoded source value 120 * @param bytes Pointer to encoded source value
121 * @return Depth value as an u32 121 * @return Depth value as an u32
122 */ 122 */
123inline u32 DecodeD16(const u8* bytes) { 123[[nodiscard]] inline u32 DecodeD16(const u8* bytes) {
124 u16_le data; 124 u16_le data;
125 std::memcpy(&data, bytes, sizeof(data)); 125 std::memcpy(&data, bytes, sizeof(data));
126 return data; 126 return data;
@@ -131,7 +131,7 @@ inline u32 DecodeD16(const u8* bytes) {
131 * @param bytes Pointer to encoded source value 131 * @param bytes Pointer to encoded source value
132 * @return Depth value as an u32 132 * @return Depth value as an u32
133 */ 133 */
134inline u32 DecodeD24(const u8* bytes) { 134[[nodiscard]] inline u32 DecodeD24(const u8* bytes) {
135 return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]; 135 return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
136} 136}
137 137
@@ -140,7 +140,7 @@ inline u32 DecodeD24(const u8* bytes) {
140 * @param bytes Pointer to encoded source values 140 * @param bytes Pointer to encoded source values
141 * @return Resulting values stored as a Common::Vec2 141 * @return Resulting values stored as a Common::Vec2
142 */ 142 */
143inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) { 143[[nodiscard]] inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) {
144 return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]}; 144 return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
145} 145}
146 146
@@ -268,4 +268,4 @@ inline void EncodeX24S8(u8 stencil, u8* bytes) {
268 bytes[3] = stencil; 268 bytes[3] = stencil;
269} 269}
270 270
271} // namespace Color 271} // namespace Common::Color
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 88cf5250a..98421bced 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -53,14 +53,14 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
53// Call directly after the command or use the error num. 53// Call directly after the command or use the error num.
54// This function might change the error code. 54// This function might change the error code.
55// Defined in Misc.cpp. 55// Defined in Misc.cpp.
56std::string GetLastErrorMsg(); 56[[nodiscard]] std::string GetLastErrorMsg();
57 57
58#define DECLARE_ENUM_FLAG_OPERATORS(type) \ 58#define DECLARE_ENUM_FLAG_OPERATORS(type) \
59 constexpr type operator|(type a, type b) noexcept { \ 59 [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
60 using T = std::underlying_type_t<type>; \ 60 using T = std::underlying_type_t<type>; \
61 return static_cast<type>(static_cast<T>(a) | static_cast<T>(b)); \ 61 return static_cast<type>(static_cast<T>(a) | static_cast<T>(b)); \
62 } \ 62 } \
63 constexpr type operator&(type a, type b) noexcept { \ 63 [[nodiscard]] constexpr type operator&(type a, type b) noexcept { \
64 using T = std::underlying_type_t<type>; \ 64 using T = std::underlying_type_t<type>; \
65 return static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \ 65 return static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \
66 } \ 66 } \
@@ -74,22 +74,22 @@ std::string GetLastErrorMsg();
74 a = static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \ 74 a = static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \
75 return a; \ 75 return a; \
76 } \ 76 } \
77 constexpr type operator~(type key) noexcept { \ 77 [[nodiscard]] constexpr type operator~(type key) noexcept { \
78 using T = std::underlying_type_t<type>; \ 78 using T = std::underlying_type_t<type>; \
79 return static_cast<type>(~static_cast<T>(key)); \ 79 return static_cast<type>(~static_cast<T>(key)); \
80 } \ 80 } \
81 constexpr bool True(type key) noexcept { \ 81 [[nodiscard]] constexpr bool True(type key) noexcept { \
82 using T = std::underlying_type_t<type>; \ 82 using T = std::underlying_type_t<type>; \
83 return static_cast<T>(key) != 0; \ 83 return static_cast<T>(key) != 0; \
84 } \ 84 } \
85 constexpr bool False(type key) noexcept { \ 85 [[nodiscard]] constexpr bool False(type key) noexcept { \
86 using T = std::underlying_type_t<type>; \ 86 using T = std::underlying_type_t<type>; \
87 return static_cast<T>(key) == 0; \ 87 return static_cast<T>(key) == 0; \
88 } 88 }
89 89
90namespace Common { 90namespace Common {
91 91
92constexpr u32 MakeMagic(char a, char b, char c, char d) { 92[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) {
93 return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24; 93 return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24;
94} 94}
95 95
diff --git a/src/common/concepts.h b/src/common/concepts.h
index 54252e778..5bef3ad67 100644
--- a/src/common/concepts.h
+++ b/src/common/concepts.h
@@ -4,10 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7namespace Common {
8
9#include <type_traits> 7#include <type_traits>
10 8
9namespace Common {
10
11// Check if type is like an STL container 11// Check if type is like an STL container
12template <typename T> 12template <typename T>
13concept IsSTLContainer = requires(T t) { 13concept IsSTLContainer = requires(T t) {
diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp
index 7ab54e9e4..7f0a10521 100644
--- a/src/common/dynamic_library.cpp
+++ b/src/common/dynamic_library.cpp
@@ -21,7 +21,7 @@ namespace Common {
21DynamicLibrary::DynamicLibrary() = default; 21DynamicLibrary::DynamicLibrary() = default;
22 22
23DynamicLibrary::DynamicLibrary(const char* filename) { 23DynamicLibrary::DynamicLibrary(const char* filename) {
24 Open(filename); 24 void(Open(filename));
25} 25}
26 26
27DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept 27DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept
diff --git a/src/common/dynamic_library.h b/src/common/dynamic_library.h
index 2a06372fd..3512da940 100644
--- a/src/common/dynamic_library.h
+++ b/src/common/dynamic_library.h
@@ -33,7 +33,7 @@ public:
33 ~DynamicLibrary(); 33 ~DynamicLibrary();
34 34
35 /// Returns the specified library name with the platform-specific suffix added. 35 /// Returns the specified library name with the platform-specific suffix added.
36 static std::string GetUnprefixedFilename(const char* filename); 36 [[nodiscard]] static std::string GetUnprefixedFilename(const char* filename);
37 37
38 /// Returns the specified library name in platform-specific format. 38 /// Returns the specified library name in platform-specific format.
39 /// Major/minor versions will not be included if set to -1. 39 /// Major/minor versions will not be included if set to -1.
@@ -41,28 +41,29 @@ public:
41 /// Windows: LIBNAME-MAJOR-MINOR.dll 41 /// Windows: LIBNAME-MAJOR-MINOR.dll
42 /// Linux: libLIBNAME.so.MAJOR.MINOR 42 /// Linux: libLIBNAME.so.MAJOR.MINOR
43 /// Mac: libLIBNAME.MAJOR.MINOR.dylib 43 /// Mac: libLIBNAME.MAJOR.MINOR.dylib
44 static std::string GetVersionedFilename(const char* libname, int major = -1, int minor = -1); 44 [[nodiscard]] static std::string GetVersionedFilename(const char* libname, int major = -1,
45 int minor = -1);
45 46
46 /// Returns true if a module is loaded, otherwise false. 47 /// Returns true if a module is loaded, otherwise false.
47 bool IsOpen() const { 48 [[nodiscard]] bool IsOpen() const {
48 return handle != nullptr; 49 return handle != nullptr;
49 } 50 }
50 51
51 /// Loads (or replaces) the handle with the specified library file name. 52 /// Loads (or replaces) the handle with the specified library file name.
52 /// Returns true if the library was loaded and can be used. 53 /// Returns true if the library was loaded and can be used.
53 bool Open(const char* filename); 54 [[nodiscard]] bool Open(const char* filename);
54 55
55 /// Unloads the library, any function pointers from this library are no longer valid. 56 /// Unloads the library, any function pointers from this library are no longer valid.
56 void Close(); 57 void Close();
57 58
58 /// Returns the address of the specified symbol (function or variable) as an untyped pointer. 59 /// Returns the address of the specified symbol (function or variable) as an untyped pointer.
59 /// If the specified symbol does not exist in this library, nullptr is returned. 60 /// If the specified symbol does not exist in this library, nullptr is returned.
60 void* GetSymbolAddress(const char* name) const; 61 [[nodiscard]] void* GetSymbolAddress(const char* name) const;
61 62
62 /// Obtains the address of the specified symbol, automatically casting to the correct type. 63 /// Obtains the address of the specified symbol, automatically casting to the correct type.
63 /// Returns true if the symbol was found and assigned, otherwise false. 64 /// Returns true if the symbol was found and assigned, otherwise false.
64 template <typename T> 65 template <typename T>
65 bool GetSymbol(const char* name, T* ptr) const { 66 [[nodiscard]] bool GetSymbol(const char* name, T* ptr) const {
66 *ptr = reinterpret_cast<T>(GetSymbolAddress(name)); 67 *ptr = reinterpret_cast<T>(GetSymbolAddress(name));
67 return *ptr != nullptr; 68 return *ptr != nullptr;
68 } 69 }
diff --git a/src/common/fiber.h b/src/common/fiber.h
index dafc1100e..89dde5e36 100644
--- a/src/common/fiber.h
+++ b/src/common/fiber.h
@@ -47,7 +47,7 @@ public:
47 /// Yields control from Fiber 'from' to Fiber 'to' 47 /// Yields control from Fiber 'from' to Fiber 'to'
48 /// Fiber 'from' must be the currently running fiber. 48 /// Fiber 'from' must be the currently running fiber.
49 static void YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to); 49 static void YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to);
50 static std::shared_ptr<Fiber> ThreadToFiber(); 50 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
51 51
52 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter); 52 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter);
53 53
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 4ede9f72c..16c3713e0 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -74,7 +74,7 @@
74// This namespace has various generic functions related to files and paths. 74// This namespace has various generic functions related to files and paths.
75// The code still needs a ton of cleanup. 75// The code still needs a ton of cleanup.
76// REMEMBER: strdup considered harmful! 76// REMEMBER: strdup considered harmful!
77namespace FileUtil { 77namespace Common::FS {
78 78
79// Remove any ending forward slashes from directory paths 79// Remove any ending forward slashes from directory paths
80// Modifies argument. 80// Modifies argument.
@@ -196,7 +196,7 @@ bool CreateFullPath(const std::string& fullPath) {
196 int panicCounter = 100; 196 int panicCounter = 100;
197 LOG_TRACE(Common_Filesystem, "path {}", fullPath); 197 LOG_TRACE(Common_Filesystem, "path {}", fullPath);
198 198
199 if (FileUtil::Exists(fullPath)) { 199 if (Exists(fullPath)) {
200 LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath); 200 LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath);
201 return true; 201 return true;
202 } 202 }
@@ -212,7 +212,7 @@ bool CreateFullPath(const std::string& fullPath) {
212 212
213 // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") 213 // Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
214 std::string const subPath(fullPath.substr(0, position + 1)); 214 std::string const subPath(fullPath.substr(0, position + 1));
215 if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) { 215 if (!IsDirectory(subPath) && !CreateDir(subPath)) {
216 LOG_ERROR(Common, "CreateFullPath: directory creation failed"); 216 LOG_ERROR(Common, "CreateFullPath: directory creation failed");
217 return false; 217 return false;
218 } 218 }
@@ -231,7 +231,7 @@ bool DeleteDir(const std::string& filename) {
231 LOG_TRACE(Common_Filesystem, "directory {}", filename); 231 LOG_TRACE(Common_Filesystem, "directory {}", filename);
232 232
233 // check if a directory 233 // check if a directory
234 if (!FileUtil::IsDirectory(filename)) { 234 if (!IsDirectory(filename)) {
235 LOG_ERROR(Common_Filesystem, "Not a directory {}", filename); 235 LOG_ERROR(Common_Filesystem, "Not a directory {}", filename);
236 return false; 236 return false;
237 } 237 }
@@ -371,7 +371,7 @@ u64 GetSize(FILE* f) {
371bool CreateEmptyFile(const std::string& filename) { 371bool CreateEmptyFile(const std::string& filename) {
372 LOG_TRACE(Common_Filesystem, "{}", filename); 372 LOG_TRACE(Common_Filesystem, "{}", filename);
373 373
374 if (!FileUtil::IOFile(filename, "wb").IsOpen()) { 374 if (!IOFile(filename, "wb").IsOpen()) {
375 LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); 375 LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg());
376 return false; 376 return false;
377 } 377 }
@@ -488,29 +488,34 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
488 return false; 488 return false;
489 489
490 // Delete the outermost directory 490 // Delete the outermost directory
491 FileUtil::DeleteDir(directory); 491 DeleteDir(directory);
492 return true; 492 return true;
493} 493}
494 494
495void CopyDir(const std::string& source_path, const std::string& dest_path) { 495void CopyDir(const std::string& source_path, const std::string& dest_path) {
496#ifndef _WIN32 496#ifndef _WIN32
497 if (source_path == dest_path) 497 if (source_path == dest_path) {
498 return; 498 return;
499 if (!FileUtil::Exists(source_path)) 499 }
500 if (!Exists(source_path)) {
500 return; 501 return;
501 if (!FileUtil::Exists(dest_path)) 502 }
502 FileUtil::CreateFullPath(dest_path); 503 if (!Exists(dest_path)) {
504 CreateFullPath(dest_path);
505 }
503 506
504 DIR* dirp = opendir(source_path.c_str()); 507 DIR* dirp = opendir(source_path.c_str());
505 if (!dirp) 508 if (!dirp) {
506 return; 509 return;
510 }
507 511
508 while (struct dirent* result = readdir(dirp)) { 512 while (struct dirent* result = readdir(dirp)) {
509 const std::string virtualName(result->d_name); 513 const std::string virtualName(result->d_name);
510 // check for "." and ".." 514 // check for "." and ".."
511 if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || 515 if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
512 ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0'))) 516 ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0'))) {
513 continue; 517 continue;
518 }
514 519
515 std::string source, dest; 520 std::string source, dest;
516 source = source_path + virtualName; 521 source = source_path + virtualName;
@@ -518,11 +523,13 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) {
518 if (IsDirectory(source)) { 523 if (IsDirectory(source)) {
519 source += '/'; 524 source += '/';
520 dest += '/'; 525 dest += '/';
521 if (!FileUtil::Exists(dest)) 526 if (!Exists(dest)) {
522 FileUtil::CreateFullPath(dest); 527 CreateFullPath(dest);
528 }
523 CopyDir(source, dest); 529 CopyDir(source, dest);
524 } else if (!FileUtil::Exists(dest)) 530 } else if (!Exists(dest)) {
525 FileUtil::Copy(source, dest); 531 Copy(source, dest);
532 }
526 } 533 }
527 closedir(dirp); 534 closedir(dirp);
528#endif 535#endif
@@ -538,7 +545,7 @@ std::optional<std::string> GetCurrentDir() {
538 if (!dir) { 545 if (!dir) {
539#endif 546#endif
540 LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); 547 LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg());
541 return {}; 548 return std::nullopt;
542 } 549 }
543#ifdef _WIN32 550#ifdef _WIN32
544 std::string strDir = Common::UTF16ToUTF8(dir); 551 std::string strDir = Common::UTF16ToUTF8(dir);
@@ -546,7 +553,7 @@ std::optional<std::string> GetCurrentDir() {
546 std::string strDir = dir; 553 std::string strDir = dir;
547#endif 554#endif
548 free(dir); 555 free(dir);
549 return strDir; 556 return std::move(strDir);
550} 557}
551 558
552bool SetCurrentDir(const std::string& directory) { 559bool SetCurrentDir(const std::string& directory) {
@@ -668,7 +675,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
668 if (user_path.empty()) { 675 if (user_path.empty()) {
669#ifdef _WIN32 676#ifdef _WIN32
670 user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; 677 user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
671 if (!FileUtil::IsDirectory(user_path)) { 678 if (!IsDirectory(user_path)) {
672 user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP; 679 user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
673 } else { 680 } else {
674 LOG_INFO(Common_Filesystem, "Using the local user directory"); 681 LOG_INFO(Common_Filesystem, "Using the local user directory");
@@ -677,7 +684,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
677 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP); 684 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
678 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP); 685 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
679#else 686#else
680 if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) { 687 if (Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
681 user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; 688 user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
682 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP); 689 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
683 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP); 690 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
@@ -704,7 +711,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
704 } 711 }
705 712
706 if (!new_path.empty()) { 713 if (!new_path.empty()) {
707 if (!FileUtil::IsDirectory(new_path)) { 714 if (!IsDirectory(new_path)) {
708 LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path); 715 LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path);
709 return paths[path]; 716 return paths[path];
710 } else { 717 } else {
@@ -902,10 +909,10 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
902 return std::string(RemoveTrailingSlash(path)); 909 return std::string(RemoveTrailingSlash(path));
903} 910}
904 911
905IOFile::IOFile() {} 912IOFile::IOFile() = default;
906 913
907IOFile::IOFile(const std::string& filename, const char openmode[], int flags) { 914IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
908 Open(filename, openmode, flags); 915 void(Open(filename, openmode, flags));
909} 916}
910 917
911IOFile::~IOFile() { 918IOFile::~IOFile() {
@@ -946,17 +953,18 @@ bool IOFile::Open(const std::string& filename, const char openmode[], int flags)
946} 953}
947 954
948bool IOFile::Close() { 955bool IOFile::Close() {
949 if (!IsOpen() || 0 != std::fclose(m_file)) 956 if (!IsOpen() || 0 != std::fclose(m_file)) {
950 return false; 957 return false;
958 }
951 959
952 m_file = nullptr; 960 m_file = nullptr;
953 return true; 961 return true;
954} 962}
955 963
956u64 IOFile::GetSize() const { 964u64 IOFile::GetSize() const {
957 if (IsOpen()) 965 if (IsOpen()) {
958 return FileUtil::GetSize(m_file); 966 return FS::GetSize(m_file);
959 967 }
960 return 0; 968 return 0;
961} 969}
962 970
@@ -965,9 +973,9 @@ bool IOFile::Seek(s64 off, int origin) const {
965} 973}
966 974
967u64 IOFile::Tell() const { 975u64 IOFile::Tell() const {
968 if (IsOpen()) 976 if (IsOpen()) {
969 return ftello(m_file); 977 return ftello(m_file);
970 978 }
971 return std::numeric_limits<u64>::max(); 979 return std::numeric_limits<u64>::max();
972} 980}
973 981
@@ -1016,4 +1024,4 @@ bool IOFile::Resize(u64 size) {
1016 ; 1024 ;
1017} 1025}
1018 1026
1019} // namespace FileUtil 1027} // namespace Common::FS
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 187b93161..8b587320f 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -19,7 +19,7 @@
19#include "common/string_util.h" 19#include "common/string_util.h"
20#endif 20#endif
21 21
22namespace FileUtil { 22namespace Common::FS {
23 23
24// User paths for GetUserPath 24// User paths for GetUserPath
25enum class UserPath { 25enum class UserPath {
@@ -48,19 +48,19 @@ struct FSTEntry {
48}; 48};
49 49
50// Returns true if file filename exists 50// Returns true if file filename exists
51bool Exists(const std::string& filename); 51[[nodiscard]] bool Exists(const std::string& filename);
52 52
53// Returns true if filename is a directory 53// Returns true if filename is a directory
54bool IsDirectory(const std::string& filename); 54[[nodiscard]] bool IsDirectory(const std::string& filename);
55 55
56// Returns the size of filename (64bit) 56// Returns the size of filename (64bit)
57u64 GetSize(const std::string& filename); 57[[nodiscard]] u64 GetSize(const std::string& filename);
58 58
59// Overloaded GetSize, accepts file descriptor 59// Overloaded GetSize, accepts file descriptor
60u64 GetSize(const int fd); 60[[nodiscard]] u64 GetSize(int fd);
61 61
62// Overloaded GetSize, accepts FILE* 62// Overloaded GetSize, accepts FILE*
63u64 GetSize(FILE* f); 63[[nodiscard]] u64 GetSize(FILE* f);
64 64
65// Returns true if successful, or path already exists. 65// Returns true if successful, or path already exists.
66bool CreateDir(const std::string& filename); 66bool CreateDir(const std::string& filename);
@@ -120,7 +120,7 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
120bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256); 120bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256);
121 121
122// Returns the current directory 122// Returns the current directory
123std::optional<std::string> GetCurrentDir(); 123[[nodiscard]] std::optional<std::string> GetCurrentDir();
124 124
125// Create directory and copy contents (does not overwrite existing files) 125// Create directory and copy contents (does not overwrite existing files)
126void CopyDir(const std::string& source_path, const std::string& dest_path); 126void CopyDir(const std::string& source_path, const std::string& dest_path);
@@ -132,20 +132,20 @@ bool SetCurrentDir(const std::string& directory);
132// directory. To be used in "multi-user" mode (that is, installed). 132// directory. To be used in "multi-user" mode (that is, installed).
133const std::string& GetUserPath(UserPath path, const std::string& new_path = ""); 133const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
134 134
135std::string GetHactoolConfigurationPath(); 135[[nodiscard]] std::string GetHactoolConfigurationPath();
136 136
137std::string GetNANDRegistrationDir(bool system = false); 137[[nodiscard]] std::string GetNANDRegistrationDir(bool system = false);
138 138
139// Returns the path to where the sys file are 139// Returns the path to where the sys file are
140std::string GetSysDirectory(); 140[[nodiscard]] std::string GetSysDirectory();
141 141
142#ifdef __APPLE__ 142#ifdef __APPLE__
143std::string GetBundleDirectory(); 143[[nodiscard]] std::string GetBundleDirectory();
144#endif 144#endif
145 145
146#ifdef _WIN32 146#ifdef _WIN32
147const std::string& GetExeDirectory(); 147[[nodiscard]] const std::string& GetExeDirectory();
148std::string AppDataRoamingDirectory(); 148[[nodiscard]] std::string AppDataRoamingDirectory();
149#endif 149#endif
150 150
151std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str); 151std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str);
@@ -164,38 +164,55 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
164 164
165// Splits the path on '/' or '\' and put the components into a vector 165// Splits the path on '/' or '\' and put the components into a vector
166// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" } 166// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
167std::vector<std::string> SplitPathComponents(std::string_view filename); 167[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename);
168 168
169// Gets all of the text up to the last '/' or '\' in the path. 169// Gets all of the text up to the last '/' or '\' in the path.
170std::string_view GetParentPath(std::string_view path); 170[[nodiscard]] std::string_view GetParentPath(std::string_view path);
171 171
172// Gets all of the text after the first '/' or '\' in the path. 172// Gets all of the text after the first '/' or '\' in the path.
173std::string_view GetPathWithoutTop(std::string_view path); 173[[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path);
174 174
175// Gets the filename of the path 175// Gets the filename of the path
176std::string_view GetFilename(std::string_view path); 176[[nodiscard]] std::string_view GetFilename(std::string_view path);
177 177
178// Gets the extension of the filename 178// Gets the extension of the filename
179std::string_view GetExtensionFromFilename(std::string_view name); 179[[nodiscard]] std::string_view GetExtensionFromFilename(std::string_view name);
180 180
181// Removes the final '/' or '\' if one exists 181// Removes the final '/' or '\' if one exists
182std::string_view RemoveTrailingSlash(std::string_view path); 182[[nodiscard]] std::string_view RemoveTrailingSlash(std::string_view path);
183 183
184// Creates a new vector containing indices [first, last) from the original. 184// Creates a new vector containing indices [first, last) from the original.
185template <typename T> 185template <typename T>
186std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, std::size_t last) { 186[[nodiscard]] std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first,
187 if (first >= last) 187 std::size_t last) {
188 if (first >= last) {
188 return {}; 189 return {};
190 }
189 last = std::min<std::size_t>(last, vector.size()); 191 last = std::min<std::size_t>(last, vector.size());
190 return std::vector<T>(vector.begin() + first, vector.begin() + first + last); 192 return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
191} 193}
192 194
193enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault }; 195enum class DirectorySeparator {
196 ForwardSlash,
197 BackwardSlash,
198 PlatformDefault,
199};
194 200
195// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\' 201// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
196// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows 202// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
197std::string SanitizePath(std::string_view path, 203[[nodiscard]] std::string SanitizePath(
198 DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash); 204 std::string_view path,
205 DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
206
207// To deal with Windows being dumb at Unicode
208template <typename T>
209void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
210#ifdef _MSC_VER
211 fstream.open(Common::UTF8ToUTF16W(filename), openmode);
212#else
213 fstream.open(filename, openmode);
214#endif
215}
199 216
200// simple wrapper for cstdlib file functions to 217// simple wrapper for cstdlib file functions to
201// hopefully will make error checking easier 218// hopefully will make error checking easier
@@ -215,7 +232,7 @@ public:
215 232
216 void Swap(IOFile& other) noexcept; 233 void Swap(IOFile& other) noexcept;
217 234
218 bool Open(const std::string& filename, const char openmode[], int flags = 0); 235 [[nodiscard]] bool Open(const std::string& filename, const char openmode[], int flags = 0);
219 bool Close(); 236 bool Close();
220 237
221 template <typename T> 238 template <typename T>
@@ -256,13 +273,13 @@ public:
256 return WriteArray(str.data(), str.length()); 273 return WriteArray(str.data(), str.length());
257 } 274 }
258 275
259 bool IsOpen() const { 276 [[nodiscard]] bool IsOpen() const {
260 return nullptr != m_file; 277 return nullptr != m_file;
261 } 278 }
262 279
263 bool Seek(s64 off, int origin) const; 280 bool Seek(s64 off, int origin) const;
264 u64 Tell() const; 281 [[nodiscard]] u64 Tell() const;
265 u64 GetSize() const; 282 [[nodiscard]] u64 GetSize() const;
266 bool Resize(u64 size); 283 bool Resize(u64 size);
267 bool Flush(); 284 bool Flush();
268 285
@@ -278,14 +295,4 @@ private:
278 std::FILE* m_file = nullptr; 295 std::FILE* m_file = nullptr;
279}; 296};
280 297
281} // namespace FileUtil 298} // namespace Common::FS
282
283// To deal with Windows being dumb at unicode:
284template <typename T>
285void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
286#ifdef _MSC_VER
287 fstream.open(Common::UTF8ToUTF16W(filename), openmode);
288#else
289 fstream.open(filename, openmode);
290#endif
291}
diff --git a/src/common/hash.h b/src/common/hash.h
index b2538f3ea..298930702 100644
--- a/src/common/hash.h
+++ b/src/common/hash.h
@@ -5,36 +5,11 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <cstring>
9#include <utility> 8#include <utility>
10#include <boost/functional/hash.hpp> 9#include <boost/functional/hash.hpp>
11#include "common/cityhash.h"
12#include "common/common_types.h"
13 10
14namespace Common { 11namespace Common {
15 12
16/**
17 * Computes a 64-bit hash over the specified block of data
18 * @param data Block of data to compute hash over
19 * @param len Length of data (in bytes) to compute hash over
20 * @returns 64-bit hash value that was computed over the data block
21 */
22static inline u64 ComputeHash64(const void* data, std::size_t len) {
23 return CityHash64(static_cast<const char*>(data), len);
24}
25
26/**
27 * Computes a 64-bit hash of a struct. In addition to being trivially copyable, it is also critical
28 * that either the struct includes no padding, or that any padding is initialized to a known value
29 * by memsetting the struct to 0 before filling it in.
30 */
31template <typename T>
32static inline u64 ComputeStructHash64(const T& data) {
33 static_assert(std::is_trivially_copyable_v<T>,
34 "Type passed to ComputeStructHash64 must be trivially copyable");
35 return ComputeHash64(&data, sizeof(data));
36}
37
38struct PairHash { 13struct PairHash {
39 template <class T1, class T2> 14 template <class T1, class T2>
40 std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept { 15 std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept {
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index a0a0e78a4..120f1a5e6 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -14,7 +14,7 @@
14 14
15namespace Common { 15namespace Common {
16 16
17constexpr u8 ToHexNibble(char c) { 17[[nodiscard]] constexpr u8 ToHexNibble(char c) {
18 if (c >= 65 && c <= 70) { 18 if (c >= 65 && c <= 70) {
19 return c - 55; 19 return c - 55;
20 } 20 }
@@ -26,10 +26,10 @@ constexpr u8 ToHexNibble(char c) {
26 return c - 48; 26 return c - 48;
27} 27}
28 28
29std::vector<u8> HexStringToVector(std::string_view str, bool little_endian); 29[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
30 30
31template <std::size_t Size, bool le = false> 31template <std::size_t Size, bool le = false>
32constexpr std::array<u8, Size> HexStringToArray(std::string_view str) { 32[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
33 std::array<u8, Size> out{}; 33 std::array<u8, Size> out{};
34 if constexpr (le) { 34 if constexpr (le) {
35 for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) { 35 for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
@@ -44,7 +44,7 @@ constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
44} 44}
45 45
46template <typename ContiguousContainer> 46template <typename ContiguousContainer>
47std::string HexToString(const ContiguousContainer& data, bool upper = true) { 47[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
48 static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>, 48 static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
49 "Underlying type within the contiguous container must be u8."); 49 "Underlying type within the contiguous container must be u8.");
50 50
@@ -60,11 +60,11 @@ std::string HexToString(const ContiguousContainer& data, bool upper = true) {
60 return out; 60 return out;
61} 61}
62 62
63constexpr std::array<u8, 16> AsArray(const char (&data)[17]) { 63[[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[17]) {
64 return HexStringToArray<16>(data); 64 return HexStringToArray<16>(data);
65} 65}
66 66
67constexpr std::array<u8, 32> AsArray(const char (&data)[65]) { 67[[nodiscard]] constexpr std::array<u8, 32> AsArray(const char (&data)[65]) {
68 return HexStringToArray<32>(data); 68 return HexStringToArray<32>(data);
69} 69}
70 70
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index e5d702568..da1c2f185 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -94,7 +94,7 @@ public:
94 void Write(const Entry& entry) override; 94 void Write(const Entry& entry) override;
95 95
96private: 96private:
97 FileUtil::IOFile file; 97 Common::FS::IOFile file;
98 std::size_t bytes_written; 98 std::size_t bytes_written;
99}; 99};
100 100
diff --git a/src/common/lz4_compression.cpp b/src/common/lz4_compression.cpp
index ade6759bb..25700015a 100644
--- a/src/common/lz4_compression.cpp
+++ b/src/common/lz4_compression.cpp
@@ -14,19 +14,19 @@ std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size) {
14 ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size"); 14 ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size");
15 15
16 const auto source_size_int = static_cast<int>(source_size); 16 const auto source_size_int = static_cast<int>(source_size);
17 const int max_compressed_size = LZ4_compressBound(source_size_int); 17 const auto max_compressed_size = static_cast<std::size_t>(LZ4_compressBound(source_size_int));
18 std::vector<u8> compressed(max_compressed_size); 18 std::vector<u8> compressed(max_compressed_size);
19 19
20 const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source), 20 const int compressed_size = LZ4_compress_default(
21 reinterpret_cast<char*>(compressed.data()), 21 reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()),
22 source_size_int, max_compressed_size); 22 source_size_int, static_cast<int>(max_compressed_size));
23 23
24 if (compressed_size <= 0) { 24 if (compressed_size <= 0) {
25 // Compression failed 25 // Compression failed
26 return {}; 26 return {};
27 } 27 }
28 28
29 compressed.resize(compressed_size); 29 compressed.resize(static_cast<std::size_t>(compressed_size));
30 30
31 return compressed; 31 return compressed;
32} 32}
@@ -38,19 +38,19 @@ std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
38 compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX); 38 compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX);
39 39
40 const auto source_size_int = static_cast<int>(source_size); 40 const auto source_size_int = static_cast<int>(source_size);
41 const int max_compressed_size = LZ4_compressBound(source_size_int); 41 const auto max_compressed_size = static_cast<std::size_t>(LZ4_compressBound(source_size_int));
42 std::vector<u8> compressed(max_compressed_size); 42 std::vector<u8> compressed(max_compressed_size);
43 43
44 const int compressed_size = LZ4_compress_HC( 44 const int compressed_size = LZ4_compress_HC(
45 reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()), 45 reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()),
46 source_size_int, max_compressed_size, compression_level); 46 source_size_int, static_cast<int>(max_compressed_size), compression_level);
47 47
48 if (compressed_size <= 0) { 48 if (compressed_size <= 0) {
49 // Compression failed 49 // Compression failed
50 return {}; 50 return {};
51 } 51 }
52 52
53 compressed.resize(compressed_size); 53 compressed.resize(static_cast<std::size_t>(compressed_size));
54 54
55 return compressed; 55 return compressed;
56} 56}
diff --git a/src/common/lz4_compression.h b/src/common/lz4_compression.h
index 4c16f6e03..87a4be1b0 100644
--- a/src/common/lz4_compression.h
+++ b/src/common/lz4_compression.h
@@ -13,12 +13,12 @@ namespace Common::Compression {
13/** 13/**
14 * Compresses a source memory region with LZ4 and returns the compressed data in a vector. 14 * Compresses a source memory region with LZ4 and returns the compressed data in a vector.
15 * 15 *
16 * @param source the uncompressed source memory region. 16 * @param source The uncompressed source memory region.
17 * @param source_size the size in bytes of the uncompressed source memory region. 17 * @param source_size The size of the uncompressed source memory region.
18 * 18 *
19 * @return the compressed data. 19 * @return the compressed data.
20 */ 20 */
21std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size); 21[[nodiscard]] std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
22 22
23/** 23/**
24 * Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression 24 * Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression
@@ -26,23 +26,24 @@ std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
26 * compression level has almost no impact on decompression speed. Data compressed with LZ4HC can 26 * compression level has almost no impact on decompression speed. Data compressed with LZ4HC can
27 * also be decompressed with the default LZ4 decompression. 27 * also be decompressed with the default LZ4 decompression.
28 * 28 *
29 * @param source the uncompressed source memory region. 29 * @param source The uncompressed source memory region.
30 * @param source_size the size in bytes of the uncompressed source memory region. 30 * @param source_size The size of the uncompressed source memory region.
31 * @param compression_level the used compression level. Should be between 3 and 12. 31 * @param compression_level The used compression level. Should be between 3 and 12.
32 * 32 *
33 * @return the compressed data. 33 * @return the compressed data.
34 */ 34 */
35std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32 compression_level); 35[[nodiscard]] std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
36 s32 compression_level);
36 37
37/** 38/**
38 * Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level. 39 * Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level.
39 * 40 *
40 * @param source the uncompressed source memory region. 41 * @param source The uncompressed source memory region.
41 * @param source_size the size in bytes of the uncompressed source memory region. 42 * @param source_size The size of the uncompressed source memory region
42 * 43 *
43 * @return the compressed data. 44 * @return the compressed data.
44 */ 45 */
45std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size); 46[[nodiscard]] std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
46 47
47/** 48/**
48 * Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector. 49 * Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector.
@@ -52,6 +53,7 @@ std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
52 * 53 *
53 * @return the decompressed data. 54 * @return the decompressed data.
54 */ 55 */
55std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, std::size_t uncompressed_size); 56[[nodiscard]] std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed,
57 std::size_t uncompressed_size);
56 58
57} // namespace Common::Compression \ No newline at end of file 59} // namespace Common::Compression \ No newline at end of file
diff --git a/src/common/math_util.h b/src/common/math_util.h
index abca3177c..cc35c90ee 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -23,7 +23,7 @@ struct Rectangle {
23 constexpr Rectangle(T left, T top, T right, T bottom) 23 constexpr Rectangle(T left, T top, T right, T bottom)
24 : left(left), top(top), right(right), bottom(bottom) {} 24 : left(left), top(top), right(right), bottom(bottom) {}
25 25
26 T GetWidth() const { 26 [[nodiscard]] T GetWidth() const {
27 if constexpr (std::is_floating_point_v<T>) { 27 if constexpr (std::is_floating_point_v<T>) {
28 return std::abs(right - left); 28 return std::abs(right - left);
29 } else { 29 } else {
@@ -31,7 +31,7 @@ struct Rectangle {
31 } 31 }
32 } 32 }
33 33
34 T GetHeight() const { 34 [[nodiscard]] T GetHeight() const {
35 if constexpr (std::is_floating_point_v<T>) { 35 if constexpr (std::is_floating_point_v<T>) {
36 return std::abs(bottom - top); 36 return std::abs(bottom - top);
37 } else { 37 } else {
@@ -39,15 +39,15 @@ struct Rectangle {
39 } 39 }
40 } 40 }
41 41
42 Rectangle<T> TranslateX(const T x) const { 42 [[nodiscard]] Rectangle<T> TranslateX(const T x) const {
43 return Rectangle{left + x, top, right + x, bottom}; 43 return Rectangle{left + x, top, right + x, bottom};
44 } 44 }
45 45
46 Rectangle<T> TranslateY(const T y) const { 46 [[nodiscard]] Rectangle<T> TranslateY(const T y) const {
47 return Rectangle{left, top + y, right, bottom + y}; 47 return Rectangle{left, top + y, right, bottom + y};
48 } 48 }
49 49
50 Rectangle<T> Scale(const float s) const { 50 [[nodiscard]] Rectangle<T> Scale(const float s) const {
51 return Rectangle{left, top, static_cast<T>(left + GetWidth() * s), 51 return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
52 static_cast<T>(top + GetHeight() * s)}; 52 static_cast<T>(top + GetHeight() * s)};
53 } 53 }
diff --git a/src/common/memory_detect.h b/src/common/memory_detect.h
index a73c0f3f4..0f73751c8 100644
--- a/src/common/memory_detect.h
+++ b/src/common/memory_detect.h
@@ -17,6 +17,6 @@ struct MemoryInfo {
17 * Gets the memory info of the host system 17 * Gets the memory info of the host system
18 * @return Reference to a MemoryInfo struct with the physical and swap memory sizes in bytes 18 * @return Reference to a MemoryInfo struct with the physical and swap memory sizes in bytes
19 */ 19 */
20const MemoryInfo& GetMemInfo(); 20[[nodiscard]] const MemoryInfo& GetMemInfo();
21 21
22} // namespace Common \ No newline at end of file 22} // namespace Common \ No newline at end of file
diff --git a/src/common/multi_level_queue.h b/src/common/multi_level_queue.h
index 50acfdbf2..4b305bf40 100644
--- a/src/common/multi_level_queue.h
+++ b/src/common/multi_level_queue.h
@@ -223,15 +223,15 @@ public:
223 ListShiftForward(levels[priority], n); 223 ListShiftForward(levels[priority], n);
224 } 224 }
225 225
226 std::size_t depth() const { 226 [[nodiscard]] std::size_t depth() const {
227 return Depth; 227 return Depth;
228 } 228 }
229 229
230 std::size_t size(u32 priority) const { 230 [[nodiscard]] std::size_t size(u32 priority) const {
231 return levels[priority].size(); 231 return levels[priority].size();
232 } 232 }
233 233
234 std::size_t size() const { 234 [[nodiscard]] std::size_t size() const {
235 u64 priorities = used_priorities; 235 u64 priorities = used_priorities;
236 std::size_t size = 0; 236 std::size_t size = 0;
237 while (priorities != 0) { 237 while (priorities != 0) {
@@ -242,64 +242,64 @@ public:
242 return size; 242 return size;
243 } 243 }
244 244
245 bool empty() const { 245 [[nodiscard]] bool empty() const {
246 return used_priorities == 0; 246 return used_priorities == 0;
247 } 247 }
248 248
249 bool empty(u32 priority) const { 249 [[nodiscard]] bool empty(u32 priority) const {
250 return (used_priorities & (1ULL << priority)) == 0; 250 return (used_priorities & (1ULL << priority)) == 0;
251 } 251 }
252 252
253 u32 highest_priority_set(u32 max_priority = 0) const { 253 [[nodiscard]] u32 highest_priority_set(u32 max_priority = 0) const {
254 const u64 priorities = 254 const u64 priorities =
255 max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1)); 255 max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
256 return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities)); 256 return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
257 } 257 }
258 258
259 u32 lowest_priority_set(u32 min_priority = Depth - 1) const { 259 [[nodiscard]] u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
260 const u64 priorities = min_priority >= Depth - 1 260 const u64 priorities = min_priority >= Depth - 1
261 ? used_priorities 261 ? used_priorities
262 : (used_priorities & ((1ULL << (min_priority + 1)) - 1)); 262 : (used_priorities & ((1ULL << (min_priority + 1)) - 1));
263 return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities); 263 return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
264 } 264 }
265 265
266 const_iterator cbegin(u32 max_prio = 0) const { 266 [[nodiscard]] const_iterator cbegin(u32 max_prio = 0) const {
267 const u32 priority = highest_priority_set(max_prio); 267 const u32 priority = highest_priority_set(max_prio);
268 return priority == Depth ? cend() 268 return priority == Depth ? cend()
269 : const_iterator{*this, levels[priority].cbegin(), priority}; 269 : const_iterator{*this, levels[priority].cbegin(), priority};
270 } 270 }
271 const_iterator begin(u32 max_prio = 0) const { 271 [[nodiscard]] const_iterator begin(u32 max_prio = 0) const {
272 return cbegin(max_prio); 272 return cbegin(max_prio);
273 } 273 }
274 iterator begin(u32 max_prio = 0) { 274 [[nodiscard]] iterator begin(u32 max_prio = 0) {
275 const u32 priority = highest_priority_set(max_prio); 275 const u32 priority = highest_priority_set(max_prio);
276 return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority}; 276 return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
277 } 277 }
278 278
279 const_iterator cend(u32 min_prio = Depth - 1) const { 279 [[nodiscard]] const_iterator cend(u32 min_prio = Depth - 1) const {
280 return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1); 280 return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
281 } 281 }
282 const_iterator end(u32 min_prio = Depth - 1) const { 282 [[nodiscard]] const_iterator end(u32 min_prio = Depth - 1) const {
283 return cend(min_prio); 283 return cend(min_prio);
284 } 284 }
285 iterator end(u32 min_prio = Depth - 1) { 285 [[nodiscard]] iterator end(u32 min_prio = Depth - 1) {
286 return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1); 286 return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
287 } 287 }
288 288
289 T& front(u32 max_priority = 0) { 289 [[nodiscard]] T& front(u32 max_priority = 0) {
290 const u32 priority = highest_priority_set(max_priority); 290 const u32 priority = highest_priority_set(max_priority);
291 return levels[priority == Depth ? 0 : priority].front(); 291 return levels[priority == Depth ? 0 : priority].front();
292 } 292 }
293 const T& front(u32 max_priority = 0) const { 293 [[nodiscard]] const T& front(u32 max_priority = 0) const {
294 const u32 priority = highest_priority_set(max_priority); 294 const u32 priority = highest_priority_set(max_priority);
295 return levels[priority == Depth ? 0 : priority].front(); 295 return levels[priority == Depth ? 0 : priority].front();
296 } 296 }
297 297
298 T back(u32 min_priority = Depth - 1) { 298 [[nodiscard]] T& back(u32 min_priority = Depth - 1) {
299 const u32 priority = lowest_priority_set(min_priority); // intended 299 const u32 priority = lowest_priority_set(min_priority); // intended
300 return levels[priority == Depth ? 63 : priority].back(); 300 return levels[priority == Depth ? 63 : priority].back();
301 } 301 }
302 const T& back(u32 min_priority = Depth - 1) const { 302 [[nodiscard]] const T& back(u32 min_priority = Depth - 1) const {
303 const u32 priority = lowest_priority_set(min_priority); // intended 303 const u32 priority = lowest_priority_set(min_priority); // intended
304 return levels[priority == Depth ? 63 : priority].back(); 304 return levels[priority == Depth ? 63 : priority].back();
305 } 305 }
@@ -329,7 +329,8 @@ private:
329 in_list.splice(position, out_list, element); 329 in_list.splice(position, out_list, element);
330 } 330 }
331 331
332 static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) { 332 [[nodiscard]] static const_list_iterator ListIterateTo(const std::list<T>& list,
333 const T& element) {
333 auto it = list.cbegin(); 334 auto it = list.cbegin();
334 while (it != list.cend() && *it != element) { 335 while (it != list.cend() && *it != element) {
335 ++it; 336 ++it;
diff --git a/src/common/page_table.h b/src/common/page_table.h
index 1e8bd3187..cf5eed780 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -36,11 +36,11 @@ struct SpecialRegion {
36 36
37 MemoryHookPointer handler; 37 MemoryHookPointer handler;
38 38
39 bool operator<(const SpecialRegion& other) const { 39 [[nodiscard]] bool operator<(const SpecialRegion& other) const {
40 return std::tie(type, handler) < std::tie(other.type, other.handler); 40 return std::tie(type, handler) < std::tie(other.type, other.handler);
41 } 41 }
42 42
43 bool operator==(const SpecialRegion& other) const { 43 [[nodiscard]] bool operator==(const SpecialRegion& other) const {
44 return std::tie(type, handler) == std::tie(other.type, other.handler); 44 return std::tie(type, handler) == std::tie(other.type, other.handler);
45 } 45 }
46}; 46};
diff --git a/src/common/param_package.h b/src/common/param_package.h
index 6a0a9b656..c8a70bfa9 100644
--- a/src/common/param_package.h
+++ b/src/common/param_package.h
@@ -24,14 +24,14 @@ public:
24 ParamPackage& operator=(const ParamPackage& other) = default; 24 ParamPackage& operator=(const ParamPackage& other) = default;
25 ParamPackage& operator=(ParamPackage&& other) = default; 25 ParamPackage& operator=(ParamPackage&& other) = default;
26 26
27 std::string Serialize() const; 27 [[nodiscard]] std::string Serialize() const;
28 std::string Get(const std::string& key, const std::string& default_value) const; 28 [[nodiscard]] std::string Get(const std::string& key, const std::string& default_value) const;
29 int Get(const std::string& key, int default_value) const; 29 [[nodiscard]] int Get(const std::string& key, int default_value) const;
30 float Get(const std::string& key, float default_value) const; 30 [[nodiscard]] float Get(const std::string& key, float default_value) const;
31 void Set(const std::string& key, std::string value); 31 void Set(const std::string& key, std::string value);
32 void Set(const std::string& key, int value); 32 void Set(const std::string& key, int value);
33 void Set(const std::string& key, float value); 33 void Set(const std::string& key, float value);
34 bool Has(const std::string& key) const; 34 [[nodiscard]] bool Has(const std::string& key) const;
35 void Erase(const std::string& key); 35 void Erase(const std::string& key);
36 void Clear(); 36 void Clear();
37 37
diff --git a/src/common/quaternion.h b/src/common/quaternion.h
index 370198ae0..da44f35cd 100644
--- a/src/common/quaternion.h
+++ b/src/common/quaternion.h
@@ -14,35 +14,36 @@ public:
14 Vec3<T> xyz; 14 Vec3<T> xyz;
15 T w{}; 15 T w{};
16 16
17 Quaternion<decltype(-T{})> Inverse() const { 17 [[nodiscard]] Quaternion<decltype(-T{})> Inverse() const {
18 return {-xyz, w}; 18 return {-xyz, w};
19 } 19 }
20 20
21 Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const { 21 [[nodiscard]] Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const {
22 return {xyz + other.xyz, w + other.w}; 22 return {xyz + other.xyz, w + other.w};
23 } 23 }
24 24
25 Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const { 25 [[nodiscard]] Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const {
26 return {xyz - other.xyz, w - other.w}; 26 return {xyz - other.xyz, w - other.w};
27 } 27 }
28 28
29 Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(const Quaternion& other) const { 29 [[nodiscard]] Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(
30 const Quaternion& other) const {
30 return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz), 31 return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),
31 w * other.w - Dot(xyz, other.xyz)}; 32 w * other.w - Dot(xyz, other.xyz)};
32 } 33 }
33 34
34 Quaternion<T> Normalized() const { 35 [[nodiscard]] Quaternion<T> Normalized() const {
35 T length = std::sqrt(xyz.Length2() + w * w); 36 T length = std::sqrt(xyz.Length2() + w * w);
36 return {xyz / length, w / length}; 37 return {xyz / length, w / length};
37 } 38 }
38}; 39};
39 40
40template <typename T> 41template <typename T>
41auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) { 42[[nodiscard]] auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {
42 return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w); 43 return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w);
43} 44}
44 45
45inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) { 46[[nodiscard]] inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {
46 return {axis * std::sin(angle / 2), std::cos(angle / 2)}; 47 return {axis * std::sin(angle / 2), std::cos(angle / 2)};
47} 48}
48 49
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h
index abe3b4dc2..138fa0131 100644
--- a/src/common/ring_buffer.h
+++ b/src/common/ring_buffer.h
@@ -91,12 +91,12 @@ public:
91 } 91 }
92 92
93 /// @returns Number of slots used 93 /// @returns Number of slots used
94 std::size_t Size() const { 94 [[nodiscard]] std::size_t Size() const {
95 return m_write_index.load() - m_read_index.load(); 95 return m_write_index.load() - m_read_index.load();
96 } 96 }
97 97
98 /// @returns Maximum size of ring buffer 98 /// @returns Maximum size of ring buffer
99 constexpr std::size_t Capacity() const { 99 [[nodiscard]] constexpr std::size_t Capacity() const {
100 return capacity; 100 return capacity;
101 } 101 }
102 102
diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h
index 1df5528c4..4f946a258 100644
--- a/src/common/spin_lock.h
+++ b/src/common/spin_lock.h
@@ -17,7 +17,7 @@ class SpinLock {
17public: 17public:
18 void lock(); 18 void lock();
19 void unlock(); 19 void unlock();
20 bool try_lock(); 20 [[nodiscard]] bool try_lock();
21 21
22private: 22private:
23 std::atomic_flag lck = ATOMIC_FLAG_INIT; 23 std::atomic_flag lck = ATOMIC_FLAG_INIT;
diff --git a/src/common/string_util.h b/src/common/string_util.h
index 023dff5dc..a32c07c06 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -12,19 +12,19 @@
12namespace Common { 12namespace Common {
13 13
14/// Make a string lowercase 14/// Make a string lowercase
15std::string ToLower(std::string str); 15[[nodiscard]] std::string ToLower(std::string str);
16 16
17/// Make a string uppercase 17/// Make a string uppercase
18std::string ToUpper(std::string str); 18[[nodiscard]] std::string ToUpper(std::string str);
19 19
20std::string StringFromBuffer(const std::vector<u8>& data); 20[[nodiscard]] std::string StringFromBuffer(const std::vector<u8>& data);
21 21
22std::string StripSpaces(const std::string& s); 22[[nodiscard]] std::string StripSpaces(const std::string& s);
23std::string StripQuotes(const std::string& s); 23[[nodiscard]] std::string StripQuotes(const std::string& s);
24 24
25std::string StringFromBool(bool value); 25[[nodiscard]] std::string StringFromBool(bool value);
26 26
27std::string TabsToSpaces(int tab_size, std::string in); 27[[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in);
28 28
29void SplitString(const std::string& str, char delim, std::vector<std::string>& output); 29void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
30 30
@@ -34,14 +34,15 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
34 34
35void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, 35void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path,
36 const std::string& _Filename); 36 const std::string& _Filename);
37std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest); 37[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
38 const std::string& dest);
38 39
39std::string UTF16ToUTF8(const std::u16string& input); 40[[nodiscard]] std::string UTF16ToUTF8(const std::u16string& input);
40std::u16string UTF8ToUTF16(const std::string& input); 41[[nodiscard]] std::u16string UTF8ToUTF16(const std::string& input);
41 42
42#ifdef _WIN32 43#ifdef _WIN32
43std::string UTF16ToUTF8(const std::wstring& input); 44[[nodiscard]] std::string UTF16ToUTF8(const std::wstring& input);
44std::wstring UTF8ToUTF16W(const std::string& str); 45[[nodiscard]] std::wstring UTF8ToUTF16W(const std::string& str);
45 46
46#endif 47#endif
47 48
@@ -50,7 +51,7 @@ std::wstring UTF8ToUTF16W(const std::string& str);
50 * `other` for equality. 51 * `other` for equality.
51 */ 52 */
52template <typename InIt> 53template <typename InIt>
53bool ComparePartialString(InIt begin, InIt end, const char* other) { 54[[nodiscard]] bool ComparePartialString(InIt begin, InIt end, const char* other) {
54 for (; begin != end && *other != '\0'; ++begin, ++other) { 55 for (; begin != end && *other != '\0'; ++begin, ++other) {
55 if (*begin != *other) { 56 if (*begin != *other) {
56 return false; 57 return false;
@@ -64,14 +65,15 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
64 * Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't 65 * Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
65 * NUL-terminated then the string ends at max_len characters. 66 * NUL-terminated then the string ends at max_len characters.
66 */ 67 */
67std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len); 68[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(const char* buffer,
69 std::size_t max_len);
68 70
69/** 71/**
70 * Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't 72 * Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
71 * null-terminated, then the string ends at the greatest multiple of two less then or equal to 73 * null-terminated, then the string ends at the greatest multiple of two less then or equal to
72 * max_len_bytes. 74 * max_len_bytes.
73 */ 75 */
74std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, 76[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
75 std::size_t max_len); 77 std::size_t max_len);
76 78
77} // namespace Common 79} // namespace Common
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp
index 16d42facd..6241d08b3 100644
--- a/src/common/telemetry.cpp
+++ b/src/common/telemetry.cpp
@@ -12,7 +12,7 @@
12#include "common/x64/cpu_detect.h" 12#include "common/x64/cpu_detect.h"
13#endif 13#endif
14 14
15namespace Telemetry { 15namespace Common::Telemetry {
16 16
17void FieldCollection::Accept(VisitorInterface& visitor) const { 17void FieldCollection::Accept(VisitorInterface& visitor) const {
18 for (const auto& field : fields) { 18 for (const auto& field : fields) {
@@ -88,4 +88,4 @@ void AppendOSInfo(FieldCollection& fc) {
88#endif 88#endif
89} 89}
90 90
91} // namespace Telemetry 91} // namespace Common::Telemetry
diff --git a/src/common/telemetry.h b/src/common/telemetry.h
index 854a73fae..a50c5d1de 100644
--- a/src/common/telemetry.h
+++ b/src/common/telemetry.h
@@ -10,7 +10,7 @@
10#include <string> 10#include <string>
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace Telemetry { 13namespace Common::Telemetry {
14 14
15/// Field type, used for grouping fields together in the final submitted telemetry log 15/// Field type, used for grouping fields together in the final submitted telemetry log
16enum class FieldType : u8 { 16enum class FieldType : u8 {
@@ -63,30 +63,30 @@ public:
63 63
64 void Accept(VisitorInterface& visitor) const override; 64 void Accept(VisitorInterface& visitor) const override;
65 65
66 const std::string& GetName() const override { 66 [[nodiscard]] const std::string& GetName() const override {
67 return name; 67 return name;
68 } 68 }
69 69
70 /** 70 /**
71 * Returns the type of the field. 71 * Returns the type of the field.
72 */ 72 */
73 FieldType GetType() const { 73 [[nodiscard]] FieldType GetType() const {
74 return type; 74 return type;
75 } 75 }
76 76
77 /** 77 /**
78 * Returns the value of the field. 78 * Returns the value of the field.
79 */ 79 */
80 const T& GetValue() const { 80 [[nodiscard]] const T& GetValue() const {
81 return value; 81 return value;
82 } 82 }
83 83
84 bool operator==(const Field& other) const { 84 [[nodiscard]] bool operator==(const Field& other) const {
85 return (type == other.type) && (name == other.name) && (value == other.value); 85 return (type == other.type) && (name == other.name) && (value == other.value);
86 } 86 }
87 87
88 bool operator!=(const Field& other) const { 88 [[nodiscard]] bool operator!=(const Field& other) const {
89 return !(*this == other); 89 return !operator==(other);
90 } 90 }
91 91
92private: 92private:
@@ -196,4 +196,4 @@ void AppendCPUInfo(FieldCollection& fc);
196/// such as platform name, etc. 196/// such as platform name, etc.
197void AppendOSInfo(FieldCollection& fc); 197void AppendOSInfo(FieldCollection& fc);
198 198
199} // namespace Telemetry 199} // namespace Common::Telemetry
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index 791f99a8c..def9e5d8d 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -18,14 +18,14 @@ struct ThreadQueueList {
18 using Priority = unsigned int; 18 using Priority = unsigned int;
19 19
20 // Number of priority levels. (Valid levels are [0..NUM_QUEUES).) 20 // Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
21 static const Priority NUM_QUEUES = N; 21 static constexpr Priority NUM_QUEUES = N;
22 22
23 ThreadQueueList() { 23 ThreadQueueList() {
24 first = nullptr; 24 first = nullptr;
25 } 25 }
26 26
27 // Only for debugging, returns priority level. 27 // Only for debugging, returns priority level.
28 Priority contains(const T& uid) const { 28 [[nodiscard]] Priority contains(const T& uid) const {
29 for (Priority i = 0; i < NUM_QUEUES; ++i) { 29 for (Priority i = 0; i < NUM_QUEUES; ++i) {
30 const Queue& cur = queues[i]; 30 const Queue& cur = queues[i];
31 if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) { 31 if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
@@ -36,7 +36,7 @@ struct ThreadQueueList {
36 return -1; 36 return -1;
37 } 37 }
38 38
39 T get_first() const { 39 [[nodiscard]] T get_first() const {
40 const Queue* cur = first; 40 const Queue* cur = first;
41 while (cur != nullptr) { 41 while (cur != nullptr) {
42 if (!cur->data.empty()) { 42 if (!cur->data.empty()) {
@@ -49,7 +49,7 @@ struct ThreadQueueList {
49 } 49 }
50 50
51 template <typename UnaryPredicate> 51 template <typename UnaryPredicate>
52 T get_first_filter(UnaryPredicate filter) const { 52 [[nodiscard]] T get_first_filter(UnaryPredicate filter) const {
53 const Queue* cur = first; 53 const Queue* cur = first;
54 while (cur != nullptr) { 54 while (cur != nullptr) {
55 if (!cur->data.empty()) { 55 if (!cur->data.empty()) {
@@ -129,7 +129,7 @@ struct ThreadQueueList {
129 first = nullptr; 129 first = nullptr;
130 } 130 }
131 131
132 bool empty(Priority priority) const { 132 [[nodiscard]] bool empty(Priority priority) const {
133 const Queue* cur = &queues[priority]; 133 const Queue* cur = &queues[priority];
134 return cur->data.empty(); 134 return cur->data.empty();
135 } 135 }
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index 8268bbd5c..a4647314a 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -25,15 +25,15 @@ public:
25 delete read_ptr; 25 delete read_ptr;
26 } 26 }
27 27
28 std::size_t Size() const { 28 [[nodiscard]] std::size_t Size() const {
29 return size.load(); 29 return size.load();
30 } 30 }
31 31
32 bool Empty() const { 32 [[nodiscard]] bool Empty() const {
33 return Size() == 0; 33 return Size() == 0;
34 } 34 }
35 35
36 T& Front() const { 36 [[nodiscard]] T& Front() const {
37 return read_ptr->current; 37 return read_ptr->current;
38 } 38 }
39 39
@@ -130,15 +130,15 @@ private:
130template <typename T> 130template <typename T>
131class MPSCQueue { 131class MPSCQueue {
132public: 132public:
133 std::size_t Size() const { 133 [[nodiscard]] std::size_t Size() const {
134 return spsc_queue.Size(); 134 return spsc_queue.Size();
135 } 135 }
136 136
137 bool Empty() const { 137 [[nodiscard]] bool Empty() const {
138 return spsc_queue.Empty(); 138 return spsc_queue.Empty();
139 } 139 }
140 140
141 T& Front() const { 141 [[nodiscard]] T& Front() const {
142 return spsc_queue.Front(); 142 return spsc_queue.Front();
143 } 143 }
144 144
diff --git a/src/common/time_zone.h b/src/common/time_zone.h
index 945daa09c..9f5939ca5 100644
--- a/src/common/time_zone.h
+++ b/src/common/time_zone.h
@@ -10,9 +10,9 @@
10namespace Common::TimeZone { 10namespace Common::TimeZone {
11 11
12/// Gets the default timezone, i.e. "GMT" 12/// Gets the default timezone, i.e. "GMT"
13std::string GetDefaultTimeZone(); 13[[nodiscard]] std::string GetDefaultTimeZone();
14 14
15/// Gets the offset of the current timezone (from the default), in seconds 15/// Gets the offset of the current timezone (from the default), in seconds
16std::chrono::seconds GetCurrentOffsetSeconds(); 16[[nodiscard]] std::chrono::seconds GetCurrentOffsetSeconds();
17 17
18} // namespace Common::TimeZone 18} // namespace Common::TimeZone
diff --git a/src/common/timer.h b/src/common/timer.h
index 27b521baa..8894a143d 100644
--- a/src/common/timer.h
+++ b/src/common/timer.h
@@ -19,18 +19,18 @@ public:
19 19
20 // The time difference is always returned in milliseconds, regardless of alternative internal 20 // The time difference is always returned in milliseconds, regardless of alternative internal
21 // representation 21 // representation
22 std::chrono::milliseconds GetTimeDifference(); 22 [[nodiscard]] std::chrono::milliseconds GetTimeDifference();
23 void AddTimeDifference(); 23 void AddTimeDifference();
24 24
25 static std::chrono::seconds GetTimeSinceJan1970(); 25 [[nodiscard]] static std::chrono::seconds GetTimeSinceJan1970();
26 static std::chrono::seconds GetLocalTimeSinceJan1970(); 26 [[nodiscard]] static std::chrono::seconds GetLocalTimeSinceJan1970();
27 static double GetDoubleTime(); 27 [[nodiscard]] static double GetDoubleTime();
28 28
29 static std::string GetTimeFormatted(); 29 [[nodiscard]] static std::string GetTimeFormatted();
30 std::string GetTimeElapsedFormatted() const; 30 [[nodiscard]] std::string GetTimeElapsedFormatted() const;
31 std::chrono::milliseconds GetTimeElapsed(); 31 [[nodiscard]] std::chrono::milliseconds GetTimeElapsed();
32 32
33 static std::chrono::milliseconds GetTimeMs(); 33 [[nodiscard]] static std::chrono::milliseconds GetTimeMs();
34 34
35private: 35private:
36 std::chrono::milliseconds m_LastTime; 36 std::chrono::milliseconds m_LastTime;
diff --git a/src/common/uint128.h b/src/common/uint128.h
index 503cd2d0c..969259ab6 100644
--- a/src/common/uint128.h
+++ b/src/common/uint128.h
@@ -10,13 +10,13 @@
10namespace Common { 10namespace Common {
11 11
12// This function multiplies 2 u64 values and divides it by a u64 value. 12// This function multiplies 2 u64 values and divides it by a u64 value.
13u64 MultiplyAndDivide64(u64 a, u64 b, u64 d); 13[[nodiscard]] u64 MultiplyAndDivide64(u64 a, u64 b, u64 d);
14 14
15// This function multiplies 2 u64 values and produces a u128 value; 15// This function multiplies 2 u64 values and produces a u128 value;
16u128 Multiply64Into128(u64 a, u64 b); 16[[nodiscard]] u128 Multiply64Into128(u64 a, u64 b);
17 17
18// This function divides a u128 by a u32 value and produces two u64 values: 18// This function divides a u128 by a u32 value and produces two u64 values:
19// the result of division and the remainder 19// the result of division and the remainder
20std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor); 20[[nodiscard]] std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
21 21
22} // namespace Common 22} // namespace Common
diff --git a/src/common/uuid.h b/src/common/uuid.h
index 4d3af8cec..4ab9a25f0 100644
--- a/src/common/uuid.h
+++ b/src/common/uuid.h
@@ -19,21 +19,21 @@ struct UUID {
19 constexpr explicit UUID(const u128& id) : uuid{id} {} 19 constexpr explicit UUID(const u128& id) : uuid{id} {}
20 constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} 20 constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
21 21
22 constexpr explicit operator bool() const { 22 [[nodiscard]] constexpr explicit operator bool() const {
23 return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1]; 23 return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1];
24 } 24 }
25 25
26 constexpr bool operator==(const UUID& rhs) const { 26 [[nodiscard]] constexpr bool operator==(const UUID& rhs) const {
27 // TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20 27 // TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20
28 return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1]; 28 return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1];
29 } 29 }
30 30
31 constexpr bool operator!=(const UUID& rhs) const { 31 [[nodiscard]] constexpr bool operator!=(const UUID& rhs) const {
32 return !operator==(rhs); 32 return !operator==(rhs);
33 } 33 }
34 34
35 // TODO(ogniK): Properly generate uuids based on RFC-4122 35 // TODO(ogniK): Properly generate uuids based on RFC-4122
36 static UUID Generate(); 36 [[nodiscard]] static UUID Generate();
37 37
38 // Set the UUID to {0,0} to be considered an invalid user 38 // Set the UUID to {0,0} to be considered an invalid user
39 constexpr void Invalidate() { 39 constexpr void Invalidate() {
@@ -41,12 +41,12 @@ struct UUID {
41 } 41 }
42 42
43 // TODO(ogniK): Properly generate a Nintendo ID 43 // TODO(ogniK): Properly generate a Nintendo ID
44 constexpr u64 GetNintendoID() const { 44 [[nodiscard]] constexpr u64 GetNintendoID() const {
45 return uuid[0]; 45 return uuid[0];
46 } 46 }
47 47
48 std::string Format() const; 48 [[nodiscard]] std::string Format() const;
49 std::string FormatSwitch() const; 49 [[nodiscard]] std::string FormatSwitch() const;
50}; 50};
51static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 51static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
52 52
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index 429485329..2a0fcf541 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -52,15 +52,15 @@ public:
52 constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {} 52 constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
53 53
54 template <typename T2> 54 template <typename T2>
55 constexpr Vec2<T2> Cast() const { 55 [[nodiscard]] constexpr Vec2<T2> Cast() const {
56 return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y)); 56 return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
57 } 57 }
58 58
59 static constexpr Vec2 AssignToAll(const T& f) { 59 [[nodiscard]] static constexpr Vec2 AssignToAll(const T& f) {
60 return Vec2{f, f}; 60 return Vec2{f, f};
61 } 61 }
62 62
63 constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const { 63 [[nodiscard]] constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
64 return {x + other.x, y + other.y}; 64 return {x + other.x, y + other.y};
65 } 65 }
66 constexpr Vec2& operator+=(const Vec2& other) { 66 constexpr Vec2& operator+=(const Vec2& other) {
@@ -68,7 +68,7 @@ public:
68 y += other.y; 68 y += other.y;
69 return *this; 69 return *this;
70 } 70 }
71 constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const { 71 [[nodiscard]] constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
72 return {x - other.x, y - other.y}; 72 return {x - other.x, y - other.y};
73 } 73 }
74 constexpr Vec2& operator-=(const Vec2& other) { 74 constexpr Vec2& operator-=(const Vec2& other) {
@@ -78,15 +78,15 @@ public:
78 } 78 }
79 79
80 template <typename U = T> 80 template <typename U = T>
81 constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const { 81 [[nodiscard]] constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
82 return {-x, -y}; 82 return {-x, -y};
83 } 83 }
84 constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const { 84 [[nodiscard]] constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
85 return {x * other.x, y * other.y}; 85 return {x * other.x, y * other.y};
86 } 86 }
87 87
88 template <typename V> 88 template <typename V>
89 constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const { 89 [[nodiscard]] constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
90 return {x * f, y * f}; 90 return {x * f, y * f};
91 } 91 }
92 92
@@ -97,7 +97,7 @@ public:
97 } 97 }
98 98
99 template <typename V> 99 template <typename V>
100 constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const { 100 [[nodiscard]] constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
101 return {x / f, y / f}; 101 return {x / f, y / f};
102 } 102 }
103 103
@@ -107,18 +107,18 @@ public:
107 return *this; 107 return *this;
108 } 108 }
109 109
110 constexpr T Length2() const { 110 [[nodiscard]] constexpr T Length2() const {
111 return x * x + y * y; 111 return x * x + y * y;
112 } 112 }
113 113
114 // Only implemented for T=float 114 // Only implemented for T=float
115 float Length() const; 115 [[nodiscard]] float Length() const;
116 float Normalize(); // returns the previous length, which is often useful 116 [[nodiscard]] float Normalize(); // returns the previous length, which is often useful
117 117
118 constexpr T& operator[](std::size_t i) { 118 [[nodiscard]] constexpr T& operator[](std::size_t i) {
119 return *((&x) + i); 119 return *((&x) + i);
120 } 120 }
121 constexpr const T& operator[](std::size_t i) const { 121 [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
122 return *((&x) + i); 122 return *((&x) + i);
123 } 123 }
124 124
@@ -128,46 +128,46 @@ public:
128 } 128 }
129 129
130 // Common aliases: UV (texel coordinates), ST (texture coordinates) 130 // Common aliases: UV (texel coordinates), ST (texture coordinates)
131 constexpr T& u() { 131 [[nodiscard]] constexpr T& u() {
132 return x; 132 return x;
133 } 133 }
134 constexpr T& v() { 134 [[nodiscard]] constexpr T& v() {
135 return y; 135 return y;
136 } 136 }
137 constexpr T& s() { 137 [[nodiscard]] constexpr T& s() {
138 return x; 138 return x;
139 } 139 }
140 constexpr T& t() { 140 [[nodiscard]] constexpr T& t() {
141 return y; 141 return y;
142 } 142 }
143 143
144 constexpr const T& u() const { 144 [[nodiscard]] constexpr const T& u() const {
145 return x; 145 return x;
146 } 146 }
147 constexpr const T& v() const { 147 [[nodiscard]] constexpr const T& v() const {
148 return y; 148 return y;
149 } 149 }
150 constexpr const T& s() const { 150 [[nodiscard]] constexpr const T& s() const {
151 return x; 151 return x;
152 } 152 }
153 constexpr const T& t() const { 153 [[nodiscard]] constexpr const T& t() const {
154 return y; 154 return y;
155 } 155 }
156 156
157 // swizzlers - create a subvector of specific components 157 // swizzlers - create a subvector of specific components
158 constexpr Vec2 yx() const { 158 [[nodiscard]] constexpr Vec2 yx() const {
159 return Vec2(y, x); 159 return Vec2(y, x);
160 } 160 }
161 constexpr Vec2 vu() const { 161 [[nodiscard]] constexpr Vec2 vu() const {
162 return Vec2(y, x); 162 return Vec2(y, x);
163 } 163 }
164 constexpr Vec2 ts() const { 164 [[nodiscard]] constexpr Vec2 ts() const {
165 return Vec2(y, x); 165 return Vec2(y, x);
166 } 166 }
167}; 167};
168 168
169template <typename T, typename V> 169template <typename T, typename V>
170constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) { 170[[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
171 return Vec2<T>(f * vec.x, f * vec.y); 171 return Vec2<T>(f * vec.x, f * vec.y);
172} 172}
173 173
@@ -196,15 +196,15 @@ public:
196 constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {} 196 constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
197 197
198 template <typename T2> 198 template <typename T2>
199 constexpr Vec3<T2> Cast() const { 199 [[nodiscard]] constexpr Vec3<T2> Cast() const {
200 return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z)); 200 return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
201 } 201 }
202 202
203 static constexpr Vec3 AssignToAll(const T& f) { 203 [[nodiscard]] static constexpr Vec3 AssignToAll(const T& f) {
204 return Vec3(f, f, f); 204 return Vec3(f, f, f);
205 } 205 }
206 206
207 constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const { 207 [[nodiscard]] constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
208 return {x + other.x, y + other.y, z + other.z}; 208 return {x + other.x, y + other.y, z + other.z};
209 } 209 }
210 210
@@ -215,7 +215,7 @@ public:
215 return *this; 215 return *this;
216 } 216 }
217 217
218 constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const { 218 [[nodiscard]] constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
219 return {x - other.x, y - other.y, z - other.z}; 219 return {x - other.x, y - other.y, z - other.z};
220 } 220 }
221 221
@@ -227,16 +227,16 @@ public:
227 } 227 }
228 228
229 template <typename U = T> 229 template <typename U = T>
230 constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const { 230 [[nodiscard]] constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
231 return {-x, -y, -z}; 231 return {-x, -y, -z};
232 } 232 }
233 233
234 constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const { 234 [[nodiscard]] constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
235 return {x * other.x, y * other.y, z * other.z}; 235 return {x * other.x, y * other.y, z * other.z};
236 } 236 }
237 237
238 template <typename V> 238 template <typename V>
239 constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const { 239 [[nodiscard]] constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
240 return {x * f, y * f, z * f}; 240 return {x * f, y * f, z * f};
241 } 241 }
242 242
@@ -246,7 +246,7 @@ public:
246 return *this; 246 return *this;
247 } 247 }
248 template <typename V> 248 template <typename V>
249 constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const { 249 [[nodiscard]] constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
250 return {x / f, y / f, z / f}; 250 return {x / f, y / f, z / f};
251 } 251 }
252 252
@@ -256,20 +256,20 @@ public:
256 return *this; 256 return *this;
257 } 257 }
258 258
259 constexpr T Length2() const { 259 [[nodiscard]] constexpr T Length2() const {
260 return x * x + y * y + z * z; 260 return x * x + y * y + z * z;
261 } 261 }
262 262
263 // Only implemented for T=float 263 // Only implemented for T=float
264 float Length() const; 264 [[nodiscard]] float Length() const;
265 Vec3 Normalized() const; 265 [[nodiscard]] Vec3 Normalized() const;
266 float Normalize(); // returns the previous length, which is often useful 266 [[nodiscard]] float Normalize(); // returns the previous length, which is often useful
267 267
268 constexpr T& operator[](std::size_t i) { 268 [[nodiscard]] constexpr T& operator[](std::size_t i) {
269 return *((&x) + i); 269 return *((&x) + i);
270 } 270 }
271 271
272 constexpr const T& operator[](std::size_t i) const { 272 [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
273 return *((&x) + i); 273 return *((&x) + i);
274 } 274 }
275 275
@@ -280,63 +280,63 @@ public:
280 } 280 }
281 281
282 // Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates) 282 // Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
283 constexpr T& u() { 283 [[nodiscard]] constexpr T& u() {
284 return x; 284 return x;
285 } 285 }
286 constexpr T& v() { 286 [[nodiscard]] constexpr T& v() {
287 return y; 287 return y;
288 } 288 }
289 constexpr T& w() { 289 [[nodiscard]] constexpr T& w() {
290 return z; 290 return z;
291 } 291 }
292 292
293 constexpr T& r() { 293 [[nodiscard]] constexpr T& r() {
294 return x; 294 return x;
295 } 295 }
296 constexpr T& g() { 296 [[nodiscard]] constexpr T& g() {
297 return y; 297 return y;
298 } 298 }
299 constexpr T& b() { 299 [[nodiscard]] constexpr T& b() {
300 return z; 300 return z;
301 } 301 }
302 302
303 constexpr T& s() { 303 [[nodiscard]] constexpr T& s() {
304 return x; 304 return x;
305 } 305 }
306 constexpr T& t() { 306 [[nodiscard]] constexpr T& t() {
307 return y; 307 return y;
308 } 308 }
309 constexpr T& q() { 309 [[nodiscard]] constexpr T& q() {
310 return z; 310 return z;
311 } 311 }
312 312
313 constexpr const T& u() const { 313 [[nodiscard]] constexpr const T& u() const {
314 return x; 314 return x;
315 } 315 }
316 constexpr const T& v() const { 316 [[nodiscard]] constexpr const T& v() const {
317 return y; 317 return y;
318 } 318 }
319 constexpr const T& w() const { 319 [[nodiscard]] constexpr const T& w() const {
320 return z; 320 return z;
321 } 321 }
322 322
323 constexpr const T& r() const { 323 [[nodiscard]] constexpr const T& r() const {
324 return x; 324 return x;
325 } 325 }
326 constexpr const T& g() const { 326 [[nodiscard]] constexpr const T& g() const {
327 return y; 327 return y;
328 } 328 }
329 constexpr const T& b() const { 329 [[nodiscard]] constexpr const T& b() const {
330 return z; 330 return z;
331 } 331 }
332 332
333 constexpr const T& s() const { 333 [[nodiscard]] constexpr const T& s() const {
334 return x; 334 return x;
335 } 335 }
336 constexpr const T& t() const { 336 [[nodiscard]] constexpr const T& t() const {
337 return y; 337 return y;
338 } 338 }
339 constexpr const T& q() const { 339 [[nodiscard]] constexpr const T& q() const {
340 return z; 340 return z;
341 } 341 }
342 342
@@ -345,7 +345,7 @@ public:
345// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all 345// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
346// component names (x<->r) and permutations (xy<->yx) 346// component names (x<->r) and permutations (xy<->yx)
347#define _DEFINE_SWIZZLER2(a, b, name) \ 347#define _DEFINE_SWIZZLER2(a, b, name) \
348 constexpr Vec2<T> name() const { \ 348 [[nodiscard]] constexpr Vec2<T> name() const { \
349 return Vec2<T>(a, b); \ 349 return Vec2<T>(a, b); \
350 } 350 }
351#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \ 351#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
@@ -366,7 +366,7 @@ public:
366}; 366};
367 367
368template <typename T, typename V> 368template <typename T, typename V>
369constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) { 369[[nodiscard]] constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
370 return Vec3<T>(f * vec.x, f * vec.y, f * vec.z); 370 return Vec3<T>(f * vec.x, f * vec.y, f * vec.z);
371} 371}
372 372
@@ -402,16 +402,16 @@ public:
402 : x(x_), y(y_), z(z_), w(w_) {} 402 : x(x_), y(y_), z(z_), w(w_) {}
403 403
404 template <typename T2> 404 template <typename T2>
405 constexpr Vec4<T2> Cast() const { 405 [[nodiscard]] constexpr Vec4<T2> Cast() const {
406 return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z), 406 return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
407 static_cast<T2>(w)); 407 static_cast<T2>(w));
408 } 408 }
409 409
410 static constexpr Vec4 AssignToAll(const T& f) { 410 [[nodiscard]] static constexpr Vec4 AssignToAll(const T& f) {
411 return Vec4(f, f, f, f); 411 return Vec4(f, f, f, f);
412 } 412 }
413 413
414 constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const { 414 [[nodiscard]] constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
415 return {x + other.x, y + other.y, z + other.z, w + other.w}; 415 return {x + other.x, y + other.y, z + other.z, w + other.w};
416 } 416 }
417 417
@@ -423,7 +423,7 @@ public:
423 return *this; 423 return *this;
424 } 424 }
425 425
426 constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const { 426 [[nodiscard]] constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
427 return {x - other.x, y - other.y, z - other.z, w - other.w}; 427 return {x - other.x, y - other.y, z - other.z, w - other.w};
428 } 428 }
429 429
@@ -436,16 +436,16 @@ public:
436 } 436 }
437 437
438 template <typename U = T> 438 template <typename U = T>
439 constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const { 439 [[nodiscard]] constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
440 return {-x, -y, -z, -w}; 440 return {-x, -y, -z, -w};
441 } 441 }
442 442
443 constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const { 443 [[nodiscard]] constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
444 return {x * other.x, y * other.y, z * other.z, w * other.w}; 444 return {x * other.x, y * other.y, z * other.z, w * other.w};
445 } 445 }
446 446
447 template <typename V> 447 template <typename V>
448 constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const { 448 [[nodiscard]] constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
449 return {x * f, y * f, z * f, w * f}; 449 return {x * f, y * f, z * f, w * f};
450 } 450 }
451 451
@@ -456,7 +456,7 @@ public:
456 } 456 }
457 457
458 template <typename V> 458 template <typename V>
459 constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const { 459 [[nodiscard]] constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
460 return {x / f, y / f, z / f, w / f}; 460 return {x / f, y / f, z / f, w / f};
461 } 461 }
462 462
@@ -466,15 +466,15 @@ public:
466 return *this; 466 return *this;
467 } 467 }
468 468
469 constexpr T Length2() const { 469 [[nodiscard]] constexpr T Length2() const {
470 return x * x + y * y + z * z + w * w; 470 return x * x + y * y + z * z + w * w;
471 } 471 }
472 472
473 constexpr T& operator[](std::size_t i) { 473 [[nodiscard]] constexpr T& operator[](std::size_t i) {
474 return *((&x) + i); 474 return *((&x) + i);
475 } 475 }
476 476
477 constexpr const T& operator[](std::size_t i) const { 477 [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
478 return *((&x) + i); 478 return *((&x) + i);
479 } 479 }
480 480
@@ -486,29 +486,29 @@ public:
486 } 486 }
487 487
488 // Common alias: RGBA (colors) 488 // Common alias: RGBA (colors)
489 constexpr T& r() { 489 [[nodiscard]] constexpr T& r() {
490 return x; 490 return x;
491 } 491 }
492 constexpr T& g() { 492 [[nodiscard]] constexpr T& g() {
493 return y; 493 return y;
494 } 494 }
495 constexpr T& b() { 495 [[nodiscard]] constexpr T& b() {
496 return z; 496 return z;
497 } 497 }
498 constexpr T& a() { 498 [[nodiscard]] constexpr T& a() {
499 return w; 499 return w;
500 } 500 }
501 501
502 constexpr const T& r() const { 502 [[nodiscard]] constexpr const T& r() const {
503 return x; 503 return x;
504 } 504 }
505 constexpr const T& g() const { 505 [[nodiscard]] constexpr const T& g() const {
506 return y; 506 return y;
507 } 507 }
508 constexpr const T& b() const { 508 [[nodiscard]] constexpr const T& b() const {
509 return z; 509 return z;
510 } 510 }
511 constexpr const T& a() const { 511 [[nodiscard]] constexpr const T& a() const {
512 return w; 512 return w;
513 } 513 }
514 514
@@ -520,7 +520,7 @@ public:
520// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and 520// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
521// permutations (xy<->yx) 521// permutations (xy<->yx)
522#define _DEFINE_SWIZZLER2(a, b, name) \ 522#define _DEFINE_SWIZZLER2(a, b, name) \
523 constexpr Vec2<T> name() const { \ 523 [[nodiscard]] constexpr Vec2<T> name() const { \
524 return Vec2<T>(a, b); \ 524 return Vec2<T>(a, b); \
525 } 525 }
526#define DEFINE_SWIZZLER2_COMP1(a, a2) \ 526#define DEFINE_SWIZZLER2_COMP1(a, a2) \
@@ -547,7 +547,7 @@ public:
547#undef _DEFINE_SWIZZLER2 547#undef _DEFINE_SWIZZLER2
548 548
549#define _DEFINE_SWIZZLER3(a, b, c, name) \ 549#define _DEFINE_SWIZZLER3(a, b, c, name) \
550 constexpr Vec3<T> name() const { \ 550 [[nodiscard]] constexpr Vec3<T> name() const { \
551 return Vec3<T>(a, b, c); \ 551 return Vec3<T>(a, b, c); \
552 } 552 }
553#define DEFINE_SWIZZLER3_COMP1(a, a2) \ 553#define DEFINE_SWIZZLER3_COMP1(a, a2) \
@@ -581,7 +581,7 @@ public:
581}; 581};
582 582
583template <typename T, typename V> 583template <typename T, typename V>
584constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) { 584[[nodiscard]] constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
585 return {f * vec.x, f * vec.y, f * vec.z, f * vec.w}; 585 return {f * vec.x, f * vec.y, f * vec.z, f * vec.w};
586} 586}
587 587
@@ -593,39 +593,41 @@ constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b
593} 593}
594 594
595template <typename T> 595template <typename T>
596constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) { 596[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
597 return a.x * b.x + a.y * b.y + a.z * b.z; 597 return a.x * b.x + a.y * b.y + a.z * b.z;
598} 598}
599 599
600template <typename T> 600template <typename T>
601constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) { 601[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
602 return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; 602 return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
603} 603}
604 604
605template <typename T> 605template <typename T>
606constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) { 606[[nodiscard]] constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a,
607 const Vec3<T>& b) {
607 return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; 608 return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
608} 609}
609 610
610// linear interpolation via float: 0.0=begin, 1.0=end 611// linear interpolation via float: 0.0=begin, 1.0=end
611template <typename X> 612template <typename X>
612constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end, 613[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
613 const float t) { 614 const float t) {
614 return begin * (1.f - t) + end * t; 615 return begin * (1.f - t) + end * t;
615} 616}
616 617
617// linear interpolation via int: 0=begin, base=end 618// linear interpolation via int: 0=begin, base=end
618template <typename X, int base> 619template <typename X, int base>
619constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end, 620[[nodiscard]] constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin,
620 const int t) { 621 const X& end,
622 const int t) {
621 return (begin * (base - t) + end * t) / base; 623 return (begin * (base - t) + end * t) / base;
622} 624}
623 625
624// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second 626// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
625// interpolation. 627// interpolation.
626template <typename X> 628template <typename X>
627constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s, 629[[nodiscard]] constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11,
628 const float t) { 630 const float s, const float t) {
629 auto y0 = Lerp(x00, x01, s); 631 auto y0 = Lerp(x00, x01, s);
630 auto y1 = Lerp(x10, x11, s); 632 auto y1 = Lerp(x10, x11, s);
631 return Lerp(y0, y1, t); 633 return Lerp(y0, y1, t);
@@ -633,42 +635,42 @@ constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X&
633 635
634// Utility vector factories 636// Utility vector factories
635template <typename T> 637template <typename T>
636constexpr Vec2<T> MakeVec(const T& x, const T& y) { 638[[nodiscard]] constexpr Vec2<T> MakeVec(const T& x, const T& y) {
637 return Vec2<T>{x, y}; 639 return Vec2<T>{x, y};
638} 640}
639 641
640template <typename T> 642template <typename T>
641constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) { 643[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
642 return Vec3<T>{x, y, z}; 644 return Vec3<T>{x, y, z};
643} 645}
644 646
645template <typename T> 647template <typename T>
646constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) { 648[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
647 return MakeVec(x, y, zw[0], zw[1]); 649 return MakeVec(x, y, zw[0], zw[1]);
648} 650}
649 651
650template <typename T> 652template <typename T>
651constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) { 653[[nodiscard]] constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
652 return MakeVec(xy[0], xy[1], z); 654 return MakeVec(xy[0], xy[1], z);
653} 655}
654 656
655template <typename T> 657template <typename T>
656constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) { 658[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
657 return MakeVec(x, yz[0], yz[1]); 659 return MakeVec(x, yz[0], yz[1]);
658} 660}
659 661
660template <typename T> 662template <typename T>
661constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) { 663[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
662 return Vec4<T>{x, y, z, w}; 664 return Vec4<T>{x, y, z, w};
663} 665}
664 666
665template <typename T> 667template <typename T>
666constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) { 668[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
667 return MakeVec(xy[0], xy[1], z, w); 669 return MakeVec(xy[0], xy[1], z, w);
668} 670}
669 671
670template <typename T> 672template <typename T>
671constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) { 673[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
672 return MakeVec(x, yz[0], yz[1], w); 674 return MakeVec(x, yz[0], yz[1], w);
673} 675}
674 676
@@ -676,17 +678,17 @@ constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
676// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error 678// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
677// out soon enough due to misuse of the returned structure. 679// out soon enough due to misuse of the returned structure.
678template <typename T> 680template <typename T>
679constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) { 681[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
680 return MakeVec(xy[0], xy[1], zw[0], zw[1]); 682 return MakeVec(xy[0], xy[1], zw[0], zw[1]);
681} 683}
682 684
683template <typename T> 685template <typename T>
684constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) { 686[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
685 return MakeVec(xyz[0], xyz[1], xyz[2], w); 687 return MakeVec(xyz[0], xyz[1], xyz[2], w);
686} 688}
687 689
688template <typename T> 690template <typename T>
689constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) { 691[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
690 return MakeVec(x, yzw[0], yzw[1], yzw[2]); 692 return MakeVec(x, yzw[0], yzw[1], yzw[2]);
691} 693}
692 694
diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h
index da064e59e..125cb42f0 100644
--- a/src/common/virtual_buffer.h
+++ b/src/common/virtual_buffer.h
@@ -30,23 +30,23 @@ public:
30 base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size)); 30 base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
31 } 31 }
32 32
33 constexpr const T& operator[](std::size_t index) const { 33 [[nodiscard]] constexpr const T& operator[](std::size_t index) const {
34 return base_ptr[index]; 34 return base_ptr[index];
35 } 35 }
36 36
37 constexpr T& operator[](std::size_t index) { 37 [[nodiscard]] constexpr T& operator[](std::size_t index) {
38 return base_ptr[index]; 38 return base_ptr[index];
39 } 39 }
40 40
41 constexpr T* data() { 41 [[nodiscard]] constexpr T* data() {
42 return base_ptr; 42 return base_ptr;
43 } 43 }
44 44
45 constexpr const T* data() const { 45 [[nodiscard]] constexpr const T* data() const {
46 return base_ptr; 46 return base_ptr;
47 } 47 }
48 48
49 constexpr std::size_t size() const { 49 [[nodiscard]] constexpr std::size_t size() const {
50 return alloc_size / sizeof(T); 50 return alloc_size / sizeof(T);
51 } 51 }
52 52
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h
index 367d72134..5db30083d 100644
--- a/src/common/wall_clock.h
+++ b/src/common/wall_clock.h
@@ -14,24 +14,24 @@ namespace Common {
14class WallClock { 14class WallClock {
15public: 15public:
16 /// Returns current wall time in nanoseconds 16 /// Returns current wall time in nanoseconds
17 virtual std::chrono::nanoseconds GetTimeNS() = 0; 17 [[nodiscard]] virtual std::chrono::nanoseconds GetTimeNS() = 0;
18 18
19 /// Returns current wall time in microseconds 19 /// Returns current wall time in microseconds
20 virtual std::chrono::microseconds GetTimeUS() = 0; 20 [[nodiscard]] virtual std::chrono::microseconds GetTimeUS() = 0;
21 21
22 /// Returns current wall time in milliseconds 22 /// Returns current wall time in milliseconds
23 virtual std::chrono::milliseconds GetTimeMS() = 0; 23 [[nodiscard]] virtual std::chrono::milliseconds GetTimeMS() = 0;
24 24
25 /// Returns current wall time in emulated clock cycles 25 /// Returns current wall time in emulated clock cycles
26 virtual u64 GetClockCycles() = 0; 26 [[nodiscard]] virtual u64 GetClockCycles() = 0;
27 27
28 /// Returns current wall time in emulated cpu cycles 28 /// Returns current wall time in emulated cpu cycles
29 virtual u64 GetCPUCycles() = 0; 29 [[nodiscard]] virtual u64 GetCPUCycles() = 0;
30 30
31 virtual void Pause(bool is_paused) = 0; 31 virtual void Pause(bool is_paused) = 0;
32 32
33 /// Tells if the wall clock, uses the host CPU's hardware clock 33 /// Tells if the wall clock, uses the host CPU's hardware clock
34 bool IsNative() const { 34 [[nodiscard]] bool IsNative() const {
35 return is_native; 35 return is_native;
36 } 36 }
37 37
@@ -47,7 +47,7 @@ private:
47 bool is_native; 47 bool is_native;
48}; 48};
49 49
50std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, 50[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
51 u32 emulated_clock_frequency); 51 u32 emulated_clock_frequency);
52 52
53} // namespace Common 53} // namespace Common
diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp
index 978526492..5f45459da 100644
--- a/src/common/zstd_compression.cpp
+++ b/src/common/zstd_compression.cpp
@@ -5,7 +5,6 @@
5#include <algorithm> 5#include <algorithm>
6#include <zstd.h> 6#include <zstd.h>
7 7
8#include "common/assert.h"
9#include "common/zstd_compression.h" 8#include "common/zstd_compression.h"
10 9
11namespace Common::Compression { 10namespace Common::Compression {
diff --git a/src/common/zstd_compression.h b/src/common/zstd_compression.h
index e9de941c8..c26a30ab9 100644
--- a/src/common/zstd_compression.h
+++ b/src/common/zstd_compression.h
@@ -13,24 +13,25 @@ namespace Common::Compression {
13/** 13/**
14 * Compresses a source memory region with Zstandard and returns the compressed data in a vector. 14 * Compresses a source memory region with Zstandard and returns the compressed data in a vector.
15 * 15 *
16 * @param source the uncompressed source memory region. 16 * @param source The uncompressed source memory region.
17 * @param source_size the size in bytes of the uncompressed source memory region. 17 * @param source_size The size of the uncompressed source memory region.
18 * @param compression_level the used compression level. Should be between 1 and 22. 18 * @param compression_level The used compression level. Should be between 1 and 22.
19 * 19 *
20 * @return the compressed data. 20 * @return the compressed data.
21 */ 21 */
22std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level); 22[[nodiscard]] std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size,
23 s32 compression_level);
23 24
24/** 25/**
25 * Compresses a source memory region with Zstandard with the default compression level and returns 26 * Compresses a source memory region with Zstandard with the default compression level and returns
26 * the compressed data in a vector. 27 * the compressed data in a vector.
27 * 28 *
28 * @param source the uncompressed source memory region. 29 * @param source The uncompressed source memory region.
29 * @param source_size the size in bytes of the uncompressed source memory region. 30 * @param source_size The size of the uncompressed source memory region.
30 * 31 *
31 * @return the compressed data. 32 * @return the compressed data.
32 */ 33 */
33std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size); 34[[nodiscard]] std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size);
34 35
35/** 36/**
36 * Decompresses a source memory region with Zstandard and returns the uncompressed data in a vector. 37 * Decompresses a source memory region with Zstandard and returns the uncompressed data in a vector.
@@ -39,6 +40,6 @@ std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_siz
39 * 40 *
40 * @return the decompressed data. 41 * @return the decompressed data.
41 */ 42 */
42std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed); 43[[nodiscard]] std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed);
43 44
44} // namespace Common::Compression \ No newline at end of file 45} // namespace Common::Compression \ No newline at end of file
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 358943429..ef0bae556 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 c09f7ad41..dc591c730 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -36,6 +36,7 @@
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;
@@ -49,7 +50,72 @@ constexpr std::array eticket_source_hashes{
49}; 50};
50// clang-format on 51// clang-format on
51 52
52const 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{{
53 {{S128KeyType::Master, 0}, "master_key_"}, 119 {{S128KeyType::Master, 0}, "master_key_"},
54 {{S128KeyType::Package1, 0}, "package1_key_"}, 120 {{S128KeyType::Package1, 0}, "package1_key_"},
55 {{S128KeyType::Package2, 0}, "package2_key_"}, 121 {{S128KeyType::Package2, 0}, "package2_key_"},
@@ -57,14 +123,13 @@ const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
57 {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"}, 123 {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"},
58 {{S128KeyType::Keyblob, 0}, "keyblob_key_"}, 124 {{S128KeyType::Keyblob, 0}, "keyblob_key_"},
59 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"}, 125 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
60}; 126}};
61 127
62namespace {
63template <std::size_t Size> 128template <std::size_t Size>
64bool IsAllZeroArray(const std::array<u8, Size>& array) { 129bool IsAllZeroArray(const std::array<u8, Size>& array) {
65 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; });
66} 131}
67} // namespace 132} // Anonymous namespace
68 133
69u64 GetSignatureTypeDataSize(SignatureType type) { 134u64 GetSignatureTypeDataSize(SignatureType type) {
70 switch (type) { 135 switch (type) {
@@ -96,13 +161,13 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
96} 161}
97 162
98SignatureType Ticket::GetSignatureType() const { 163SignatureType Ticket::GetSignatureType() const {
99 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 164 if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
100 return ticket->sig_type; 165 return ticket->sig_type;
101 } 166 }
102 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 167 if (const auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
103 return ticket->sig_type; 168 return ticket->sig_type;
104 } 169 }
105 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 170 if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
106 return ticket->sig_type; 171 return ticket->sig_type;
107 } 172 }
108 173
@@ -110,13 +175,13 @@ SignatureType Ticket::GetSignatureType() const {
110} 175}
111 176
112TicketData& Ticket::GetData() { 177TicketData& Ticket::GetData() {
113 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 178 if (auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
114 return ticket->data; 179 return ticket->data;
115 } 180 }
116 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 181 if (auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
117 return ticket->data; 182 return ticket->data;
118 } 183 }
119 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 184 if (auto* ticket = std::get_if<ECDSATicket>(&data)) {
120 return ticket->data; 185 return ticket->data;
121 } 186 }
122 187
@@ -124,13 +189,13 @@ TicketData& Ticket::GetData() {
124} 189}
125 190
126const TicketData& Ticket::GetData() const { 191const TicketData& Ticket::GetData() const {
127 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 192 if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
128 return ticket->data; 193 return ticket->data;
129 } 194 }
130 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 195 if (const auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
131 return ticket->data; 196 return ticket->data;
132 } 197 }
133 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 198 if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
134 return ticket->data; 199 return ticket->data;
135 } 200 }
136 201
@@ -233,8 +298,9 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
233} 298}
234 299
235RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { 300RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const {
236 if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) 301 if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) {
237 return {}; 302 return {};
303 }
238 304
239 const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); 305 const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek);
240 306
@@ -261,27 +327,30 @@ Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source)
261} 327}
262 328
263std::optional<Key128> DeriveSDSeed() { 329std::optional<Key128> DeriveSDSeed() {
264 const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 330 const Common::FS::IOFile save_43(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
265 "/system/save/8000000000000043", 331 "/system/save/8000000000000043",
266 "rb+"); 332 "rb+");
267 if (!save_43.IsOpen()) 333 if (!save_43.IsOpen()) {
268 return {}; 334 return std::nullopt;
335 }
269 336
270 const FileUtil::IOFile sd_private( 337 const Common::FS::IOFile sd_private(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
271 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); 338 "/Nintendo/Contents/private",
272 if (!sd_private.IsOpen()) 339 "rb+");
273 return {}; 340 if (!sd_private.IsOpen()) {
341 return std::nullopt;
342 }
274 343
275 std::array<u8, 0x10> private_seed{}; 344 std::array<u8, 0x10> private_seed{};
276 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()) {
277 return {}; 346 return std::nullopt;
278 } 347 }
279 348
280 std::array<u8, 0x10> buffer{}; 349 std::array<u8, 0x10> buffer{};
281 std::size_t offset = 0; 350 std::size_t offset = 0;
282 for (; offset + 0x10 < save_43.GetSize(); ++offset) { 351 for (; offset + 0x10 < save_43.GetSize(); ++offset) {
283 if (!save_43.Seek(offset, SEEK_SET)) { 352 if (!save_43.Seek(offset, SEEK_SET)) {
284 return {}; 353 return std::nullopt;
285 } 354 }
286 355
287 save_43.ReadBytes(buffer.data(), buffer.size()); 356 save_43.ReadBytes(buffer.data(), buffer.size());
@@ -291,23 +360,26 @@ std::optional<Key128> DeriveSDSeed() {
291 } 360 }
292 361
293 if (!save_43.Seek(offset + 0x10, SEEK_SET)) { 362 if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
294 return {}; 363 return std::nullopt;
295 } 364 }
296 365
297 Key128 seed{}; 366 Key128 seed{};
298 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) { 367 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
299 return {}; 368 return std::nullopt;
300 } 369 }
301 return seed; 370 return seed;
302} 371}
303 372
304Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) { 373Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) {
305 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek))) 374 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek))) {
306 return Loader::ResultStatus::ErrorMissingSDKEKSource; 375 return Loader::ResultStatus::ErrorMissingSDKEKSource;
307 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration))) 376 }
377 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration))) {
308 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource; 378 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource;
309 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) 379 }
380 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) {
310 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource; 381 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource;
382 }
311 383
312 const auto sd_kek_source = 384 const auto sd_kek_source =
313 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek)); 385 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek));
@@ -320,14 +392,17 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
320 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);
321 keys.SetKey(S128KeyType::SDKek, sd_kek); 393 keys.SetKey(S128KeyType::SDKek, sd_kek);
322 394
323 if (!keys.HasKey(S128KeyType::SDSeed)) 395 if (!keys.HasKey(S128KeyType::SDSeed)) {
324 return Loader::ResultStatus::ErrorMissingSDSeed; 396 return Loader::ResultStatus::ErrorMissingSDSeed;
397 }
325 const auto sd_seed = keys.GetKey(S128KeyType::SDSeed); 398 const auto sd_seed = keys.GetKey(S128KeyType::SDSeed);
326 399
327 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save))) 400 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save))) {
328 return Loader::ResultStatus::ErrorMissingSDSaveKeySource; 401 return Loader::ResultStatus::ErrorMissingSDSaveKeySource;
329 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA))) 402 }
403 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA))) {
330 return Loader::ResultStatus::ErrorMissingSDNCAKeySource; 404 return Loader::ResultStatus::ErrorMissingSDNCAKeySource;
405 }
331 406
332 std::array<Key256, 2> sd_key_sources{ 407 std::array<Key256, 2> sd_key_sources{
333 keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)), 408 keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)),
@@ -336,8 +411,9 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
336 411
337 // Combine sources and seed 412 // Combine sources and seed
338 for (auto& source : sd_key_sources) { 413 for (auto& source : sd_key_sources) {
339 for (std::size_t i = 0; i < source.size(); ++i) 414 for (std::size_t i = 0; i < source.size(); ++i) {
340 source[i] ^= sd_seed[i & 0xF]; 415 source[i] ^= sd_seed[i & 0xF];
416 }
341 } 417 }
342 418
343 AESCipher<Key128> cipher(sd_kek, Mode::ECB); 419 AESCipher<Key128> cipher(sd_kek, Mode::ECB);
@@ -355,9 +431,10 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
355 return Loader::ResultStatus::Success; 431 return Loader::ResultStatus::Success;
356} 432}
357 433
358std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save) { 434std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save) {
359 if (!ticket_save.IsOpen()) 435 if (!ticket_save.IsOpen()) {
360 return {}; 436 return {};
437 }
361 438
362 std::vector<u8> buffer(ticket_save.GetSize()); 439 std::vector<u8> buffer(ticket_save.GetSize());
363 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) { 440 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
@@ -417,7 +494,7 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
417 offset = i + 1; 494 offset = i + 1;
418 break; 495 break;
419 } else if (data[i] != 0x0) { 496 } else if (data[i] != 0x0) {
420 return {}; 497 return std::nullopt;
421 } 498 }
422 } 499 }
423 500
@@ -427,16 +504,18 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
427std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, 504std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
428 const RSAKeyPair<2048>& key) { 505 const RSAKeyPair<2048>& key) {
429 const auto issuer = ticket.GetData().issuer; 506 const auto issuer = ticket.GetData().issuer;
430 if (IsAllZeroArray(issuer)) 507 if (IsAllZeroArray(issuer)) {
431 return {}; 508 return std::nullopt;
509 }
432 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') {
433 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.");
434 } 512 }
435 513
436 Key128 rights_id = ticket.GetData().rights_id; 514 Key128 rights_id = ticket.GetData().rights_id;
437 515
438 if (rights_id == Key128{}) 516 if (rights_id == Key128{}) {
439 return {}; 517 return std::nullopt;
518 }
440 519
441 if (!std::any_of(ticket.GetData().title_key_common_pad.begin(), 520 if (!std::any_of(ticket.GetData().title_key_common_pad.begin(),
442 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; })) {
@@ -468,15 +547,17 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
468 std::array<u8, 0xDF> m_2; 547 std::array<u8, 0xDF> m_2;
469 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());
470 549
471 if (m_0 != 0) 550 if (m_0 != 0) {
472 return {}; 551 return std::nullopt;
552 }
473 553
474 m_1 = m_1 ^ MGF1<0x20>(m_2); 554 m_1 = m_1 ^ MGF1<0x20>(m_2);
475 m_2 = m_2 ^ MGF1<0xDF>(m_1); 555 m_2 = m_2 ^ MGF1<0xDF>(m_1);
476 556
477 const auto offset = FindTicketOffset(m_2); 557 const auto offset = FindTicketOffset(m_2);
478 if (!offset) 558 if (!offset) {
479 return {}; 559 return std::nullopt;
560 }
480 ASSERT(*offset > 0); 561 ASSERT(*offset > 0);
481 562
482 Key128 key_temp{}; 563 Key128 key_temp{};
@@ -487,8 +568,8 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
487 568
488KeyManager::KeyManager() { 569KeyManager::KeyManager() {
489 // Initialize keys 570 // Initialize keys
490 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); 571 const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
491 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);
492 if (Settings::values.use_dev_keys) { 573 if (Settings::values.use_dev_keys) {
493 dev_mode = true; 574 dev_mode = true;
494 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false); 575 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);
@@ -506,34 +587,39 @@ KeyManager::KeyManager() {
506} 587}
507 588
508static 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) {
509 if (base.size() < begin + length) 590 if (base.size() < begin + length) {
510 return false; 591 return false;
592 }
511 return std::all_of(base.begin() + begin, base.begin() + begin + length, 593 return std::all_of(base.begin() + begin, base.begin() + begin + length,
512 [](u8 c) { return std::isxdigit(c); }); 594 [](u8 c) { return std::isxdigit(c); });
513} 595}
514 596
515void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { 597void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
516 std::ifstream file; 598 std::ifstream file;
517 OpenFStream(file, filename, std::ios_base::in); 599 Common::FS::OpenFStream(file, filename, std::ios_base::in);
518 if (!file.is_open()) 600 if (!file.is_open()) {
519 return; 601 return;
602 }
520 603
521 std::string line; 604 std::string line;
522 while (std::getline(file, line)) { 605 while (std::getline(file, line)) {
523 std::vector<std::string> out; 606 std::vector<std::string> out;
524 std::stringstream stream(line); 607 std::stringstream stream(line);
525 std::string item; 608 std::string item;
526 while (std::getline(stream, item, '=')) 609 while (std::getline(stream, item, '=')) {
527 out.push_back(std::move(item)); 610 out.push_back(std::move(item));
611 }
528 612
529 if (out.size() != 2) 613 if (out.size() != 2) {
530 continue; 614 continue;
615 }
531 616
532 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());
533 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());
534 619
535 if (out[0].compare(0, 1, "#") == 0) 620 if (out[0].compare(0, 1, "#") == 0) {
536 continue; 621 continue;
622 }
537 623
538 if (is_title_keys) { 624 if (is_title_keys) {
539 auto rights_id_raw = Common::HexStringToArray<16>(out[0]); 625 auto rights_id_raw = Common::HexStringToArray<16>(out[0]);
@@ -543,24 +629,26 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
543 s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key; 629 s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
544 } else { 630 } else {
545 out[0] = Common::ToLower(out[0]); 631 out[0] = Common::ToLower(out[0]);
546 if (s128_file_id.find(out[0]) != s128_file_id.end()) { 632 if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
547 const auto index = s128_file_id.at(out[0]); 633 const auto& index = iter128->second;
548 Key128 key = Common::HexStringToArray<16>(out[1]); 634 const Key128 key = Common::HexStringToArray<16>(out[1]);
549 s128_keys[{index.type, index.field1, index.field2}] = key; 635 s128_keys[{index.type, index.field1, index.field2}] = key;
550 } 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()) {
551 const auto index = s256_file_id.at(out[0]); 637 const auto& index = iter256->second;
552 Key256 key = Common::HexStringToArray<32>(out[1]); 638 const Key256 key = Common::HexStringToArray<32>(out[1]);
553 s256_keys[{index.type, index.field1, index.field2}] = key; 639 s256_keys[{index.type, index.field1, index.field2}] = key;
554 } else if (out[0].compare(0, 8, "keyblob_") == 0 && 640 } else if (out[0].compare(0, 8, "keyblob_") == 0 &&
555 out[0].compare(0, 9, "keyblob_k") != 0) { 641 out[0].compare(0, 9, "keyblob_k") != 0) {
556 if (!ValidCryptoRevisionString(out[0], 8, 2)) 642 if (!ValidCryptoRevisionString(out[0], 8, 2)) {
557 continue; 643 continue;
644 }
558 645
559 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);
560 keyblobs[index] = Common::HexStringToArray<0x90>(out[1]); 647 keyblobs[index] = Common::HexStringToArray<0x90>(out[1]);
561 } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) { 648 } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) {
562 if (!ValidCryptoRevisionString(out[0], 18, 2)) 649 if (!ValidCryptoRevisionString(out[0], 18, 2)) {
563 continue; 650 continue;
651 }
564 652
565 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);
566 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); 654 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
@@ -568,8 +656,9 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
568 eticket_extended_kek = Common::HexStringToArray<576>(out[1]); 656 eticket_extended_kek = Common::HexStringToArray<576>(out[1]);
569 } else { 657 } else {
570 for (const auto& kv : KEYS_VARIABLE_LENGTH) { 658 for (const auto& kv : KEYS_VARIABLE_LENGTH) {
571 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) 659 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) {
572 continue; 660 continue;
661 }
573 if (out[0].compare(0, kv.second.size(), kv.second) == 0) { 662 if (out[0].compare(0, kv.second.size(), kv.second) == 0) {
574 const auto index = 663 const auto index =
575 std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16); 664 std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16);
@@ -604,10 +693,11 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
604 693
605void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, 694void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
606 const std::string& filename, bool title) { 695 const std::string& filename, bool title) {
607 if (FileUtil::Exists(dir1 + DIR_SEP + filename)) 696 if (Common::FS::Exists(dir1 + DIR_SEP + filename)) {
608 LoadFromFile(dir1 + DIR_SEP + filename, title); 697 LoadFromFile(dir1 + DIR_SEP + filename, title);
609 else if (FileUtil::Exists(dir2 + DIR_SEP + filename)) 698 } else if (Common::FS::Exists(dir2 + DIR_SEP + filename)) {
610 LoadFromFile(dir2 + DIR_SEP + filename, title); 699 LoadFromFile(dir2 + DIR_SEP + filename, title);
700 }
611} 701}
612 702
613bool KeyManager::BaseDeriveNecessary() const { 703bool KeyManager::BaseDeriveNecessary() const {
@@ -615,8 +705,9 @@ bool KeyManager::BaseDeriveNecessary() const {
615 return !HasKey(key_type, index1, index2); 705 return !HasKey(key_type, index1, index2);
616 }; 706 };
617 707
618 if (check_key_existence(S256KeyType::Header)) 708 if (check_key_existence(S256KeyType::Header)) {
619 return true; 709 return true;
710 }
620 711
621 for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) { 712 for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) {
622 if (check_key_existence(S128KeyType::Master, i) || 713 if (check_key_existence(S128KeyType::Master, i) ||
@@ -641,14 +732,16 @@ bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) const {
641} 732}
642 733
643Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const { 734Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const {
644 if (!HasKey(id, field1, field2)) 735 if (!HasKey(id, field1, field2)) {
645 return {}; 736 return {};
737 }
646 return s128_keys.at({id, field1, field2}); 738 return s128_keys.at({id, field1, field2});
647} 739}
648 740
649Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const { 741Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
650 if (!HasKey(id, field1, field2)) 742 if (!HasKey(id, field1, field2)) {
651 return {}; 743 return {};
744 }
652 return s256_keys.at({id, field1, field2}); 745 return s256_keys.at({id, field1, field2});
653} 746}
654 747
@@ -670,7 +763,7 @@ Key256 KeyManager::GetBISKey(u8 partition_id) const {
670template <size_t Size> 763template <size_t Size>
671void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname, 764void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
672 const std::array<u8, Size>& key) { 765 const std::array<u8, Size>& key) {
673 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);
674 std::string filename = "title.keys_autogenerated"; 767 std::string filename = "title.keys_autogenerated";
675 if (category == KeyCategory::Standard) { 768 if (category == KeyCategory::Standard) {
676 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated"; 769 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
@@ -679,9 +772,9 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
679 } 772 }
680 773
681 const auto path = yuzu_keys_dir + DIR_SEP + filename; 774 const auto path = yuzu_keys_dir + DIR_SEP + filename;
682 const auto add_info_text = !FileUtil::Exists(path); 775 const auto add_info_text = !Common::FS::Exists(path);
683 FileUtil::CreateFullPath(path); 776 Common::FS::CreateFullPath(path);
684 FileUtil::IOFile file{path, "a"}; 777 Common::FS::IOFile file{path, "a"};
685 if (!file.IsOpen()) { 778 if (!file.IsOpen()) {
686 return; 779 return;
687 } 780 }
@@ -714,8 +807,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
714 } 807 }
715 808
716 const auto iter2 = std::find_if( 809 const auto iter2 = std::find_if(
717 s128_file_id.begin(), s128_file_id.end(), 810 s128_file_id.begin(), s128_file_id.end(), [&id, &field1, &field2](const auto& elem) {
718 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
719 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) ==
720 std::tie(id, field1, field2); 812 std::tie(id, field1, field2);
721 }); 813 });
@@ -725,9 +817,11 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
725 817
726 // Variable cases 818 // Variable cases
727 if (id == S128KeyType::KeyArea) { 819 if (id == S128KeyType::KeyArea) {
728 static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}", 820 static constexpr std::array<const char*, 3> kak_names = {
729 "key_area_key_ocean_{:02X}", 821 "key_area_key_application_{:02X}",
730 "key_area_key_system_{:02X}"}; 822 "key_area_key_ocean_{:02X}",
823 "key_area_key_system_{:02X}",
824 };
731 WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key); 825 WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key);
732 } else if (id == S128KeyType::Master) { 826 } else if (id == S128KeyType::Master) {
733 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key); 827 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
@@ -753,8 +847,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
753 return; 847 return;
754 } 848 }
755 const auto iter = std::find_if( 849 const auto iter = std::find_if(
756 s256_file_id.begin(), s256_file_id.end(), 850 s256_file_id.begin(), s256_file_id.end(), [&id, &field1, &field2](const auto& elem) {
757 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
758 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) ==
759 std::tie(id, field1, field2); 852 std::tie(id, field1, field2);
760 }); 853 });
@@ -765,29 +858,31 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
765} 858}
766 859
767bool KeyManager::KeyFileExists(bool title) { 860bool KeyManager::KeyFileExists(bool title) {
768 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); 861 const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
769 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);
770 if (title) { 863 if (title) {
771 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "title.keys") || 864 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "title.keys") ||
772 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "title.keys"); 865 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "title.keys");
773 } 866 }
774 867
775 if (Settings::values.use_dev_keys) { 868 if (Settings::values.use_dev_keys) {
776 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") || 869 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") ||
777 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys"); 870 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys");
778 } 871 }
779 872
780 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") || 873 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") ||
781 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys"); 874 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys");
782} 875}
783 876
784void KeyManager::DeriveSDSeedLazy() { 877void KeyManager::DeriveSDSeedLazy() {
785 if (HasKey(S128KeyType::SDSeed)) 878 if (HasKey(S128KeyType::SDSeed)) {
786 return; 879 return;
880 }
787 881
788 const auto res = DeriveSDSeed(); 882 const auto res = DeriveSDSeed();
789 if (res) 883 if (res) {
790 SetKey(S128KeyType::SDSeed, *res); 884 SetKey(S128KeyType::SDSeed, *res);
885 }
791} 886}
792 887
793static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) { 888static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
@@ -799,11 +894,13 @@ static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
799} 894}
800 895
801void KeyManager::DeriveBase() { 896void KeyManager::DeriveBase() {
802 if (!BaseDeriveNecessary()) 897 if (!BaseDeriveNecessary()) {
803 return; 898 return;
899 }
804 900
805 if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC)) 901 if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC)) {
806 return; 902 return;
903 }
807 904
808 const auto has_bis = [this](u64 id) { 905 const auto has_bis = [this](u64 id) {
809 return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) && 906 return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) &&
@@ -820,10 +917,11 @@ void KeyManager::DeriveBase() {
820 static_cast<u64>(BISKeyType::Tweak)); 917 static_cast<u64>(BISKeyType::Tweak));
821 }; 918 };
822 919
823 if (has_bis(2) && !has_bis(3)) 920 if (has_bis(2) && !has_bis(3)) {
824 copy_bis(2, 3); 921 copy_bis(2, 3);
825 else if (has_bis(3) && !has_bis(2)) 922 } else if (has_bis(3) && !has_bis(2)) {
826 copy_bis(3, 2); 923 copy_bis(3, 2);
924 }
827 925
828 std::bitset<32> revisions(0xFFFFFFFF); 926 std::bitset<32> revisions(0xFFFFFFFF);
829 for (size_t i = 0; i < revisions.size(); ++i) { 927 for (size_t i = 0; i < revisions.size(); ++i) {
@@ -833,15 +931,17 @@ void KeyManager::DeriveBase() {
833 } 931 }
834 } 932 }
835 933
836 if (!revisions.any()) 934 if (!revisions.any()) {
837 return; 935 return;
936 }
838 937
839 const auto sbk = GetKey(S128KeyType::SecureBoot); 938 const auto sbk = GetKey(S128KeyType::SecureBoot);
840 const auto tsec = GetKey(S128KeyType::TSEC); 939 const auto tsec = GetKey(S128KeyType::TSEC);
841 940
842 for (size_t i = 0; i < revisions.size(); ++i) { 941 for (size_t i = 0; i < revisions.size(); ++i) {
843 if (!revisions[i]) 942 if (!revisions[i]) {
844 continue; 943 continue;
944 }
845 945
846 // Derive keyblob key 946 // Derive keyblob key
847 const auto key = DeriveKeyblobKey( 947 const auto key = DeriveKeyblobKey(
@@ -850,16 +950,18 @@ void KeyManager::DeriveBase() {
850 SetKey(S128KeyType::Keyblob, key, i); 950 SetKey(S128KeyType::Keyblob, key, i);
851 951
852 // Derive keyblob MAC key 952 // Derive keyblob MAC key
853 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))) 953 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))) {
854 continue; 954 continue;
955 }
855 956
856 const auto mac_key = DeriveKeyblobMACKey( 957 const auto mac_key = DeriveKeyblobMACKey(
857 key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))); 958 key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)));
858 SetKey(S128KeyType::KeyblobMAC, mac_key, i); 959 SetKey(S128KeyType::KeyblobMAC, mac_key, i);
859 960
860 Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key); 961 Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key);
861 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) {
862 continue; 963 continue;
964 }
863 965
864 // Decrypt keyblob 966 // Decrypt keyblob
865 if (keyblobs[i] == std::array<u8, 0x90>{}) { 967 if (keyblobs[i] == std::array<u8, 0x90>{}) {
@@ -883,16 +985,19 @@ void KeyManager::DeriveBase() {
883 985
884 revisions.set(); 986 revisions.set();
885 for (size_t i = 0; i < revisions.size(); ++i) { 987 for (size_t i = 0; i < revisions.size(); ++i) {
886 if (!HasKey(S128KeyType::Master, i)) 988 if (!HasKey(S128KeyType::Master, i)) {
887 revisions.reset(i); 989 revisions.reset(i);
990 }
888 } 991 }
889 992
890 if (!revisions.any()) 993 if (!revisions.any()) {
891 return; 994 return;
995 }
892 996
893 for (size_t i = 0; i < revisions.size(); ++i) { 997 for (size_t i = 0; i < revisions.size(); ++i) {
894 if (!revisions[i]) 998 if (!revisions[i]) {
895 continue; 999 continue;
1000 }
896 1001
897 // Derive general purpose keys 1002 // Derive general purpose keys
898 DeriveGeneralPurposeKeys(i); 1003 DeriveGeneralPurposeKeys(i);
@@ -922,16 +1027,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
922 const auto es = Core::System::GetInstance().GetContentProvider().GetEntry( 1027 const auto es = Core::System::GetInstance().GetContentProvider().GetEntry(
923 0x0100000000000033, FileSys::ContentRecordType::Program); 1028 0x0100000000000033, FileSys::ContentRecordType::Program);
924 1029
925 if (es == nullptr) 1030 if (es == nullptr) {
926 return; 1031 return;
1032 }
927 1033
928 const auto exefs = es->GetExeFS(); 1034 const auto exefs = es->GetExeFS();
929 if (exefs == nullptr) 1035 if (exefs == nullptr) {
930 return; 1036 return;
1037 }
931 1038
932 const auto main = exefs->GetFile("main"); 1039 const auto main = exefs->GetFile("main");
933 if (main == nullptr) 1040 if (main == nullptr) {
934 return; 1041 return;
1042 }
935 1043
936 const auto bytes = main->ReadAllBytes(); 1044 const auto bytes = main->ReadAllBytes();
937 1045
@@ -941,16 +1049,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
941 const auto seed3 = data.GetRSAKekSeed3(); 1049 const auto seed3 = data.GetRSAKekSeed3();
942 const auto mask0 = data.GetRSAKekMask0(); 1050 const auto mask0 = data.GetRSAKekMask0();
943 1051
944 if (eticket_kek != Key128{}) 1052 if (eticket_kek != Key128{}) {
945 SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek)); 1053 SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek));
1054 }
946 if (eticket_kekek != Key128{}) { 1055 if (eticket_kekek != Key128{}) {
947 SetKey(S128KeyType::Source, eticket_kekek, 1056 SetKey(S128KeyType::Source, eticket_kekek,
948 static_cast<size_t>(SourceKeyType::ETicketKekek)); 1057 static_cast<size_t>(SourceKeyType::ETicketKekek));
949 } 1058 }
950 if (seed3 != Key128{}) 1059 if (seed3 != Key128{}) {
951 SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3)); 1060 SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3));
952 if (mask0 != Key128{}) 1061 }
1062 if (mask0 != Key128{}) {
953 SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0)); 1063 SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0));
1064 }
954 if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} || 1065 if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} ||
955 mask0 == Key128{}) { 1066 mask0 == Key128{}) {
956 return; 1067 return;
@@ -976,8 +1087,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
976 AESCipher<Key128> es_kek(temp_kekek, Mode::ECB); 1087 AESCipher<Key128> es_kek(temp_kekek, Mode::ECB);
977 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);
978 1089
979 if (eticket_final == Key128{}) 1090 if (eticket_final == Key128{}) {
980 return; 1091 return;
1092 }
981 1093
982 SetKey(S128KeyType::ETicketRSAKek, eticket_final); 1094 SetKey(S128KeyType::ETicketRSAKek, eticket_final);
983 1095
@@ -992,18 +1104,20 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
992void KeyManager::PopulateTickets() { 1104void KeyManager::PopulateTickets() {
993 const auto rsa_key = GetETicketRSAKey(); 1105 const auto rsa_key = GetETicketRSAKey();
994 1106
995 if (rsa_key == RSAKeyPair<2048>{}) 1107 if (rsa_key == RSAKeyPair<2048>{}) {
996 return; 1108 return;
1109 }
997 1110
998 if (!common_tickets.empty() && !personal_tickets.empty()) 1111 if (!common_tickets.empty() && !personal_tickets.empty()) {
999 return; 1112 return;
1113 }
1000 1114
1001 const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1115 const Common::FS::IOFile save1(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1002 "/system/save/80000000000000e1", 1116 "/system/save/80000000000000e1",
1003 "rb+"); 1117 "rb+");
1004 const FileUtil::IOFile save2(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1118 const Common::FS::IOFile save2(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1005 "/system/save/80000000000000e2", 1119 "/system/save/80000000000000e2",
1006 "rb+"); 1120 "rb+");
1007 1121
1008 const auto blob2 = GetTicketblob(save2); 1122 const auto blob2 = GetTicketblob(save2);
1009 auto res = GetTicketblob(save1); 1123 auto res = GetTicketblob(save1);
@@ -1013,8 +1127,10 @@ void KeyManager::PopulateTickets() {
1013 for (std::size_t i = 0; i < res.size(); ++i) { 1127 for (std::size_t i = 0; i < res.size(); ++i) {
1014 const auto common = i < idx; 1128 const auto common = i < idx;
1015 const auto pair = ParseTicket(res[i], rsa_key); 1129 const auto pair = ParseTicket(res[i], rsa_key);
1016 if (!pair) 1130 if (!pair) {
1017 continue; 1131 continue;
1132 }
1133
1018 const auto& [rid, key] = *pair; 1134 const auto& [rid, key] = *pair;
1019 u128 rights_id; 1135 u128 rights_id;
1020 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1136 std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1043,27 +1159,33 @@ void KeyManager::SynthesizeTickets() {
1043} 1159}
1044 1160
1045void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) { 1161void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
1046 if (key == Key128{}) 1162 if (key == Key128{}) {
1047 return; 1163 return;
1164 }
1048 SetKey(id, key, field1, field2); 1165 SetKey(id, key, field1, field2);
1049} 1166}
1050 1167
1051void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) { 1168void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) {
1052 if (key == Key256{}) 1169 if (key == Key256{}) {
1053 return; 1170 return;
1171 }
1172
1054 SetKey(id, key, field1, field2); 1173 SetKey(id, key, field1, field2);
1055} 1174}
1056 1175
1057void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) { 1176void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1058 if (!BaseDeriveNecessary()) 1177 if (!BaseDeriveNecessary()) {
1059 return; 1178 return;
1179 }
1060 1180
1061 if (!data.HasBoot0()) 1181 if (!data.HasBoot0()) {
1062 return; 1182 return;
1183 }
1063 1184
1064 for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) { 1185 for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) {
1065 if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{}) 1186 if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{}) {
1066 continue; 1187 continue;
1188 }
1067 encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i); 1189 encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i);
1068 WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i), 1190 WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i),
1069 encrypted_keyblobs[i]); 1191 encrypted_keyblobs[i]);
@@ -1085,8 +1207,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1085 static_cast<u64>(SourceKeyType::Keyblob), i); 1207 static_cast<u64>(SourceKeyType::Keyblob), i);
1086 } 1208 }
1087 1209
1088 if (data.HasFuses()) 1210 if (data.HasFuses()) {
1089 SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey()); 1211 SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey());
1212 }
1090 1213
1091 DeriveBase(); 1214 DeriveBase();
1092 1215
@@ -1100,8 +1223,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1100 1223
1101 const auto masters = data.GetTZMasterKeys(latest_master); 1224 const auto masters = data.GetTZMasterKeys(latest_master);
1102 for (size_t i = 0; i < masters.size(); ++i) { 1225 for (size_t i = 0; i < masters.size(); ++i) {
1103 if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i)) 1226 if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i)) {
1104 SetKey(S128KeyType::Master, masters[i], i); 1227 SetKey(S128KeyType::Master, masters[i], i);
1228 }
1105 } 1229 }
1106 1230
1107 DeriveBase(); 1231 DeriveBase();
@@ -1111,8 +1235,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1111 1235
1112 std::array<Key128, 0x20> package2_keys{}; 1236 std::array<Key128, 0x20> package2_keys{};
1113 for (size_t i = 0; i < package2_keys.size(); ++i) { 1237 for (size_t i = 0; i < package2_keys.size(); ++i) {
1114 if (HasKey(S128KeyType::Package2, i)) 1238 if (HasKey(S128KeyType::Package2, i)) {
1115 package2_keys[i] = GetKey(S128KeyType::Package2, i); 1239 package2_keys[i] = GetKey(S128KeyType::Package2, i);
1240 }
1116 } 1241 }
1117 data.DecryptPackage2(package2_keys, Package2Type::NormalMain); 1242 data.DecryptPackage2(package2_keys, Package2Type::NormalMain);
1118 1243
@@ -1150,12 +1275,15 @@ const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const {
1150 1275
1151bool KeyManager::AddTicketCommon(Ticket raw) { 1276bool KeyManager::AddTicketCommon(Ticket raw) {
1152 const auto rsa_key = GetETicketRSAKey(); 1277 const auto rsa_key = GetETicketRSAKey();
1153 if (rsa_key == RSAKeyPair<2048>{}) 1278 if (rsa_key == RSAKeyPair<2048>{}) {
1154 return false; 1279 return false;
1280 }
1155 1281
1156 const auto pair = ParseTicket(raw, rsa_key); 1282 const auto pair = ParseTicket(raw, rsa_key);
1157 if (!pair) 1283 if (!pair) {
1158 return false; 1284 return false;
1285 }
1286
1159 const auto& [rid, key] = *pair; 1287 const auto& [rid, key] = *pair;
1160 u128 rights_id; 1288 u128 rights_id;
1161 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1289 std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1166,12 +1294,15 @@ bool KeyManager::AddTicketCommon(Ticket raw) {
1166 1294
1167bool KeyManager::AddTicketPersonalized(Ticket raw) { 1295bool KeyManager::AddTicketPersonalized(Ticket raw) {
1168 const auto rsa_key = GetETicketRSAKey(); 1296 const auto rsa_key = GetETicketRSAKey();
1169 if (rsa_key == RSAKeyPair<2048>{}) 1297 if (rsa_key == RSAKeyPair<2048>{}) {
1170 return false; 1298 return false;
1299 }
1171 1300
1172 const auto pair = ParseTicket(raw, rsa_key); 1301 const auto pair = ParseTicket(raw, rsa_key);
1173 if (!pair) 1302 if (!pair) {
1174 return false; 1303 return false;
1304 }
1305
1175 const auto& [rid, key] = *pair; 1306 const auto& [rid, key] = *pair;
1176 u128 rights_id; 1307 u128 rights_id;
1177 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1308 std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1179,58 +1310,4 @@ bool KeyManager::AddTicketPersonalized(Ticket raw) {
1179 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); 1310 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
1180 return true; 1311 return true;
1181} 1312}
1182
1183const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
1184 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
1185 {"eticket_rsa_kek_source",
1186 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
1187 {"eticket_rsa_kekek_source",
1188 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
1189 {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
1190 {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
1191 {"rsa_oaep_kek_generation_source",
1192 {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
1193 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
1194 {"aes_kek_generation_source",
1195 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
1196 {"aes_key_generation_source",
1197 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
1198 {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
1199 {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
1200 {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
1201 {"key_area_key_application_source",
1202 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1203 static_cast<u64>(KeyAreaKeyType::Application)}},
1204 {"key_area_key_ocean_source",
1205 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1206 static_cast<u64>(KeyAreaKeyType::Ocean)}},
1207 {"key_area_key_system_source",
1208 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1209 static_cast<u64>(KeyAreaKeyType::System)}},
1210 {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
1211 {"keyblob_mac_key_source",
1212 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},
1213 {"tsec_key", {S128KeyType::TSEC, 0, 0}},
1214 {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
1215 {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
1216 {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
1217 {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
1218 {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
1219 {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
1220 {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
1221 {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
1222 {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
1223 {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
1224 {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
1225 {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
1226};
1227
1228const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
1229 {"header_key", {S256KeyType::Header, 0, 0}},
1230 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
1231 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
1232 {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
1233 {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
1234 {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
1235};
1236} // 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 3e96f7516..46136d04a 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -367,8 +367,8 @@ static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header
367 Package2Header temp = header; 367 Package2Header temp = header;
368 AESCipher<Key128> cipher(key, Mode::CTR); 368 AESCipher<Key128> cipher(key, Mode::CTR);
369 cipher.SetIV(header.header_ctr); 369 cipher.SetIV(header.header_ctr);
370 cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr, 370 cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - sizeof(Package2Header::signature),
371 Op::Decrypt); 371 &temp.header_ctr, Op::Decrypt);
372 if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) { 372 if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
373 header = temp; 373 header = temp;
374 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/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/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/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.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/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 4157fbf39..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() {
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp
index 51c2ba964..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()) {
@@ -420,7 +420,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
420 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); 420 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
421 421
422 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { 422 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
423 FileUtil::Delete(path); 423 Common::FS::Delete(path);
424 } 424 }
425 425
426 HandleDownloadDisplayResult(applet_manager, res); 426 HandleDownloadDisplayResult(applet_manager, res);
@@ -428,7 +428,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
428 } 428 }
429 } 429 }
430 430
431 FileUtil::IOFile bin{path, "rb"}; 431 Common::FS::IOFile bin{path, "rb"};
432 const auto size = bin.GetSize(); 432 const auto size = bin.GetSize();
433 std::vector<u8> bytes(size); 433 std::vector<u8> bytes(size);
434 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..0e7794dc7 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -90,7 +90,7 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
90 default: 90 default:
91 UNIMPLEMENTED_MSG("Unknown npad index {}", index); 91 UNIMPLEMENTED_MSG("Unknown npad index {}", index);
92 return 0; 92 return 0;
93 }; 93 }
94} 94}
95 95
96Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} 96Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
@@ -630,7 +630,7 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
630 default: 630 default:
631 UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id); 631 UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
632 return LedPattern{0, 0, 0, 0}; 632 return LedPattern{0, 0, 0, 0};
633 }; 633 }
634} 634}
635 635
636void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { 636void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
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/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 7c48e55e1..9bc3a8840 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -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/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..d328fb8b7 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -121,8 +121,8 @@ void LogSettings() {
121 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); 121 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
122 log_setting("Audio_OutputDevice", values.audio_device_id); 122 log_setting("Audio_OutputDevice", values.audio_device_id);
123 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd); 123 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd);
124 log_setting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); 124 log_setting("DataStorage_NandDir", Common::FS::GetUserPath(Common::FS::UserPath::NANDDir));
125 log_setting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)); 125 log_setting("DataStorage_SdmcDir", Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir));
126 log_setting("Debugging_UseGdbstub", values.use_gdbstub); 126 log_setting("Debugging_UseGdbstub", values.use_gdbstub);
127 log_setting("Debugging_GdbstubPort", values.gdbstub_port); 127 log_setting("Debugging_GdbstubPort", values.gdbstub_port);
128 log_setting("Debugging_ProgramArgs", values.program_args); 128 log_setting("Debugging_ProgramArgs", values.program_args);
diff --git a/src/core/settings.h b/src/core/settings.h
index bb145f193..3681b5e9d 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -359,7 +359,8 @@ enum class GPUAccuracy : u32 {
359 359
360enum class CPUAccuracy { 360enum class CPUAccuracy {
361 Accurate = 0, 361 Accurate = 0,
362 DebugMode = 1, 362 Unsafe = 1,
363 DebugMode = 2,
363}; 364};
364 365
365extern bool configuring_global; 366extern bool configuring_global;
@@ -419,6 +420,9 @@ struct Values {
419 bool cpuopt_misc_ir; 420 bool cpuopt_misc_ir;
420 bool cpuopt_reduce_misalign_checks; 421 bool cpuopt_reduce_misalign_checks;
421 422
423 bool cpuopt_unsafe_unfuse_fma;
424 bool cpuopt_unsafe_reduce_fp_error;
425
422 // Renderer 426 // Renderer
423 Setting<RendererBackend> renderer_backend; 427 Setting<RendererBackend> renderer_backend;
424 bool renderer_debug; 428 bool renderer_debug;
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/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 3cd896a0f..d85f1e9d1 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,3 +1,5 @@
1add_subdirectory(host_shaders)
2
1add_library(video_core STATIC 3add_library(video_core STATIC
2 buffer_cache/buffer_block.h 4 buffer_cache/buffer_block.h
3 buffer_cache/buffer_cache.h 5 buffer_cache/buffer_cache.h
@@ -244,6 +246,9 @@ create_target_directory_groups(video_core)
244target_link_libraries(video_core PUBLIC common core) 246target_link_libraries(video_core PUBLIC common core)
245target_link_libraries(video_core PRIVATE glad xbyak) 247target_link_libraries(video_core PRIVATE glad xbyak)
246 248
249add_dependencies(video_core host_shaders)
250target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
251
247if (ENABLE_VULKAN) 252if (ENABLE_VULKAN)
248 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) 253 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
249 target_compile_definitions(video_core PRIVATE HAS_VULKAN) 254 target_compile_definitions(video_core PRIVATE HAS_VULKAN)
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index ff10ff40d..6e50661a3 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -10,7 +10,13 @@
10 10
11namespace Tegra::Engines { 11namespace Tegra::Engines {
12 12
13Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {} 13Fermi2D::Fermi2D() = default;
14
15Fermi2D::~Fermi2D() = default;
16
17void Fermi2D::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
18 rasterizer = &rasterizer_;
19}
14 20
15void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 21void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
16 ASSERT_MSG(method < Regs::NUM_REGS, 22 ASSERT_MSG(method < Regs::NUM_REGS,
@@ -87,7 +93,7 @@ void Fermi2D::HandleSurfaceCopy() {
87 copy_config.src_rect = src_rect; 93 copy_config.src_rect = src_rect;
88 copy_config.dst_rect = dst_rect; 94 copy_config.dst_rect = dst_rect;
89 95
90 if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst, copy_config)) { 96 if (!rasterizer->AccelerateSurfaceCopy(regs.src, regs.dst, copy_config)) {
91 UNIMPLEMENTED(); 97 UNIMPLEMENTED();
92 } 98 }
93} 99}
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 8f37d053f..213abfaae 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -34,8 +34,11 @@ namespace Tegra::Engines {
34 34
35class Fermi2D final : public EngineInterface { 35class Fermi2D final : public EngineInterface {
36public: 36public:
37 explicit Fermi2D(VideoCore::RasterizerInterface& rasterizer); 37 explicit Fermi2D();
38 ~Fermi2D() = default; 38 ~Fermi2D();
39
40 /// Binds a rasterizer to this engine.
41 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
39 42
40 /// Write the value to the register identified by method. 43 /// Write the value to the register identified by method.
41 void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; 44 void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
@@ -149,7 +152,7 @@ public:
149 }; 152 };
150 153
151private: 154private:
152 VideoCore::RasterizerInterface& rasterizer; 155 VideoCore::RasterizerInterface* rasterizer;
153 156
154 /// Performs the copy from the source surface to the destination surface as configured in the 157 /// Performs the copy from the source surface to the destination surface as configured in the
155 /// registers. 158 /// registers.
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index a82b06a38..898370739 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -16,14 +16,15 @@
16 16
17namespace Tegra::Engines { 17namespace Tegra::Engines {
18 18
19KeplerCompute::KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 19KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_)
20 MemoryManager& memory_manager) 20 : system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {}
21 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, upload_state{
22 memory_manager,
23 regs.upload} {}
24 21
25KeplerCompute::~KeplerCompute() = default; 22KeplerCompute::~KeplerCompute() = default;
26 23
24void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
25 rasterizer = &rasterizer_;
26}
27
27void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 28void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
28 ASSERT_MSG(method < Regs::NUM_REGS, 29 ASSERT_MSG(method < Regs::NUM_REGS,
29 "Invalid KeplerCompute register, increase the size of the Regs structure"); 30 "Invalid KeplerCompute register, increase the size of the Regs structure");
@@ -104,11 +105,11 @@ SamplerDescriptor KeplerCompute::AccessSampler(u32 handle) const {
104} 105}
105 106
106VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() { 107VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() {
107 return rasterizer.AccessGuestDriverProfile(); 108 return rasterizer->AccessGuestDriverProfile();
108} 109}
109 110
110const VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() const { 111const VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() const {
111 return rasterizer.AccessGuestDriverProfile(); 112 return rasterizer->AccessGuestDriverProfile();
112} 113}
113 114
114void KeplerCompute::ProcessLaunch() { 115void KeplerCompute::ProcessLaunch() {
@@ -119,7 +120,7 @@ void KeplerCompute::ProcessLaunch() {
119 const GPUVAddr code_addr = regs.code_loc.Address() + launch_description.program_start; 120 const GPUVAddr code_addr = regs.code_loc.Address() + launch_description.program_start;
120 LOG_TRACE(HW_GPU, "Compute invocation launched at address 0x{:016x}", code_addr); 121 LOG_TRACE(HW_GPU, "Compute invocation launched at address 0x{:016x}", code_addr);
121 122
122 rasterizer.DispatchCompute(code_addr); 123 rasterizer->DispatchCompute(code_addr);
123} 124}
124 125
125Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const { 126Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const {
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index b7f668d88..7f2500aab 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -42,10 +42,12 @@ namespace Tegra::Engines {
42 42
43class KeplerCompute final : public ConstBufferEngineInterface, public EngineInterface { 43class KeplerCompute final : public ConstBufferEngineInterface, public EngineInterface {
44public: 44public:
45 explicit KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 45 explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager);
46 MemoryManager& memory_manager);
47 ~KeplerCompute(); 46 ~KeplerCompute();
48 47
48 /// Binds a rasterizer to this engine.
49 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
50
49 static constexpr std::size_t NumConstBuffers = 8; 51 static constexpr std::size_t NumConstBuffers = 8;
50 52
51 struct Regs { 53 struct Regs {
@@ -230,11 +232,6 @@ public:
230 const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override; 232 const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override;
231 233
232private: 234private:
233 Core::System& system;
234 VideoCore::RasterizerInterface& rasterizer;
235 MemoryManager& memory_manager;
236 Upload::State upload_state;
237
238 void ProcessLaunch(); 235 void ProcessLaunch();
239 236
240 /// Retrieves information about a specific TIC entry from the TIC buffer. 237 /// Retrieves information about a specific TIC entry from the TIC buffer.
@@ -242,6 +239,11 @@ private:
242 239
243 /// Retrieves information about a specific TSC entry from the TSC buffer. 240 /// Retrieves information about a specific TSC entry from the TSC buffer.
244 Texture::TSCEntry GetTSCEntry(u32 tsc_index) const; 241 Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
242
243 Core::System& system;
244 MemoryManager& memory_manager;
245 VideoCore::RasterizerInterface* rasterizer = nullptr;
246 Upload::State upload_state;
245}; 247};
246 248
247#define ASSERT_REG_POSITION(field_name, position) \ 249#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index c01436295..33854445f 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -22,14 +22,19 @@ using VideoCore::QueryType;
22/// First register id that is actually a Macro call. 22/// First register id that is actually a Macro call.
23constexpr u32 MacroRegistersStart = 0xE00; 23constexpr u32 MacroRegistersStart = 0xE00;
24 24
25Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 25Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
26 MemoryManager& memory_manager) 26 : system{system_}, memory_manager{memory_manager_}, macro_engine{GetMacroEngine(*this)},
27 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, 27 upload_state{memory_manager, regs.upload} {
28 macro_engine{GetMacroEngine(*this)}, upload_state{memory_manager, regs.upload} {
29 dirty.flags.flip(); 28 dirty.flags.flip();
30 InitializeRegisterDefaults(); 29 InitializeRegisterDefaults();
31} 30}
32 31
32Maxwell3D::~Maxwell3D() = default;
33
34void Maxwell3D::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
35 rasterizer = &rasterizer_;
36}
37
33void Maxwell3D::InitializeRegisterDefaults() { 38void Maxwell3D::InitializeRegisterDefaults() {
34 // Initializes registers to their default values - what games expect them to be at boot. This is 39 // Initializes registers to their default values - what games expect them to be at boot. This is
35 // for certain registers that may not be explicitly set by games. 40 // for certain registers that may not be explicitly set by games.
@@ -192,7 +197,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
192 197
193 switch (method) { 198 switch (method) {
194 case MAXWELL3D_REG_INDEX(wait_for_idle): { 199 case MAXWELL3D_REG_INDEX(wait_for_idle): {
195 rasterizer.WaitForIdle(); 200 rasterizer->WaitForIdle();
196 break; 201 break;
197 } 202 }
198 case MAXWELL3D_REG_INDEX(shadow_ram_control): { 203 case MAXWELL3D_REG_INDEX(shadow_ram_control): {
@@ -402,7 +407,7 @@ void Maxwell3D::FlushMMEInlineDraw() {
402 407
403 const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed; 408 const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
404 if (ShouldExecute()) { 409 if (ShouldExecute()) {
405 rasterizer.Draw(is_indexed, true); 410 rasterizer->Draw(is_indexed, true);
406 } 411 }
407 412
408 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if 413 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
@@ -465,7 +470,7 @@ void Maxwell3D::ProcessQueryGet() {
465 switch (regs.query.query_get.operation) { 470 switch (regs.query.query_get.operation) {
466 case Regs::QueryOperation::Release: 471 case Regs::QueryOperation::Release:
467 if (regs.query.query_get.fence == 1) { 472 if (regs.query.query_get.fence == 1) {
468 rasterizer.SignalSemaphore(regs.query.QueryAddress(), regs.query.query_sequence); 473 rasterizer->SignalSemaphore(regs.query.QueryAddress(), regs.query.query_sequence);
469 } else { 474 } else {
470 StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0); 475 StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0);
471 } 476 }
@@ -533,7 +538,7 @@ void Maxwell3D::ProcessQueryCondition() {
533void Maxwell3D::ProcessCounterReset() { 538void Maxwell3D::ProcessCounterReset() {
534 switch (regs.counter_reset) { 539 switch (regs.counter_reset) {
535 case Regs::CounterReset::SampleCnt: 540 case Regs::CounterReset::SampleCnt:
536 rasterizer.ResetCounter(QueryType::SamplesPassed); 541 rasterizer->ResetCounter(QueryType::SamplesPassed);
537 break; 542 break;
538 default: 543 default:
539 LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", 544 LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}",
@@ -547,7 +552,7 @@ void Maxwell3D::ProcessSyncPoint() {
547 const u32 increment = regs.sync_info.increment.Value(); 552 const u32 increment = regs.sync_info.increment.Value();
548 [[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value(); 553 [[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value();
549 if (increment) { 554 if (increment) {
550 rasterizer.SignalSyncPoint(sync_point); 555 rasterizer->SignalSyncPoint(sync_point);
551 } 556 }
552} 557}
553 558
@@ -570,7 +575,7 @@ void Maxwell3D::DrawArrays() {
570 575
571 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; 576 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
572 if (ShouldExecute()) { 577 if (ShouldExecute()) {
573 rasterizer.Draw(is_indexed, false); 578 rasterizer->Draw(is_indexed, false);
574 } 579 }
575 580
576 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if 581 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
@@ -590,8 +595,8 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
590 return 0; 595 return 0;
591 case Regs::QuerySelect::SamplesPassed: 596 case Regs::QuerySelect::SamplesPassed:
592 // Deferred. 597 // Deferred.
593 rasterizer.Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed, 598 rasterizer->Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed,
594 system.GPU().GetTicks()); 599 system.GPU().GetTicks());
595 return {}; 600 return {};
596 default: 601 default:
597 LOG_DEBUG(HW_GPU, "Unimplemented query select type {}", 602 LOG_DEBUG(HW_GPU, "Unimplemented query select type {}",
@@ -718,7 +723,7 @@ void Maxwell3D::ProcessClearBuffers() {
718 regs.clear_buffers.R == regs.clear_buffers.B && 723 regs.clear_buffers.R == regs.clear_buffers.B &&
719 regs.clear_buffers.R == regs.clear_buffers.A); 724 regs.clear_buffers.R == regs.clear_buffers.A);
720 725
721 rasterizer.Clear(); 726 rasterizer->Clear();
722} 727}
723 728
724u32 Maxwell3D::AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const { 729u32 Maxwell3D::AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const {
@@ -752,11 +757,11 @@ SamplerDescriptor Maxwell3D::AccessSampler(u32 handle) const {
752} 757}
753 758
754VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() { 759VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() {
755 return rasterizer.AccessGuestDriverProfile(); 760 return rasterizer->AccessGuestDriverProfile();
756} 761}
757 762
758const VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() const { 763const VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() const {
759 return rasterizer.AccessGuestDriverProfile(); 764 return rasterizer->AccessGuestDriverProfile();
760} 765}
761 766
762} // namespace Tegra::Engines 767} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index ef1618990..bc289c55d 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -51,9 +51,11 @@ namespace Tegra::Engines {
51 51
52class Maxwell3D final : public ConstBufferEngineInterface, public EngineInterface { 52class Maxwell3D final : public ConstBufferEngineInterface, public EngineInterface {
53public: 53public:
54 explicit Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 54 explicit Maxwell3D(Core::System& system, MemoryManager& memory_manager);
55 MemoryManager& memory_manager); 55 ~Maxwell3D();
56 ~Maxwell3D() = default; 56
57 /// Binds a rasterizer to this engine.
58 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
57 59
58 /// Register structure of the Maxwell3D engine. 60 /// Register structure of the Maxwell3D engine.
59 /// TODO(Subv): This structure will need to be made bigger as more registers are discovered. 61 /// TODO(Subv): This structure will need to be made bigger as more registers are discovered.
@@ -647,7 +649,7 @@ public:
647 GetX() + GetWidth(), // right 649 GetX() + GetWidth(), // right
648 GetY() // bottom 650 GetY() // bottom
649 }; 651 };
650 }; 652 }
651 653
652 f32 GetX() const { 654 f32 GetX() const {
653 return std::max(0.0f, translate_x - std::fabs(scale_x)); 655 return std::max(0.0f, translate_x - std::fabs(scale_x));
@@ -1418,12 +1420,12 @@ public:
1418 return execute_on; 1420 return execute_on;
1419 } 1421 }
1420 1422
1421 VideoCore::RasterizerInterface& GetRasterizer() { 1423 VideoCore::RasterizerInterface& Rasterizer() {
1422 return rasterizer; 1424 return *rasterizer;
1423 } 1425 }
1424 1426
1425 const VideoCore::RasterizerInterface& GetRasterizer() const { 1427 const VideoCore::RasterizerInterface& Rasterizer() const {
1426 return rasterizer; 1428 return *rasterizer;
1427 } 1429 }
1428 1430
1429 /// Notify a memory write has happened. 1431 /// Notify a memory write has happened.
@@ -1460,11 +1462,10 @@ private:
1460 void InitializeRegisterDefaults(); 1462 void InitializeRegisterDefaults();
1461 1463
1462 Core::System& system; 1464 Core::System& system;
1463
1464 VideoCore::RasterizerInterface& rasterizer;
1465
1466 MemoryManager& memory_manager; 1465 MemoryManager& memory_manager;
1467 1466
1467 VideoCore::RasterizerInterface* rasterizer = nullptr;
1468
1468 /// Start offsets of each macro in macro_memory 1469 /// Start offsets of each macro in macro_memory
1469 std::array<u32, 0x80> macro_positions = {}; 1470 std::array<u32, 0x80> macro_positions = {};
1470 1471
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index a2d3d7823..e88290754 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -94,7 +94,8 @@ void MaxwellDMA::CopyPitchToPitch() {
94} 94}
95 95
96void MaxwellDMA::CopyBlockLinearToPitch() { 96void MaxwellDMA::CopyBlockLinearToPitch() {
97 ASSERT(regs.src_params.block_size.depth == 0); 97 UNIMPLEMENTED_IF(regs.src_params.block_size.depth != 0);
98 UNIMPLEMENTED_IF(regs.src_params.layer != 0);
98 99
99 // Optimized path for micro copies. 100 // Optimized path for micro copies.
100 const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count; 101 const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
@@ -123,17 +124,12 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
123 write_buffer.resize(dst_size); 124 write_buffer.resize(dst_size);
124 } 125 }
125 126
126 if (Settings::IsGPULevelExtreme()) { 127 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
127 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); 128 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
128 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
129 } else {
130 memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
131 memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
132 }
133 129
134 UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel, 130 UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel,
135 read_buffer.data() + src_layer_size * src_params.layer, write_buffer.data(), 131 block_height, src_params.origin.x, src_params.origin.y, write_buffer.data(),
136 block_height, src_params.origin.x, src_params.origin.y); 132 read_buffer.data());
137 133
138 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 134 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
139} 135}
@@ -198,7 +194,6 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
198 if (read_buffer.size() < src_size) { 194 if (read_buffer.size() < src_size) {
199 read_buffer.resize(src_size); 195 read_buffer.resize(src_size);
200 } 196 }
201
202 if (write_buffer.size() < dst_size) { 197 if (write_buffer.size() < dst_size) {
203 write_buffer.resize(dst_size); 198 write_buffer.resize(dst_size);
204 } 199 }
@@ -212,8 +207,8 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
212 } 207 }
213 208
214 UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width, 209 UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width,
215 bytes_per_pixel, read_buffer.data(), write_buffer.data(), 210 bytes_per_pixel, regs.src_params.block_size.height, pos_x, pos_y,
216 regs.src_params.block_size.height, pos_x, pos_y); 211 write_buffer.data(), read_buffer.data());
217 212
218 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 213 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
219} 214}
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index 8b2a6a42c..06cc12d5a 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -5,15 +5,10 @@
5#pragma once 5#pragma once
6 6
7#include <algorithm> 7#include <algorithm>
8#include <array>
9#include <memory>
10#include <queue> 8#include <queue>
11 9
12#include "common/assert.h"
13#include "common/common_types.h" 10#include "common/common_types.h"
14#include "core/core.h" 11#include "core/core.h"
15#include "core/memory.h"
16#include "core/settings.h"
17#include "video_core/gpu.h" 12#include "video_core/gpu.h"
18#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
19#include "video_core/rasterizer_interface.h" 14#include "video_core/rasterizer_interface.h"
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 512578c8b..acb6e6d46 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -27,21 +27,28 @@ namespace Tegra {
27 27
28MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); 28MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
29 29
30GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, bool is_async) 30GPU::GPU(Core::System& system_, bool is_async_)
31 : system{system}, renderer{std::move(renderer_)}, is_async{is_async} { 31 : system{system_}, dma_pusher{std::make_unique<Tegra::DmaPusher>(system, *this)},
32 auto& rasterizer{renderer->Rasterizer()}; 32 memory_manager{std::make_unique<Tegra::MemoryManager>(system)},
33 memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer); 33 maxwell_3d{std::make_unique<Engines::Maxwell3D>(system, *memory_manager)},
34 dma_pusher = std::make_unique<Tegra::DmaPusher>(system, *this); 34 fermi_2d{std::make_unique<Engines::Fermi2D>()},
35 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); 35 kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)},
36 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer); 36 maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)},
37 kepler_compute = std::make_unique<Engines::KeplerCompute>(system, rasterizer, *memory_manager); 37 kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)},
38 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, *memory_manager); 38 shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_} {}
39 kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager);
40 shader_notify = std::make_unique<VideoCore::ShaderNotify>();
41}
42 39
43GPU::~GPU() = default; 40GPU::~GPU() = default;
44 41
42void GPU::BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) {
43 renderer = std::move(renderer_);
44
45 VideoCore::RasterizerInterface& rasterizer = renderer->Rasterizer();
46 memory_manager->BindRasterizer(rasterizer);
47 maxwell_3d->BindRasterizer(rasterizer);
48 fermi_2d->BindRasterizer(rasterizer);
49 kepler_compute->BindRasterizer(rasterizer);
50}
51
45Engines::Maxwell3D& GPU::Maxwell3D() { 52Engines::Maxwell3D& GPU::Maxwell3D() {
46 return *maxwell_3d; 53 return *maxwell_3d;
47} 54}
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index ebfc7b0c7..c7d11deb2 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -142,11 +142,6 @@ class MemoryManager;
142 142
143class GPU { 143class GPU {
144public: 144public:
145 explicit GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
146 bool is_async);
147
148 virtual ~GPU();
149
150 struct MethodCall { 145 struct MethodCall {
151 u32 method{}; 146 u32 method{};
152 u32 argument{}; 147 u32 argument{};
@@ -162,6 +157,12 @@ public:
162 method_count(method_count) {} 157 method_count(method_count) {}
163 }; 158 };
164 159
160 explicit GPU(Core::System& system, bool is_async);
161 virtual ~GPU();
162
163 /// Binds a renderer to the GPU.
164 void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer);
165
165 /// Calls a GPU method. 166 /// Calls a GPU method.
166 void CallMethod(const MethodCall& method_call); 167 void CallMethod(const MethodCall& method_call);
167 168
@@ -345,8 +346,8 @@ private:
345 bool ExecuteMethodOnEngine(u32 method); 346 bool ExecuteMethodOnEngine(u32 method);
346 347
347protected: 348protected:
348 std::unique_ptr<Tegra::DmaPusher> dma_pusher;
349 Core::System& system; 349 Core::System& system;
350 std::unique_ptr<Tegra::DmaPusher> dma_pusher;
350 std::unique_ptr<VideoCore::RendererBase> renderer; 351 std::unique_ptr<VideoCore::RendererBase> renderer;
351 352
352private: 353private:
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index 7b855f63e..70a3d5738 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -10,16 +10,14 @@
10 10
11namespace VideoCommon { 11namespace VideoCommon {
12 12
13GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, 13GPUAsynch::GPUAsynch(Core::System& system) : GPU{system, true}, gpu_thread{system} {}
14 std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
15 : GPU(system, std::move(renderer_), true), gpu_thread{system},
16 cpu_context(renderer->GetRenderWindow().CreateSharedContext()),
17 gpu_context(std::move(context)) {}
18 14
19GPUAsynch::~GPUAsynch() = default; 15GPUAsynch::~GPUAsynch() = default;
20 16
21void GPUAsynch::Start() { 17void GPUAsynch::Start() {
22 gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher); 18 gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher);
19 cpu_context = renderer->GetRenderWindow().CreateSharedContext();
20 cpu_context->MakeCurrent();
23} 21}
24 22
25void GPUAsynch::ObtainContext() { 23void GPUAsynch::ObtainContext() {
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index 15e9f1d38..f89c855a5 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -20,8 +20,7 @@ namespace VideoCommon {
20/// Implementation of GPU interface that runs the GPU asynchronously 20/// Implementation of GPU interface that runs the GPU asynchronously
21class GPUAsynch final : public Tegra::GPU { 21class GPUAsynch final : public Tegra::GPU {
22public: 22public:
23 explicit GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, 23 explicit GPUAsynch(Core::System& system);
24 std::unique_ptr<Core::Frontend::GraphicsContext>&& context);
25 ~GPUAsynch() override; 24 ~GPUAsynch() override;
26 25
27 void Start() override; 26 void Start() override;
@@ -42,7 +41,6 @@ protected:
42private: 41private:
43 GPUThread::ThreadManager gpu_thread; 42 GPUThread::ThreadManager gpu_thread;
44 std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context; 43 std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
45 std::unique_ptr<Core::Frontend::GraphicsContext> gpu_context;
46}; 44};
47 45
48} // namespace VideoCommon 46} // namespace VideoCommon
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
index aaeb9811d..1ca47ddef 100644
--- a/src/video_core/gpu_synch.cpp
+++ b/src/video_core/gpu_synch.cpp
@@ -7,20 +7,18 @@
7 7
8namespace VideoCommon { 8namespace VideoCommon {
9 9
10GPUSynch::GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, 10GPUSynch::GPUSynch(Core::System& system) : GPU{system, false} {}
11 std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
12 : GPU(system, std::move(renderer), false), context{std::move(context)} {}
13 11
14GPUSynch::~GPUSynch() = default; 12GPUSynch::~GPUSynch() = default;
15 13
16void GPUSynch::Start() {} 14void GPUSynch::Start() {}
17 15
18void GPUSynch::ObtainContext() { 16void GPUSynch::ObtainContext() {
19 context->MakeCurrent(); 17 renderer->Context().MakeCurrent();
20} 18}
21 19
22void GPUSynch::ReleaseContext() { 20void GPUSynch::ReleaseContext() {
23 context->DoneCurrent(); 21 renderer->Context().DoneCurrent();
24} 22}
25 23
26void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { 24void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 762c20aa5..297258cb1 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -19,8 +19,7 @@ namespace VideoCommon {
19/// Implementation of GPU interface that runs the GPU synchronously 19/// Implementation of GPU interface that runs the GPU synchronously
20class GPUSynch final : public Tegra::GPU { 20class GPUSynch final : public Tegra::GPU {
21public: 21public:
22 explicit GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, 22 explicit GPUSynch(Core::System& system);
23 std::unique_ptr<Core::Frontend::GraphicsContext>&& context);
24 ~GPUSynch() override; 23 ~GPUSynch() override;
25 24
26 void Start() override; 25 void Start() override;
@@ -36,9 +35,6 @@ public:
36protected: 35protected:
37 void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id, 36 void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,
38 [[maybe_unused]] u32 value) const override {} 37 [[maybe_unused]] u32 value) const override {}
39
40private:
41 std::unique_ptr<Core::Frontend::GraphicsContext> context;
42}; 38};
43 39
44} // namespace VideoCommon 40} // namespace VideoCommon
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
new file mode 100644
index 000000000..aa62363a7
--- /dev/null
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -0,0 +1,43 @@
1set(SHADER_FILES
2 opengl_present.frag
3 opengl_present.vert
4)
5
6set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include)
7set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
8
9set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders)
10add_custom_command(
11 OUTPUT
12 ${SHADER_DIR}
13 COMMAND
14 ${CMAKE_COMMAND} -E make_directory ${SHADER_DIR}
15)
16
17set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in)
18set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake)
19
20foreach(FILENAME IN ITEMS ${SHADER_FILES})
21 string(REPLACE "." "_" SHADER_NAME ${FILENAME})
22 set(SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME})
23 set(HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}.h)
24 add_custom_command(
25 OUTPUT
26 ${HEADER_FILE}
27 COMMAND
28 ${CMAKE_COMMAND} -P ${HEADER_GENERATOR} ${SOURCE_FILE} ${HEADER_FILE} ${INPUT_FILE}
29 MAIN_DEPENDENCY
30 ${SOURCE_FILE}
31 DEPENDS
32 ${HEADER_GENERATOR}
33 ${INPUT_FILE}
34 )
35 set(SHADER_HEADERS ${SHADER_HEADERS} ${HEADER_FILE})
36endforeach()
37
38add_custom_target(host_shaders
39 DEPENDS
40 ${SHADER_HEADERS}
41 SOURCES
42 ${SHADER_FILES}
43)
diff --git a/src/video_core/host_shaders/StringShaderHeader.cmake b/src/video_core/host_shaders/StringShaderHeader.cmake
new file mode 100644
index 000000000..368bce0ed
--- /dev/null
+++ b/src/video_core/host_shaders/StringShaderHeader.cmake
@@ -0,0 +1,11 @@
1set(SOURCE_FILE ${CMAKE_ARGV3})
2set(HEADER_FILE ${CMAKE_ARGV4})
3set(INPUT_FILE ${CMAKE_ARGV5})
4
5get_filename_component(CONTENTS_NAME ${SOURCE_FILE} NAME)
6string(REPLACE "." "_" CONTENTS_NAME ${CONTENTS_NAME})
7string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME)
8
9file(READ ${SOURCE_FILE} CONTENTS)
10
11configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY)
diff --git a/src/video_core/host_shaders/opengl_present.frag b/src/video_core/host_shaders/opengl_present.frag
new file mode 100644
index 000000000..8a4cb024b
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_present.frag
@@ -0,0 +1,10 @@
1#version 430 core
2
3layout (location = 0) in vec2 frag_tex_coord;
4layout (location = 0) out vec4 color;
5
6layout (binding = 0) uniform sampler2D color_texture;
7
8void main() {
9 color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
10}
diff --git a/src/video_core/host_shaders/opengl_present.vert b/src/video_core/host_shaders/opengl_present.vert
new file mode 100644
index 000000000..2235d31a4
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_present.vert
@@ -0,0 +1,24 @@
1#version 430 core
2
3out gl_PerVertex {
4 vec4 gl_Position;
5};
6
7layout (location = 0) in vec2 vert_position;
8layout (location = 1) in vec2 vert_tex_coord;
9layout (location = 0) out vec2 frag_tex_coord;
10
11// This is a truncated 3x3 matrix for 2D transformations:
12// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
13// The third column performs translation.
14// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
15// implicitly be [0, 0, 1]
16layout (location = 0) uniform mat3x2 modelview_matrix;
17
18void main() {
19 // Multiply input position by the rotscale part of the matrix and then manually translate by
20 // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
21 // to `vec3(vert_position.xy, 1.0)`
22 gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
23 frag_tex_coord = vert_tex_coord;
24}
diff --git a/src/video_core/host_shaders/source_shader.h.in b/src/video_core/host_shaders/source_shader.h.in
new file mode 100644
index 000000000..ccdb0d2a9
--- /dev/null
+++ b/src/video_core/host_shaders/source_shader.h.in
@@ -0,0 +1,9 @@
1#pragma once
2
3#include <string_view>
4
5namespace HostShaders {
6
7constexpr std::string_view @CONTENTS_NAME@ = R"(@CONTENTS@)";
8
9} // namespace HostShaders
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 0c9ff59a4..df00b57df 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -24,7 +24,7 @@ void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
24 maxwell3d.regs.index_array.first = parameters[4]; 24 maxwell3d.regs.index_array.first = parameters[4];
25 25
26 if (maxwell3d.ShouldExecute()) { 26 if (maxwell3d.ShouldExecute()) {
27 maxwell3d.GetRasterizer().Draw(true, true); 27 maxwell3d.Rasterizer().Draw(true, true);
28 } 28 }
29 maxwell3d.regs.index_array.count = 0; 29 maxwell3d.regs.index_array.count = 0;
30 maxwell3d.mme_draw.instance_count = 0; 30 maxwell3d.mme_draw.instance_count = 0;
@@ -42,7 +42,7 @@ void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
42 maxwell3d.mme_draw.instance_count = count; 42 maxwell3d.mme_draw.instance_count = count;
43 43
44 if (maxwell3d.ShouldExecute()) { 44 if (maxwell3d.ShouldExecute()) {
45 maxwell3d.GetRasterizer().Draw(false, true); 45 maxwell3d.Rasterizer().Draw(false, true);
46 } 46 }
47 maxwell3d.regs.vertex_buffer.count = 0; 47 maxwell3d.regs.vertex_buffer.count = 0;
48 maxwell3d.mme_draw.instance_count = 0; 48 maxwell3d.mme_draw.instance_count = 0;
@@ -65,7 +65,7 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
65 maxwell3d.regs.draw.topology.Assign( 65 maxwell3d.regs.draw.topology.Assign(
66 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); 66 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
67 if (maxwell3d.ShouldExecute()) { 67 if (maxwell3d.ShouldExecute()) {
68 maxwell3d.GetRasterizer().Draw(true, true); 68 maxwell3d.Rasterizer().Draw(true, true);
69 } 69 }
70 maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base? 70 maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base?
71 maxwell3d.regs.index_array.count = 0; 71 maxwell3d.regs.index_array.count = 0;
diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp
index aa5256419..bd01fd1f2 100644
--- a/src/video_core/macro/macro_interpreter.cpp
+++ b/src/video_core/macro/macro_interpreter.cpp
@@ -34,7 +34,6 @@ void MacroInterpreterImpl::Execute(const std::vector<u32>& parameters, u32 metho
34 this->parameters = std::make_unique<u32[]>(num_parameters); 34 this->parameters = std::make_unique<u32[]>(num_parameters);
35 } 35 }
36 std::memcpy(this->parameters.get(), parameters.data(), num_parameters * sizeof(u32)); 36 std::memcpy(this->parameters.get(), parameters.data(), num_parameters * sizeof(u32));
37 this->num_parameters = num_parameters;
38 37
39 // Execute the code until we hit an exit condition. 38 // Execute the code until we hit an exit condition.
40 bool keep_executing = true; 39 bool keep_executing = true;
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 844164645..16b2aaa27 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -14,11 +14,15 @@
14 14
15namespace Tegra { 15namespace Tegra {
16 16
17MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer) 17MemoryManager::MemoryManager(Core::System& system_)
18 : system{system}, rasterizer{rasterizer}, page_table(page_table_size) {} 18 : system{system_}, page_table(page_table_size) {}
19 19
20MemoryManager::~MemoryManager() = default; 20MemoryManager::~MemoryManager() = default;
21 21
22void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
23 rasterizer = &rasterizer_;
24}
25
22GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) { 26GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
23 u64 remaining_size{size}; 27 u64 remaining_size{size};
24 for (u64 offset{}; offset < size; offset += page_size) { 28 for (u64 offset{}; offset < size; offset += page_size) {
@@ -217,7 +221,7 @@ void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::siz
217 221
218 // Flush must happen on the rasterizer interface, such that memory is always synchronous 222 // Flush must happen on the rasterizer interface, such that memory is always synchronous
219 // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu. 223 // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
220 rasterizer.FlushRegion(src_addr, copy_amount); 224 rasterizer->FlushRegion(src_addr, copy_amount);
221 system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount); 225 system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
222 } 226 }
223 227
@@ -266,7 +270,7 @@ void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, s
266 270
267 // Invalidate must happen on the rasterizer interface, such that memory is always 271 // Invalidate must happen on the rasterizer interface, such that memory is always
268 // synchronous when it is written (even when in asynchronous GPU mode). 272 // synchronous when it is written (even when in asynchronous GPU mode).
269 rasterizer.InvalidateRegion(dest_addr, copy_amount); 273 rasterizer->InvalidateRegion(dest_addr, copy_amount);
270 system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount); 274 system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
271 } 275 }
272 276
@@ -312,10 +316,10 @@ void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_add
312 WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size); 316 WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size);
313} 317}
314 318
315bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) { 319bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
316 const auto cpu_addr{GpuToCpuAddress(gpu_addr)}; 320 const auto cpu_addr{GpuToCpuAddress(gpu_addr)};
317 if (!cpu_addr) { 321 if (!cpu_addr) {
318 return {}; 322 return false;
319 } 323 }
320 const std::size_t page{(*cpu_addr & Core::Memory::PAGE_MASK) + size}; 324 const std::size_t page{(*cpu_addr & Core::Memory::PAGE_MASK) + size};
321 return page <= Core::Memory::PAGE_SIZE; 325 return page <= Core::Memory::PAGE_SIZE;
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 681bd9588..53c8d122a 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -31,19 +31,19 @@ public:
31 constexpr PageEntry(State state) : state{state} {} 31 constexpr PageEntry(State state) : state{state} {}
32 constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {} 32 constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {}
33 33
34 constexpr bool IsUnmapped() const { 34 [[nodiscard]] constexpr bool IsUnmapped() const {
35 return state == State::Unmapped; 35 return state == State::Unmapped;
36 } 36 }
37 37
38 constexpr bool IsAllocated() const { 38 [[nodiscard]] constexpr bool IsAllocated() const {
39 return state == State::Allocated; 39 return state == State::Allocated;
40 } 40 }
41 41
42 constexpr bool IsValid() const { 42 [[nodiscard]] constexpr bool IsValid() const {
43 return !IsUnmapped() && !IsAllocated(); 43 return !IsUnmapped() && !IsAllocated();
44 } 44 }
45 45
46 constexpr VAddr ToAddress() const { 46 [[nodiscard]] constexpr VAddr ToAddress() const {
47 if (!IsValid()) { 47 if (!IsValid()) {
48 return {}; 48 return {};
49 } 49 }
@@ -51,7 +51,7 @@ public:
51 return static_cast<VAddr>(state) << ShiftBits; 51 return static_cast<VAddr>(state) << ShiftBits;
52 } 52 }
53 53
54 constexpr PageEntry operator+(u64 offset) { 54 [[nodiscard]] constexpr PageEntry operator+(u64 offset) const {
55 // If this is a reserved value, offsets do not apply 55 // If this is a reserved value, offsets do not apply
56 if (!IsValid()) { 56 if (!IsValid()) {
57 return *this; 57 return *this;
@@ -68,19 +68,22 @@ static_assert(sizeof(PageEntry) == 4, "PageEntry is too large");
68 68
69class MemoryManager final { 69class MemoryManager final {
70public: 70public:
71 explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer); 71 explicit MemoryManager(Core::System& system);
72 ~MemoryManager(); 72 ~MemoryManager();
73 73
74 std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const; 74 /// Binds a renderer to the memory manager.
75 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
76
77 [[nodiscard]] std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
75 78
76 template <typename T> 79 template <typename T>
77 T Read(GPUVAddr addr) const; 80 [[nodiscard]] T Read(GPUVAddr addr) const;
78 81
79 template <typename T> 82 template <typename T>
80 void Write(GPUVAddr addr, T data); 83 void Write(GPUVAddr addr, T data);
81 84
82 u8* GetPointer(GPUVAddr addr); 85 [[nodiscard]] u8* GetPointer(GPUVAddr addr);
83 const u8* GetPointer(GPUVAddr addr) const; 86 [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const;
84 87
85 /** 88 /**
86 * ReadBlock and WriteBlock are full read and write operations over virtual 89 * ReadBlock and WriteBlock are full read and write operations over virtual
@@ -109,24 +112,24 @@ public:
109 /** 112 /**
110 * IsGranularRange checks if a gpu region can be simply read with a pointer. 113 * IsGranularRange checks if a gpu region can be simply read with a pointer.
111 */ 114 */
112 bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size); 115 [[nodiscard]] bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const;
113 116
114 GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size); 117 [[nodiscard]] GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size);
115 GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align); 118 [[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align);
116 std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size); 119 [[nodiscard]] std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size);
117 GPUVAddr Allocate(std::size_t size, std::size_t align); 120 [[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align);
118 void Unmap(GPUVAddr gpu_addr, std::size_t size); 121 void Unmap(GPUVAddr gpu_addr, std::size_t size);
119 122
120private: 123private:
121 PageEntry GetPageEntry(GPUVAddr gpu_addr) const; 124 [[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
122 void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size); 125 void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size);
123 GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size); 126 GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size);
124 std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const; 127 [[nodiscard]] std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const;
125 128
126 void TryLockPage(PageEntry page_entry, std::size_t size); 129 void TryLockPage(PageEntry page_entry, std::size_t size);
127 void TryUnlockPage(PageEntry page_entry, std::size_t size); 130 void TryUnlockPage(PageEntry page_entry, std::size_t size);
128 131
129 static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) { 132 [[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
130 return (gpu_addr >> page_bits) & page_table_mask; 133 return (gpu_addr >> page_bits) & page_table_mask;
131 } 134 }
132 135
@@ -141,7 +144,7 @@ private:
141 144
142 Core::System& system; 145 Core::System& system;
143 146
144 VideoCore::RasterizerInterface& rasterizer; 147 VideoCore::RasterizerInterface* rasterizer = nullptr;
145 148
146 std::vector<PageEntry> page_table; 149 std::vector<PageEntry> page_table;
147}; 150};
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index dfb06e87e..a93a1732c 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -9,7 +9,9 @@
9 9
10namespace VideoCore { 10namespace VideoCore {
11 11
12RendererBase::RendererBase(Core::Frontend::EmuWindow& window) : render_window{window} { 12RendererBase::RendererBase(Core::Frontend::EmuWindow& window_,
13 std::unique_ptr<Core::Frontend::GraphicsContext> context_)
14 : render_window{window_}, context{std::move(context_)} {
13 RefreshBaseSettings(); 15 RefreshBaseSettings();
14} 16}
15 17
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 1d85219b6..649074acd 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -15,7 +15,8 @@
15 15
16namespace Core::Frontend { 16namespace Core::Frontend {
17class EmuWindow; 17class EmuWindow;
18} 18class GraphicsContext;
19} // namespace Core::Frontend
19 20
20namespace VideoCore { 21namespace VideoCore {
21 22
@@ -25,14 +26,15 @@ struct RendererSettings {
25 26
26 // Screenshot 27 // Screenshot
27 std::atomic<bool> screenshot_requested{false}; 28 std::atomic<bool> screenshot_requested{false};
28 void* screenshot_bits; 29 void* screenshot_bits{};
29 std::function<void()> screenshot_complete_callback; 30 std::function<void()> screenshot_complete_callback;
30 Layout::FramebufferLayout screenshot_framebuffer_layout; 31 Layout::FramebufferLayout screenshot_framebuffer_layout;
31}; 32};
32 33
33class RendererBase : NonCopyable { 34class RendererBase : NonCopyable {
34public: 35public:
35 explicit RendererBase(Core::Frontend::EmuWindow& window); 36 explicit RendererBase(Core::Frontend::EmuWindow& window,
37 std::unique_ptr<Core::Frontend::GraphicsContext> context);
36 virtual ~RendererBase(); 38 virtual ~RendererBase();
37 39
38 /// Initialize the renderer 40 /// Initialize the renderer
@@ -68,6 +70,14 @@ public:
68 return *rasterizer; 70 return *rasterizer;
69 } 71 }
70 72
73 Core::Frontend::GraphicsContext& Context() {
74 return *context;
75 }
76
77 const Core::Frontend::GraphicsContext& Context() const {
78 return *context;
79 }
80
71 Core::Frontend::EmuWindow& GetRenderWindow() { 81 Core::Frontend::EmuWindow& GetRenderWindow() {
72 return render_window; 82 return render_window;
73 } 83 }
@@ -94,6 +104,7 @@ public:
94protected: 104protected:
95 Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle. 105 Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
96 std::unique_ptr<RasterizerInterface> rasterizer; 106 std::unique_ptr<RasterizerInterface> rasterizer;
107 std::unique_ptr<Core::Frontend::GraphicsContext> context;
97 f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer 108 f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer
98 int m_current_frame = 0; ///< Current frame, should be set by the renderer 109 int m_current_frame = 0; ///< Current frame, should be set by the renderer
99 110
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp
index ec5421afa..3d2588dd2 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp
@@ -4,16 +4,17 @@
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6 6
7#include <glad/glad.h>
8
7#include "video_core/renderer_opengl/gl_buffer_cache.h" 9#include "video_core/renderer_opengl/gl_buffer_cache.h"
8#include "video_core/renderer_opengl/gl_fence_manager.h" 10#include "video_core/renderer_opengl/gl_fence_manager.h"
9 11
10namespace OpenGL { 12namespace OpenGL {
11 13
12GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed) 14GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed) : FenceBase(payload, is_stubbed) {}
13 : VideoCommon::FenceBase(payload, is_stubbed), sync_object{} {}
14 15
15GLInnerFence::GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed) 16GLInnerFence::GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed)
16 : VideoCommon::FenceBase(address, payload, is_stubbed), sync_object{} {} 17 : FenceBase(address, payload, is_stubbed) {}
17 18
18GLInnerFence::~GLInnerFence() = default; 19GLInnerFence::~GLInnerFence() = default;
19 20
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h
index c917b3343..1686cf5c8 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.h
+++ b/src/video_core/renderer_opengl/gl_fence_manager.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <glad/glad.h>
9 8
10#include "common/common_types.h" 9#include "common/common_types.h"
11#include "video_core/fence_manager.h" 10#include "video_core/fence_manager.h"
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index cb284db77..4af5824cd 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -177,15 +177,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
177 } 177 }
178 178
179 if (device.UseAsynchronousShaders()) { 179 if (device.UseAsynchronousShaders()) {
180 // Max worker threads we should allow 180 async_shaders.AllocateWorkers();
181 constexpr u32 MAX_THREADS = 4;
182 // Deduce how many threads we can use
183 const u32 threads_used = std::thread::hardware_concurrency() / 4;
184 // Always allow at least 1 thread regardless of our settings
185 const auto max_worker_count = std::max(1U, threads_used);
186 // Don't use more than MAX_THREADS
187 const auto worker_count = std::min(max_worker_count, MAX_THREADS);
188 async_shaders.AllocateWorkers(worker_count);
189 } 181 }
190} 182}
191 183
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index a787e27d2..0ebcec427 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
5#include <utility> 6#include <utility>
6#include <glad/glad.h> 7#include <glad/glad.h>
7#include "common/common_types.h" 8#include "common/common_types.h"
@@ -82,11 +83,13 @@ void OGLSampler::Release() {
82 handle = 0; 83 handle = 0;
83} 84}
84 85
85void OGLShader::Create(const char* source, GLenum type) { 86void OGLShader::Create(std::string_view source, GLenum type) {
86 if (handle != 0) 87 if (handle != 0) {
87 return; 88 return;
88 if (source == nullptr) 89 }
90 if (source.empty()) {
89 return; 91 return;
92 }
90 93
91 MICROPROFILE_SCOPE(OpenGL_ResourceCreation); 94 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
92 handle = GLShader::LoadShader(source, type); 95 handle = GLShader::LoadShader(source, type);
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index b05cb641c..f48398669 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <string_view>
7#include <utility> 8#include <utility>
8#include <glad/glad.h> 9#include <glad/glad.h>
9#include "common/common_types.h" 10#include "common/common_types.h"
@@ -127,7 +128,7 @@ public:
127 return *this; 128 return *this;
128 } 129 }
129 130
130 void Create(const char* source, GLenum type); 131 void Create(std::string_view source, GLenum type);
131 132
132 void Release(); 133 void Release();
133 134
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index eb49a36bf..a07d56ef0 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -22,6 +22,7 @@
22#include "video_core/memory_manager.h" 22#include "video_core/memory_manager.h"
23#include "video_core/renderer_opengl/gl_arb_decompiler.h" 23#include "video_core/renderer_opengl/gl_arb_decompiler.h"
24#include "video_core/renderer_opengl/gl_rasterizer.h" 24#include "video_core/renderer_opengl/gl_rasterizer.h"
25#include "video_core/renderer_opengl/gl_resource_manager.h"
25#include "video_core/renderer_opengl/gl_shader_cache.h" 26#include "video_core/renderer_opengl/gl_shader_cache.h"
26#include "video_core/renderer_opengl/gl_shader_decompiler.h" 27#include "video_core/renderer_opengl/gl_shader_decompiler.h"
27#include "video_core/renderer_opengl/gl_shader_disk_cache.h" 28#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 2dcc2b0eb..40c0877c1 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -73,7 +73,7 @@ ShaderDiskCacheEntry::ShaderDiskCacheEntry() = default;
73 73
74ShaderDiskCacheEntry::~ShaderDiskCacheEntry() = default; 74ShaderDiskCacheEntry::~ShaderDiskCacheEntry() = default;
75 75
76bool ShaderDiskCacheEntry::Load(FileUtil::IOFile& file) { 76bool ShaderDiskCacheEntry::Load(Common::FS::IOFile& file) {
77 if (file.ReadBytes(&type, sizeof(u32)) != sizeof(u32)) { 77 if (file.ReadBytes(&type, sizeof(u32)) != sizeof(u32)) {
78 return false; 78 return false;
79 } 79 }
@@ -144,7 +144,7 @@ bool ShaderDiskCacheEntry::Load(FileUtil::IOFile& file) {
144 return true; 144 return true;
145} 145}
146 146
147bool ShaderDiskCacheEntry::Save(FileUtil::IOFile& file) const { 147bool ShaderDiskCacheEntry::Save(Common::FS::IOFile& file) const {
148 if (file.WriteObject(static_cast<u32>(type)) != 1 || 148 if (file.WriteObject(static_cast<u32>(type)) != 1 ||
149 file.WriteObject(static_cast<u32>(code.size())) != 1 || 149 file.WriteObject(static_cast<u32>(code.size())) != 1 ||
150 file.WriteObject(static_cast<u32>(code_b.size())) != 1) { 150 file.WriteObject(static_cast<u32>(code_b.size())) != 1) {
@@ -214,20 +214,20 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
214 // Skip games without title id 214 // Skip games without title id
215 const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0; 215 const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
216 if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) { 216 if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) {
217 return {}; 217 return std::nullopt;
218 } 218 }
219 219
220 FileUtil::IOFile file(GetTransferablePath(), "rb"); 220 Common::FS::IOFile file(GetTransferablePath(), "rb");
221 if (!file.IsOpen()) { 221 if (!file.IsOpen()) {
222 LOG_INFO(Render_OpenGL, "No transferable shader cache found"); 222 LOG_INFO(Render_OpenGL, "No transferable shader cache found");
223 is_usable = true; 223 is_usable = true;
224 return {}; 224 return std::nullopt;
225 } 225 }
226 226
227 u32 version{}; 227 u32 version{};
228 if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) { 228 if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) {
229 LOG_ERROR(Render_OpenGL, "Failed to get transferable cache version, skipping it"); 229 LOG_ERROR(Render_OpenGL, "Failed to get transferable cache version, skipping it");
230 return {}; 230 return std::nullopt;
231 } 231 }
232 232
233 if (version < NativeVersion) { 233 if (version < NativeVersion) {
@@ -235,12 +235,12 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
235 file.Close(); 235 file.Close();
236 InvalidateTransferable(); 236 InvalidateTransferable();
237 is_usable = true; 237 is_usable = true;
238 return {}; 238 return std::nullopt;
239 } 239 }
240 if (version > NativeVersion) { 240 if (version > NativeVersion) {
241 LOG_WARNING(Render_OpenGL, "Transferable shader cache was generated with a newer version " 241 LOG_WARNING(Render_OpenGL, "Transferable shader cache was generated with a newer version "
242 "of the emulator, skipping"); 242 "of the emulator, skipping");
243 return {}; 243 return std::nullopt;
244 } 244 }
245 245
246 // Version is valid, load the shaders 246 // Version is valid, load the shaders
@@ -249,7 +249,7 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
249 ShaderDiskCacheEntry& entry = entries.emplace_back(); 249 ShaderDiskCacheEntry& entry = entries.emplace_back();
250 if (!entry.Load(file)) { 250 if (!entry.Load(file)) {
251 LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry, skipping"); 251 LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry, skipping");
252 return {}; 252 return std::nullopt;
253 } 253 }
254 } 254 }
255 255
@@ -262,7 +262,7 @@ std::vector<ShaderDiskCachePrecompiled> ShaderDiskCacheOpenGL::LoadPrecompiled()
262 return {}; 262 return {};
263 } 263 }
264 264
265 FileUtil::IOFile file(GetPrecompiledPath(), "rb"); 265 Common::FS::IOFile file(GetPrecompiledPath(), "rb");
266 if (!file.IsOpen()) { 266 if (!file.IsOpen()) {
267 LOG_INFO(Render_OpenGL, "No precompiled shader cache found"); 267 LOG_INFO(Render_OpenGL, "No precompiled shader cache found");
268 return {}; 268 return {};
@@ -279,7 +279,7 @@ std::vector<ShaderDiskCachePrecompiled> ShaderDiskCacheOpenGL::LoadPrecompiled()
279} 279}
280 280
281std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::LoadPrecompiledFile( 281std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::LoadPrecompiledFile(
282 FileUtil::IOFile& file) { 282 Common::FS::IOFile& file) {
283 // Read compressed file from disk and decompress to virtual precompiled cache file 283 // Read compressed file from disk and decompress to virtual precompiled cache file
284 std::vector<u8> compressed(file.GetSize()); 284 std::vector<u8> compressed(file.GetSize());
285 file.ReadBytes(compressed.data(), compressed.size()); 285 file.ReadBytes(compressed.data(), compressed.size());
@@ -290,12 +290,12 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo
290 ShaderCacheVersionHash file_hash{}; 290 ShaderCacheVersionHash file_hash{};
291 if (!LoadArrayFromPrecompiled(file_hash.data(), file_hash.size())) { 291 if (!LoadArrayFromPrecompiled(file_hash.data(), file_hash.size())) {
292 precompiled_cache_virtual_file_offset = 0; 292 precompiled_cache_virtual_file_offset = 0;
293 return {}; 293 return std::nullopt;
294 } 294 }
295 if (GetShaderCacheVersionHash() != file_hash) { 295 if (GetShaderCacheVersionHash() != file_hash) {
296 LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator"); 296 LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator");
297 precompiled_cache_virtual_file_offset = 0; 297 precompiled_cache_virtual_file_offset = 0;
298 return {}; 298 return std::nullopt;
299 } 299 }
300 300
301 std::vector<ShaderDiskCachePrecompiled> entries; 301 std::vector<ShaderDiskCachePrecompiled> entries;
@@ -305,19 +305,20 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo
305 if (!LoadObjectFromPrecompiled(entry.unique_identifier) || 305 if (!LoadObjectFromPrecompiled(entry.unique_identifier) ||
306 !LoadObjectFromPrecompiled(entry.binary_format) || 306 !LoadObjectFromPrecompiled(entry.binary_format) ||
307 !LoadObjectFromPrecompiled(binary_size)) { 307 !LoadObjectFromPrecompiled(binary_size)) {
308 return {}; 308 return std::nullopt;
309 } 309 }
310 310
311 entry.binary.resize(binary_size); 311 entry.binary.resize(binary_size);
312 if (!LoadArrayFromPrecompiled(entry.binary.data(), entry.binary.size())) { 312 if (!LoadArrayFromPrecompiled(entry.binary.data(), entry.binary.size())) {
313 return {}; 313 return std::nullopt;
314 } 314 }
315 } 315 }
316 return entries; 316
317 return std::move(entries);
317} 318}
318 319
319void ShaderDiskCacheOpenGL::InvalidateTransferable() { 320void ShaderDiskCacheOpenGL::InvalidateTransferable() {
320 if (!FileUtil::Delete(GetTransferablePath())) { 321 if (!Common::FS::Delete(GetTransferablePath())) {
321 LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}", 322 LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}",
322 GetTransferablePath()); 323 GetTransferablePath());
323 } 324 }
@@ -328,7 +329,7 @@ void ShaderDiskCacheOpenGL::InvalidatePrecompiled() {
328 // Clear virtaul precompiled cache file 329 // Clear virtaul precompiled cache file
329 precompiled_cache_virtual_file.Resize(0); 330 precompiled_cache_virtual_file.Resize(0);
330 331
331 if (!FileUtil::Delete(GetPrecompiledPath())) { 332 if (!Common::FS::Delete(GetPrecompiledPath())) {
332 LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath()); 333 LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath());
333 } 334 }
334} 335}
@@ -344,7 +345,7 @@ void ShaderDiskCacheOpenGL::SaveEntry(const ShaderDiskCacheEntry& entry) {
344 return; 345 return;
345 } 346 }
346 347
347 FileUtil::IOFile file = AppendTransferableFile(); 348 Common::FS::IOFile file = AppendTransferableFile();
348 if (!file.IsOpen()) { 349 if (!file.IsOpen()) {
349 return; 350 return;
350 } 351 }
@@ -386,15 +387,15 @@ void ShaderDiskCacheOpenGL::SavePrecompiled(u64 unique_identifier, GLuint progra
386 } 387 }
387} 388}
388 389
389FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const { 390Common::FS::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const {
390 if (!EnsureDirectories()) { 391 if (!EnsureDirectories()) {
391 return {}; 392 return {};
392 } 393 }
393 394
394 const auto transferable_path{GetTransferablePath()}; 395 const auto transferable_path{GetTransferablePath()};
395 const bool existed = FileUtil::Exists(transferable_path); 396 const bool existed = Common::FS::Exists(transferable_path);
396 397
397 FileUtil::IOFile file(transferable_path, "ab"); 398 Common::FS::IOFile file(transferable_path, "ab");
398 if (!file.IsOpen()) { 399 if (!file.IsOpen()) {
399 LOG_ERROR(Render_OpenGL, "Failed to open transferable cache in path={}", transferable_path); 400 LOG_ERROR(Render_OpenGL, "Failed to open transferable cache in path={}", transferable_path);
400 return {}; 401 return {};
@@ -426,7 +427,7 @@ void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() {
426 Common::Compression::CompressDataZSTDDefault(uncompressed.data(), uncompressed.size()); 427 Common::Compression::CompressDataZSTDDefault(uncompressed.data(), uncompressed.size());
427 428
428 const auto precompiled_path{GetPrecompiledPath()}; 429 const auto precompiled_path{GetPrecompiledPath()};
429 FileUtil::IOFile file(precompiled_path, "wb"); 430 Common::FS::IOFile file(precompiled_path, "wb");
430 431
431 if (!file.IsOpen()) { 432 if (!file.IsOpen()) {
432 LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path); 433 LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path);
@@ -440,24 +441,24 @@ void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() {
440 441
441bool ShaderDiskCacheOpenGL::EnsureDirectories() const { 442bool ShaderDiskCacheOpenGL::EnsureDirectories() const {
442 const auto CreateDir = [](const std::string& dir) { 443 const auto CreateDir = [](const std::string& dir) {
443 if (!FileUtil::CreateDir(dir)) { 444 if (!Common::FS::CreateDir(dir)) {
444 LOG_ERROR(Render_OpenGL, "Failed to create directory={}", dir); 445 LOG_ERROR(Render_OpenGL, "Failed to create directory={}", dir);
445 return false; 446 return false;
446 } 447 }
447 return true; 448 return true;
448 }; 449 };
449 450
450 return CreateDir(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) && 451 return CreateDir(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)) &&
451 CreateDir(GetBaseDir()) && CreateDir(GetTransferableDir()) && 452 CreateDir(GetBaseDir()) && CreateDir(GetTransferableDir()) &&
452 CreateDir(GetPrecompiledDir()); 453 CreateDir(GetPrecompiledDir());
453} 454}
454 455
455std::string ShaderDiskCacheOpenGL::GetTransferablePath() const { 456std::string ShaderDiskCacheOpenGL::GetTransferablePath() const {
456 return FileUtil::SanitizePath(GetTransferableDir() + DIR_SEP_CHR + GetTitleID() + ".bin"); 457 return Common::FS::SanitizePath(GetTransferableDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
457} 458}
458 459
459std::string ShaderDiskCacheOpenGL::GetPrecompiledPath() const { 460std::string ShaderDiskCacheOpenGL::GetPrecompiledPath() const {
460 return FileUtil::SanitizePath(GetPrecompiledDir() + DIR_SEP_CHR + GetTitleID() + ".bin"); 461 return Common::FS::SanitizePath(GetPrecompiledDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
461} 462}
462 463
463std::string ShaderDiskCacheOpenGL::GetTransferableDir() const { 464std::string ShaderDiskCacheOpenGL::GetTransferableDir() const {
@@ -469,7 +470,7 @@ std::string ShaderDiskCacheOpenGL::GetPrecompiledDir() const {
469} 470}
470 471
471std::string ShaderDiskCacheOpenGL::GetBaseDir() const { 472std::string ShaderDiskCacheOpenGL::GetBaseDir() const {
472 return FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + DIR_SEP "opengl"; 473 return Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir) + DIR_SEP "opengl";
473} 474}
474 475
475std::string ShaderDiskCacheOpenGL::GetTitleID() const { 476std::string ShaderDiskCacheOpenGL::GetTitleID() const {
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
index a79cef0e9..db2bb73bc 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
@@ -25,7 +25,7 @@ namespace Core {
25class System; 25class System;
26} 26}
27 27
28namespace FileUtil { 28namespace Common::FS {
29class IOFile; 29class IOFile;
30} 30}
31 31
@@ -38,9 +38,9 @@ struct ShaderDiskCacheEntry {
38 ShaderDiskCacheEntry(); 38 ShaderDiskCacheEntry();
39 ~ShaderDiskCacheEntry(); 39 ~ShaderDiskCacheEntry();
40 40
41 bool Load(FileUtil::IOFile& file); 41 bool Load(Common::FS::IOFile& file);
42 42
43 bool Save(FileUtil::IOFile& file) const; 43 bool Save(Common::FS::IOFile& file) const;
44 44
45 bool HasProgramA() const { 45 bool HasProgramA() const {
46 return !code.empty() && !code_b.empty(); 46 return !code.empty() && !code_b.empty();
@@ -97,10 +97,10 @@ public:
97private: 97private:
98 /// Loads the transferable cache. Returns empty on failure. 98 /// Loads the transferable cache. Returns empty on failure.
99 std::optional<std::vector<ShaderDiskCachePrecompiled>> LoadPrecompiledFile( 99 std::optional<std::vector<ShaderDiskCachePrecompiled>> LoadPrecompiledFile(
100 FileUtil::IOFile& file); 100 Common::FS::IOFile& file);
101 101
102 /// Opens current game's transferable file and write it's header if it doesn't exist 102 /// Opens current game's transferable file and write it's header if it doesn't exist
103 FileUtil::IOFile AppendTransferableFile() const; 103 Common::FS::IOFile AppendTransferableFile() const;
104 104
105 /// Save precompiled header to precompiled_cache_in_memory 105 /// Save precompiled header to precompiled_cache_in_memory
106 void SavePrecompiledHeaderToVirtualPrecompiledCache(); 106 void SavePrecompiledHeaderToVirtualPrecompiledCache();
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index 9e74eda0d..4bf0d6090 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
5#include <vector> 6#include <vector>
6#include <glad/glad.h> 7#include <glad/glad.h>
7#include "common/assert.h" 8#include "common/assert.h"
@@ -11,7 +12,8 @@
11namespace OpenGL::GLShader { 12namespace OpenGL::GLShader {
12 13
13namespace { 14namespace {
14const char* GetStageDebugName(GLenum type) { 15
16std::string_view StageDebugName(GLenum type) {
15 switch (type) { 17 switch (type) {
16 case GL_VERTEX_SHADER: 18 case GL_VERTEX_SHADER:
17 return "vertex"; 19 return "vertex";
@@ -25,12 +27,17 @@ const char* GetStageDebugName(GLenum type) {
25 UNIMPLEMENTED(); 27 UNIMPLEMENTED();
26 return "unknown"; 28 return "unknown";
27} 29}
30
28} // Anonymous namespace 31} // Anonymous namespace
29 32
30GLuint LoadShader(const char* source, GLenum type) { 33GLuint LoadShader(std::string_view source, GLenum type) {
31 const char* debug_type = GetStageDebugName(type); 34 const std::string_view debug_type = StageDebugName(type);
32 const GLuint shader_id = glCreateShader(type); 35 const GLuint shader_id = glCreateShader(type);
33 glShaderSource(shader_id, 1, &source, nullptr); 36
37 const GLchar* source_string = source.data();
38 const GLint source_length = static_cast<GLint>(source.size());
39
40 glShaderSource(shader_id, 1, &source_string, &source_length);
34 LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); 41 LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type);
35 glCompileShader(shader_id); 42 glCompileShader(shader_id);
36 43
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h
index 03b7548c2..1b770532e 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.h
+++ b/src/video_core/renderer_opengl/gl_shader_util.h
@@ -38,7 +38,7 @@ void LogShaderSource(T... shaders) {
38 * @param source String of the GLSL shader program 38 * @param source String of the GLSL shader program
39 * @param type Type of the shader (GL_VERTEX_SHADER, GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER) 39 * @param type Type of the shader (GL_VERTEX_SHADER, GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER)
40 */ 40 */
41GLuint LoadShader(const char* source, GLenum type); 41GLuint LoadShader(std::string_view source, GLenum type);
42 42
43/** 43/**
44 * Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader) 44 * Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader)
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 0a7bc9e2b..f403f388a 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -403,7 +403,7 @@ void CachedSurface::DecorateSurfaceName() {
403 LabelGLObject(GL_TEXTURE, texture.handle, GetGpuAddr(), params.TargetName()); 403 LabelGLObject(GL_TEXTURE, texture.handle, GetGpuAddr(), params.TargetName());
404} 404}
405 405
406void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, std::string prefix) { 406void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, const std::string& prefix) {
407 LabelGLObject(GL_TEXTURE, main_view.handle, gpu_addr, prefix); 407 LabelGLObject(GL_TEXTURE, main_view.handle, gpu_addr, prefix);
408} 408}
409 409
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index bfc4ddf5d..de8f18489 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -90,7 +90,7 @@ public:
90 Tegra::Texture::SwizzleSource z_source, 90 Tegra::Texture::SwizzleSource z_source,
91 Tegra::Texture::SwizzleSource w_source); 91 Tegra::Texture::SwizzleSource w_source);
92 92
93 void DecorateViewName(GPUVAddr gpu_addr, std::string prefix); 93 void DecorateViewName(GPUVAddr gpu_addr, const std::string& prefix);
94 94
95 void MarkAsModified(u64 tick) { 95 void MarkAsModified(u64 tick) {
96 surface.MarkAsModified(true, tick); 96 surface.MarkAsModified(true, tick);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 52e9e8250..b759c2dba 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -21,6 +21,8 @@
21#include "core/perf_stats.h" 21#include "core/perf_stats.h"
22#include "core/settings.h" 22#include "core/settings.h"
23#include "core/telemetry_session.h" 23#include "core/telemetry_session.h"
24#include "video_core/host_shaders/opengl_present_frag.h"
25#include "video_core/host_shaders/opengl_present_vert.h"
24#include "video_core/morton.h" 26#include "video_core/morton.h"
25#include "video_core/renderer_opengl/gl_rasterizer.h" 27#include "video_core/renderer_opengl/gl_rasterizer.h"
26#include "video_core/renderer_opengl/gl_shader_manager.h" 28#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -44,46 +46,6 @@ struct Frame {
44 bool is_srgb{}; /// Framebuffer is sRGB or RGB 46 bool is_srgb{}; /// Framebuffer is sRGB or RGB
45}; 47};
46 48
47constexpr char VERTEX_SHADER[] = R"(
48#version 430 core
49
50out gl_PerVertex {
51 vec4 gl_Position;
52};
53
54layout (location = 0) in vec2 vert_position;
55layout (location = 1) in vec2 vert_tex_coord;
56layout (location = 0) out vec2 frag_tex_coord;
57
58// This is a truncated 3x3 matrix for 2D transformations:
59// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
60// The third column performs translation.
61// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
62// implicitly be [0, 0, 1]
63layout (location = 0) uniform mat3x2 modelview_matrix;
64
65void main() {
66 // Multiply input position by the rotscale part of the matrix and then manually translate by
67 // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
68 // to `vec3(vert_position.xy, 1.0)`
69 gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
70 frag_tex_coord = vert_tex_coord;
71}
72)";
73
74constexpr char FRAGMENT_SHADER[] = R"(
75#version 430 core
76
77layout (location = 0) in vec2 frag_tex_coord;
78layout (location = 0) out vec4 color;
79
80layout (binding = 0) uniform sampler2D color_texture;
81
82void main() {
83 color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
84}
85)";
86
87constexpr GLint PositionLocation = 0; 49constexpr GLint PositionLocation = 0;
88constexpr GLint TexCoordLocation = 1; 50constexpr GLint TexCoordLocation = 1;
89constexpr GLint ModelViewMatrixLocation = 0; 51constexpr GLint ModelViewMatrixLocation = 0;
@@ -313,10 +275,11 @@ public:
313 } 275 }
314}; 276};
315 277
316RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system, 278RendererOpenGL::RendererOpenGL(Core::System& system_, Core::Frontend::EmuWindow& emu_window_,
317 Core::Frontend::GraphicsContext& context) 279 Tegra::GPU& gpu_,
318 : RendererBase{emu_window}, emu_window{emu_window}, system{system}, context{context}, 280 std::unique_ptr<Core::Frontend::GraphicsContext> context_)
319 program_manager{device}, has_debug_tool{HasDebugTool()} {} 281 : RendererBase{emu_window_, std::move(context_)}, system{system_},
282 emu_window{emu_window_}, gpu{gpu_}, program_manager{device}, has_debug_tool{HasDebugTool()} {}
320 283
321RendererOpenGL::~RendererOpenGL() = default; 284RendererOpenGL::~RendererOpenGL() = default;
322 285
@@ -384,7 +347,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
384 if (has_debug_tool) { 347 if (has_debug_tool) {
385 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 348 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
386 Present(0); 349 Present(0);
387 context.SwapBuffers(); 350 context->SwapBuffers();
388 } 351 }
389} 352}
390 353
@@ -460,10 +423,10 @@ void RendererOpenGL::InitOpenGLObjects() {
460 423
461 // Create shader programs 424 // Create shader programs
462 OGLShader vertex_shader; 425 OGLShader vertex_shader;
463 vertex_shader.Create(VERTEX_SHADER, GL_VERTEX_SHADER); 426 vertex_shader.Create(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
464 427
465 OGLShader fragment_shader; 428 OGLShader fragment_shader;
466 fragment_shader.Create(FRAGMENT_SHADER, GL_FRAGMENT_SHADER); 429 fragment_shader.Create(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER);
467 430
468 vertex_program.Create(true, false, vertex_shader.handle); 431 vertex_program.Create(true, false, vertex_shader.handle);
469 fragment_program.Create(true, false, fragment_shader.handle); 432 fragment_program.Create(true, false, fragment_shader.handle);
@@ -509,9 +472,10 @@ void RendererOpenGL::AddTelemetryFields() {
509 LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model); 472 LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);
510 473
511 auto& telemetry_session = system.TelemetrySession(); 474 auto& telemetry_session = system.TelemetrySession();
512 telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_Vendor", gpu_vendor); 475 constexpr auto user_system = Common::Telemetry::FieldType::UserSystem;
513 telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model); 476 telemetry_session.AddField(user_system, "GPU_Vendor", gpu_vendor);
514 telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_OpenGL_Version", gl_version); 477 telemetry_session.AddField(user_system, "GPU_Model", gpu_model);
478 telemetry_session.AddField(user_system, "GPU_OpenGL_Version", gl_version);
515} 479}
516 480
517void RendererOpenGL::CreateRasterizer() { 481void RendererOpenGL::CreateRasterizer() {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 8b18d32e6..52ea76b7d 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -56,8 +56,9 @@ class FrameMailbox;
56 56
57class RendererOpenGL final : public VideoCore::RendererBase { 57class RendererOpenGL final : public VideoCore::RendererBase {
58public: 58public:
59 explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system, 59 explicit RendererOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
60 Core::Frontend::GraphicsContext& context); 60 Tegra::GPU& gpu,
61 std::unique_ptr<Core::Frontend::GraphicsContext> context);
61 ~RendererOpenGL() override; 62 ~RendererOpenGL() override;
62 63
63 bool Init() override; 64 bool Init() override;
@@ -93,9 +94,9 @@ private:
93 94
94 bool Present(int timeout_ms); 95 bool Present(int timeout_ms);
95 96
96 Core::Frontend::EmuWindow& emu_window;
97 Core::System& system; 97 Core::System& system;
98 Core::Frontend::GraphicsContext& context; 98 Core::Frontend::EmuWindow& emu_window;
99 Tegra::GPU& gpu;
99 const Device device; 100 const Device device;
100 101
101 StateTracker state_tracker{system}; 102 StateTracker state_tracker{system};
@@ -120,7 +121,7 @@ private:
120 std::vector<u8> gl_framebuffer_data; 121 std::vector<u8> gl_framebuffer_data;
121 122
122 /// Used for transforming the framebuffer orientation 123 /// Used for transforming the framebuffer orientation
123 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags; 124 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{};
124 Common::Rectangle<int> framebuffer_crop_rect; 125 Common::Rectangle<int> framebuffer_crop_rect;
125 126
126 /// Frame presentation mailbox 127 /// Frame presentation mailbox
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
index 435c8c1b8..5b01020ec 100644
--- a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
+++ b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
@@ -65,10 +65,10 @@ bool NsightAftermathTracker::Initialize() {
65 return false; 65 return false;
66 } 66 }
67 67
68 dump_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "gpucrash"; 68 dump_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir) + "gpucrash";
69 69
70 (void)FileUtil::DeleteDirRecursively(dump_dir); 70 (void)Common::FS::DeleteDirRecursively(dump_dir);
71 if (!FileUtil::CreateDir(dump_dir)) { 71 if (!Common::FS::CreateDir(dump_dir)) {
72 LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory"); 72 LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory");
73 return false; 73 return false;
74 } 74 }
@@ -106,7 +106,7 @@ void NsightAftermathTracker::SaveShader(const std::vector<u32>& spirv) const {
106 return; 106 return;
107 } 107 }
108 108
109 FileUtil::IOFile file(fmt::format("{}/source_{:016x}.spv", dump_dir, hash.hash), "wb"); 109 Common::FS::IOFile file(fmt::format("{}/source_{:016x}.spv", dump_dir, hash.hash), "wb");
110 if (!file.IsOpen()) { 110 if (!file.IsOpen()) {
111 LOG_ERROR(Render_Vulkan, "Failed to dump SPIR-V module with hash={:016x}", hash.hash); 111 LOG_ERROR(Render_Vulkan, "Failed to dump SPIR-V module with hash={:016x}", hash.hash);
112 return; 112 return;
@@ -156,12 +156,12 @@ void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump,
156 }(); 156 }();
157 157
158 std::string_view dump_view(static_cast<const char*>(gpu_crash_dump), gpu_crash_dump_size); 158 std::string_view dump_view(static_cast<const char*>(gpu_crash_dump), gpu_crash_dump_size);
159 if (FileUtil::WriteStringToFile(false, base_name, dump_view) != gpu_crash_dump_size) { 159 if (Common::FS::WriteStringToFile(false, base_name, dump_view) != gpu_crash_dump_size) {
160 LOG_ERROR(Render_Vulkan, "Failed to write dump file"); 160 LOG_ERROR(Render_Vulkan, "Failed to write dump file");
161 return; 161 return;
162 } 162 }
163 const std::string_view json_view(json.data(), json.size()); 163 const std::string_view json_view(json.data(), json.size());
164 if (FileUtil::WriteStringToFile(true, base_name + ".json", json_view) != json.size()) { 164 if (Common::FS::WriteStringToFile(true, base_name + ".json", json_view) != json.size()) {
165 LOG_ERROR(Render_Vulkan, "Failed to write JSON"); 165 LOG_ERROR(Render_Vulkan, "Failed to write JSON");
166 return; 166 return;
167 } 167 }
@@ -180,7 +180,7 @@ void NsightAftermathTracker::OnShaderDebugInfoCallback(const void* shader_debug_
180 180
181 const std::string path = 181 const std::string path =
182 fmt::format("{}/shader_{:016x}{:016x}.nvdbg", dump_dir, identifier.id[0], identifier.id[1]); 182 fmt::format("{}/shader_{:016x}{:016x}.nvdbg", dump_dir, identifier.id[0], identifier.id[1]);
183 FileUtil::IOFile file(path, "wb"); 183 Common::FS::IOFile file(path, "wb");
184 if (!file.IsOpen()) { 184 if (!file.IsOpen()) {
185 LOG_ERROR(Render_Vulkan, "Failed to create file {}", path); 185 LOG_ERROR(Render_Vulkan, "Failed to create file {}", path);
186 return; 186 return;
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 2258479f5..ae46e0444 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -78,7 +78,7 @@ Common::DynamicLibrary OpenVulkanLibrary() {
78 if (!libvulkan_env || !library.Open(libvulkan_env)) { 78 if (!libvulkan_env || !library.Open(libvulkan_env)) {
79 // Use the libvulkan.dylib from the application bundle. 79 // Use the libvulkan.dylib from the application bundle.
80 const std::string filename = 80 const std::string filename =
81 FileUtil::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib"; 81 Common::FS::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib";
82 library.Open(filename.c_str()); 82 library.Open(filename.c_str());
83 } 83 }
84#else 84#else
@@ -237,8 +237,10 @@ std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_ext
237 237
238} // Anonymous namespace 238} // Anonymous namespace
239 239
240RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system) 240RendererVulkan::RendererVulkan(Core::System& system_, Core::Frontend::EmuWindow& emu_window,
241 : RendererBase(window), system{system} {} 241 Tegra::GPU& gpu_,
242 std::unique_ptr<Core::Frontend::GraphicsContext> context)
243 : RendererBase{emu_window, std::move(context)}, system{system_}, gpu{gpu_} {}
242 244
243RendererVulkan::~RendererVulkan() { 245RendererVulkan::~RendererVulkan() {
244 ShutDown(); 246 ShutDown();
@@ -439,7 +441,7 @@ void RendererVulkan::Report() const {
439 LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version); 441 LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
440 442
441 auto& telemetry_session = system.TelemetrySession(); 443 auto& telemetry_session = system.TelemetrySession();
442 constexpr auto field = Telemetry::FieldType::UserSystem; 444 constexpr auto field = Common::Telemetry::FieldType::UserSystem;
443 telemetry_session.AddField(field, "GPU_Vendor", vendor_name); 445 telemetry_session.AddField(field, "GPU_Vendor", vendor_name);
444 telemetry_session.AddField(field, "GPU_Model", model_name); 446 telemetry_session.AddField(field, "GPU_Model", model_name);
445 telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name); 447 telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name);
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 522b5bff8..13debbbc0 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -38,7 +38,9 @@ struct VKScreenInfo {
38 38
39class RendererVulkan final : public VideoCore::RendererBase { 39class RendererVulkan final : public VideoCore::RendererBase {
40public: 40public:
41 explicit RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system); 41 explicit RendererVulkan(Core::System& system, Core::Frontend::EmuWindow& emu_window,
42 Tegra::GPU& gpu,
43 std::unique_ptr<Core::Frontend::GraphicsContext> context);
42 ~RendererVulkan() override; 44 ~RendererVulkan() override;
43 45
44 bool Init() override; 46 bool Init() override;
@@ -58,6 +60,7 @@ private:
58 void Report() const; 60 void Report() const;
59 61
60 Core::System& system; 62 Core::System& system;
63 Tegra::GPU& gpu;
61 64
62 Common::DynamicLibrary library; 65 Common::DynamicLibrary library;
63 vk::InstanceDispatch dld; 66 vk::InstanceDispatch dld;
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index 0c03e4d83..ebcfaa0e3 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -382,6 +382,8 @@ bool VKDevice::Create() {
382 382
383 graphics_queue = logical.GetQueue(graphics_family); 383 graphics_queue = logical.GetQueue(graphics_family);
384 present_queue = logical.GetQueue(present_family); 384 present_queue = logical.GetQueue(present_family);
385
386 use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue();
385 return true; 387 return true;
386} 388}
387 389
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h
index 529744f2d..26a233db1 100644
--- a/src/video_core/renderer_vulkan/vk_device.h
+++ b/src/video_core/renderer_vulkan/vk_device.h
@@ -202,6 +202,11 @@ public:
202 return reported_extensions; 202 return reported_extensions;
203 } 203 }
204 204
205 /// Returns true if the setting for async shader compilation is enabled.
206 bool UseAsynchronousShaders() const {
207 return use_asynchronous_shaders;
208 }
209
205 /// Checks if the physical device is suitable. 210 /// Checks if the physical device is suitable.
206 static bool IsSuitable(vk::PhysicalDevice physical, VkSurfaceKHR surface); 211 static bool IsSuitable(vk::PhysicalDevice physical, VkSurfaceKHR surface);
207 212
@@ -252,6 +257,9 @@ private:
252 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state. 257 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
253 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. 258 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
254 259
260 // Asynchronous Graphics Pipeline setting
261 bool use_asynchronous_shaders{}; ///< Setting to use asynchronous shaders/graphics pipeline
262
255 // Telemetry parameters 263 // Telemetry parameters
256 std::string vendor_name; ///< Device's driver name. 264 std::string vendor_name; ///< Device's driver name.
257 std::vector<std::string> reported_extensions; ///< Reported Vulkan extensions. 265 std::vector<std::string> reported_extensions; ///< Reported Vulkan extensions.
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
index a02be5487..d7f65d435 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
@@ -29,7 +29,7 @@ void InnerFence::Queue() {
29 } 29 }
30 ASSERT(!event); 30 ASSERT(!event);
31 31
32 event = device.GetLogical().CreateEvent(); 32 event = device.GetLogical().CreateNewEvent();
33 ticks = scheduler.Ticks(); 33 ticks = scheduler.Ticks();
34 34
35 scheduler.RequestOutsideRenderPassOperationContext(); 35 scheduler.RequestOutsideRenderPassOperationContext();
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index aaf930b90..2e46c6278 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -78,15 +78,14 @@ VKGraphicsPipeline::VKGraphicsPipeline(const VKDevice& device, VKScheduler& sche
78 const GraphicsPipelineCacheKey& key, 78 const GraphicsPipelineCacheKey& key,
79 vk::Span<VkDescriptorSetLayoutBinding> bindings, 79 vk::Span<VkDescriptorSetLayoutBinding> bindings,
80 const SPIRVProgram& program) 80 const SPIRVProgram& program)
81 : device{device}, scheduler{scheduler}, fixed_state{key.fixed_state}, hash{key.Hash()}, 81 : device{device}, scheduler{scheduler}, cache_key{key}, hash{cache_key.Hash()},
82 descriptor_set_layout{CreateDescriptorSetLayout(bindings)}, 82 descriptor_set_layout{CreateDescriptorSetLayout(bindings)},
83 descriptor_allocator{descriptor_pool, *descriptor_set_layout}, 83 descriptor_allocator{descriptor_pool, *descriptor_set_layout},
84 update_descriptor_queue{update_descriptor_queue}, layout{CreatePipelineLayout()}, 84 update_descriptor_queue{update_descriptor_queue}, layout{CreatePipelineLayout()},
85 descriptor_template{CreateDescriptorUpdateTemplate(program)}, modules{CreateShaderModules( 85 descriptor_template{CreateDescriptorUpdateTemplate(program)}, modules{CreateShaderModules(
86 program)}, 86 program)},
87 renderpass{renderpass_cache.GetRenderPass(key.renderpass_params)}, pipeline{CreatePipeline( 87 renderpass{renderpass_cache.GetRenderPass(cache_key.renderpass_params)},
88 key.renderpass_params, 88 pipeline{CreatePipeline(cache_key.renderpass_params, program)} {}
89 program)} {}
90 89
91VKGraphicsPipeline::~VKGraphicsPipeline() = default; 90VKGraphicsPipeline::~VKGraphicsPipeline() = default;
92 91
@@ -181,7 +180,7 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
181 180
182vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params, 181vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params,
183 const SPIRVProgram& program) const { 182 const SPIRVProgram& program) const {
184 const auto& state = fixed_state; 183 const auto& state = cache_key.fixed_state;
185 const auto& viewport_swizzles = state.viewport_swizzles; 184 const auto& viewport_swizzles = state.viewport_swizzles;
186 185
187 FixedPipelineState::DynamicState dynamic; 186 FixedPipelineState::DynamicState dynamic;
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index a1d699a6c..58aa35efd 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -19,7 +19,27 @@ namespace Vulkan {
19 19
20using Maxwell = Tegra::Engines::Maxwell3D::Regs; 20using Maxwell = Tegra::Engines::Maxwell3D::Regs;
21 21
22struct GraphicsPipelineCacheKey; 22struct GraphicsPipelineCacheKey {
23 RenderPassParams renderpass_params;
24 u32 padding;
25 std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
26 FixedPipelineState fixed_state;
27
28 std::size_t Hash() const noexcept;
29
30 bool operator==(const GraphicsPipelineCacheKey& rhs) const noexcept;
31
32 bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
33 return !operator==(rhs);
34 }
35
36 std::size_t Size() const noexcept {
37 return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size();
38 }
39};
40static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
41static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
42static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>);
23 43
24class VKDescriptorPool; 44class VKDescriptorPool;
25class VKDevice; 45class VKDevice;
@@ -54,6 +74,10 @@ public:
54 return renderpass; 74 return renderpass;
55 } 75 }
56 76
77 GraphicsPipelineCacheKey GetCacheKey() const {
78 return cache_key;
79 }
80
57private: 81private:
58 vk::DescriptorSetLayout CreateDescriptorSetLayout( 82 vk::DescriptorSetLayout CreateDescriptorSetLayout(
59 vk::Span<VkDescriptorSetLayoutBinding> bindings) const; 83 vk::Span<VkDescriptorSetLayoutBinding> bindings) const;
@@ -70,7 +94,7 @@ private:
70 94
71 const VKDevice& device; 95 const VKDevice& device;
72 VKScheduler& scheduler; 96 VKScheduler& scheduler;
73 const FixedPipelineState fixed_state; 97 const GraphicsPipelineCacheKey cache_key;
74 const u64 hash; 98 const u64 hash;
75 99
76 vk::DescriptorSetLayout descriptor_set_layout; 100 vk::DescriptorSetLayout descriptor_set_layout;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 418c62bc4..cfdcdd6ab 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -28,6 +28,7 @@
28#include "video_core/shader/compiler_settings.h" 28#include "video_core/shader/compiler_settings.h"
29#include "video_core/shader/memory_util.h" 29#include "video_core/shader/memory_util.h"
30#include "video_core/shader_cache.h" 30#include "video_core/shader_cache.h"
31#include "video_core/shader_notify.h"
31 32
32namespace Vulkan { 33namespace Vulkan {
33 34
@@ -205,24 +206,43 @@ std::array<Shader*, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
205 return last_shaders = shaders; 206 return last_shaders = shaders;
206} 207}
207 208
208VKGraphicsPipeline& VKPipelineCache::GetGraphicsPipeline(const GraphicsPipelineCacheKey& key) { 209VKGraphicsPipeline* VKPipelineCache::GetGraphicsPipeline(
210 const GraphicsPipelineCacheKey& key, VideoCommon::Shader::AsyncShaders& async_shaders) {
209 MICROPROFILE_SCOPE(Vulkan_PipelineCache); 211 MICROPROFILE_SCOPE(Vulkan_PipelineCache);
210 212
211 if (last_graphics_pipeline && last_graphics_key == key) { 213 if (last_graphics_pipeline && last_graphics_key == key) {
212 return *last_graphics_pipeline; 214 return last_graphics_pipeline;
213 } 215 }
214 last_graphics_key = key; 216 last_graphics_key = key;
215 217
218 if (device.UseAsynchronousShaders() && async_shaders.IsShaderAsync(system.GPU())) {
219 std::unique_lock lock{pipeline_cache};
220 const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key);
221 if (is_cache_miss) {
222 system.GPU().ShaderNotify().MarkSharderBuilding();
223 LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash());
224 const auto [program, bindings] = DecompileShaders(key.fixed_state);
225 async_shaders.QueueVulkanShader(this, device, scheduler, descriptor_pool,
226 update_descriptor_queue, renderpass_cache, bindings,
227 program, key);
228 }
229 last_graphics_pipeline = pair->second.get();
230 return last_graphics_pipeline;
231 }
232
216 const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key); 233 const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key);
217 auto& entry = pair->second; 234 auto& entry = pair->second;
218 if (is_cache_miss) { 235 if (is_cache_miss) {
236 system.GPU().ShaderNotify().MarkSharderBuilding();
219 LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash()); 237 LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash());
220 const auto [program, bindings] = DecompileShaders(key); 238 const auto [program, bindings] = DecompileShaders(key.fixed_state);
221 entry = std::make_unique<VKGraphicsPipeline>(device, scheduler, descriptor_pool, 239 entry = std::make_unique<VKGraphicsPipeline>(device, scheduler, descriptor_pool,
222 update_descriptor_queue, renderpass_cache, key, 240 update_descriptor_queue, renderpass_cache, key,
223 bindings, program); 241 bindings, program);
242 system.GPU().ShaderNotify().MarkShaderComplete();
224 } 243 }
225 return *(last_graphics_pipeline = entry.get()); 244 last_graphics_pipeline = entry.get();
245 return last_graphics_pipeline;
226} 246}
227 247
228VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCacheKey& key) { 248VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCacheKey& key) {
@@ -277,6 +297,12 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
277 return *entry; 297 return *entry;
278} 298}
279 299
300void VKPipelineCache::EmplacePipeline(std::unique_ptr<VKGraphicsPipeline> pipeline) {
301 system.GPU().ShaderNotify().MarkShaderComplete();
302 std::unique_lock lock{pipeline_cache};
303 graphics_cache.at(pipeline->GetCacheKey()) = std::move(pipeline);
304}
305
280void VKPipelineCache::OnShaderRemoval(Shader* shader) { 306void VKPipelineCache::OnShaderRemoval(Shader* shader) {
281 bool finished = false; 307 bool finished = false;
282 const auto Finish = [&] { 308 const auto Finish = [&] {
@@ -312,8 +338,7 @@ void VKPipelineCache::OnShaderRemoval(Shader* shader) {
312} 338}
313 339
314std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> 340std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>>
315VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) { 341VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) {
316 const auto& fixed_state = key.fixed_state;
317 auto& memory_manager = system.GPU().MemoryManager(); 342 auto& memory_manager = system.GPU().MemoryManager();
318 const auto& gpu = system.GPU().Maxwell3D(); 343 const auto& gpu = system.GPU().Maxwell3D();
319 344
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 0a3fe65fb..c04829e77 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -22,6 +22,7 @@
22#include "video_core/renderer_vulkan/vk_renderpass_cache.h" 22#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
23#include "video_core/renderer_vulkan/vk_shader_decompiler.h" 23#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
24#include "video_core/renderer_vulkan/wrapper.h" 24#include "video_core/renderer_vulkan/wrapper.h"
25#include "video_core/shader/async_shaders.h"
25#include "video_core/shader/memory_util.h" 26#include "video_core/shader/memory_util.h"
26#include "video_core/shader/registry.h" 27#include "video_core/shader/registry.h"
27#include "video_core/shader/shader_ir.h" 28#include "video_core/shader/shader_ir.h"
@@ -43,28 +44,6 @@ class VKUpdateDescriptorQueue;
43 44
44using Maxwell = Tegra::Engines::Maxwell3D::Regs; 45using Maxwell = Tegra::Engines::Maxwell3D::Regs;
45 46
46struct GraphicsPipelineCacheKey {
47 RenderPassParams renderpass_params;
48 u32 padding;
49 std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
50 FixedPipelineState fixed_state;
51
52 std::size_t Hash() const noexcept;
53
54 bool operator==(const GraphicsPipelineCacheKey& rhs) const noexcept;
55
56 bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
57 return !operator==(rhs);
58 }
59
60 std::size_t Size() const noexcept {
61 return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size();
62 }
63};
64static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
65static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
66static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>);
67
68struct ComputePipelineCacheKey { 47struct ComputePipelineCacheKey {
69 GPUVAddr shader; 48 GPUVAddr shader;
70 u32 shared_memory_size; 49 u32 shared_memory_size;
@@ -152,16 +131,19 @@ public:
152 131
153 std::array<Shader*, Maxwell::MaxShaderProgram> GetShaders(); 132 std::array<Shader*, Maxwell::MaxShaderProgram> GetShaders();
154 133
155 VKGraphicsPipeline& GetGraphicsPipeline(const GraphicsPipelineCacheKey& key); 134 VKGraphicsPipeline* GetGraphicsPipeline(const GraphicsPipelineCacheKey& key,
135 VideoCommon::Shader::AsyncShaders& async_shaders);
156 136
157 VKComputePipeline& GetComputePipeline(const ComputePipelineCacheKey& key); 137 VKComputePipeline& GetComputePipeline(const ComputePipelineCacheKey& key);
158 138
139 void EmplacePipeline(std::unique_ptr<VKGraphicsPipeline> pipeline);
140
159protected: 141protected:
160 void OnShaderRemoval(Shader* shader) final; 142 void OnShaderRemoval(Shader* shader) final;
161 143
162private: 144private:
163 std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> DecompileShaders( 145 std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> DecompileShaders(
164 const GraphicsPipelineCacheKey& key); 146 const FixedPipelineState& fixed_state);
165 147
166 Core::System& system; 148 Core::System& system;
167 const VKDevice& device; 149 const VKDevice& device;
@@ -178,6 +160,7 @@ private:
178 GraphicsPipelineCacheKey last_graphics_key; 160 GraphicsPipelineCacheKey last_graphics_key;
179 VKGraphicsPipeline* last_graphics_pipeline = nullptr; 161 VKGraphicsPipeline* last_graphics_pipeline = nullptr;
180 162
163 std::mutex pipeline_cache;
181 std::unordered_map<GraphicsPipelineCacheKey, std::unique_ptr<VKGraphicsPipeline>> 164 std::unordered_map<GraphicsPipelineCacheKey, std::unique_ptr<VKGraphicsPipeline>>
182 graphics_cache; 165 graphics_cache;
183 std::unordered_map<ComputePipelineCacheKey, std::unique_ptr<VKComputePipeline>> compute_cache; 166 std::unordered_map<ComputePipelineCacheKey, std::unique_ptr<VKComputePipeline>> compute_cache;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 7500e8244..ff1b52eab 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -14,6 +14,7 @@
14#include "common/assert.h" 14#include "common/assert.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/microprofile.h" 16#include "common/microprofile.h"
17#include "common/scope_exit.h"
17#include "core/core.h" 18#include "core/core.h"
18#include "core/settings.h" 19#include "core/settings.h"
19#include "video_core/engines/kepler_compute.h" 20#include "video_core/engines/kepler_compute.h"
@@ -400,8 +401,12 @@ RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWind
400 buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool), 401 buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool),
401 sampler_cache(device), 402 sampler_cache(device),
402 fence_manager(system, *this, device, scheduler, texture_cache, buffer_cache, query_cache), 403 fence_manager(system, *this, device, scheduler, texture_cache, buffer_cache, query_cache),
403 query_cache(system, *this, device, scheduler), wfi_event{device.GetLogical().CreateEvent()} { 404 query_cache(system, *this, device, scheduler),
405 wfi_event{device.GetLogical().CreateNewEvent()}, async_shaders{renderer} {
404 scheduler.SetQueryCache(query_cache); 406 scheduler.SetQueryCache(query_cache);
407 if (device.UseAsynchronousShaders()) {
408 async_shaders.AllocateWorkers();
409 }
405} 410}
406 411
407RasterizerVulkan::~RasterizerVulkan() = default; 412RasterizerVulkan::~RasterizerVulkan() = default;
@@ -413,6 +418,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
413 418
414 query_cache.UpdateCounters(); 419 query_cache.UpdateCounters();
415 420
421 SCOPE_EXIT({ system.GPU().TickWork(); });
422
416 const auto& gpu = system.GPU().Maxwell3D(); 423 const auto& gpu = system.GPU().Maxwell3D();
417 GraphicsPipelineCacheKey key; 424 GraphicsPipelineCacheKey key;
418 key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported()); 425 key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported());
@@ -439,10 +446,15 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
439 key.renderpass_params = GetRenderPassParams(texceptions); 446 key.renderpass_params = GetRenderPassParams(texceptions);
440 key.padding = 0; 447 key.padding = 0;
441 448
442 auto& pipeline = pipeline_cache.GetGraphicsPipeline(key); 449 auto* pipeline = pipeline_cache.GetGraphicsPipeline(key, async_shaders);
443 scheduler.BindGraphicsPipeline(pipeline.GetHandle()); 450 if (pipeline == nullptr || pipeline->GetHandle() == VK_NULL_HANDLE) {
451 // Async graphics pipeline was not ready.
452 return;
453 }
454
455 scheduler.BindGraphicsPipeline(pipeline->GetHandle());
444 456
445 const auto renderpass = pipeline.GetRenderPass(); 457 const auto renderpass = pipeline->GetRenderPass();
446 const auto [framebuffer, render_area] = ConfigureFramebuffers(renderpass); 458 const auto [framebuffer, render_area] = ConfigureFramebuffers(renderpass);
447 scheduler.RequestRenderpass(renderpass, framebuffer, render_area); 459 scheduler.RequestRenderpass(renderpass, framebuffer, render_area);
448 460
@@ -452,8 +464,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
452 464
453 BeginTransformFeedback(); 465 BeginTransformFeedback();
454 466
455 const auto pipeline_layout = pipeline.GetLayout(); 467 const auto pipeline_layout = pipeline->GetLayout();
456 const auto descriptor_set = pipeline.CommitDescriptorSet(); 468 const auto descriptor_set = pipeline->CommitDescriptorSet();
457 scheduler.Record([pipeline_layout, descriptor_set, draw_params](vk::CommandBuffer cmdbuf) { 469 scheduler.Record([pipeline_layout, descriptor_set, draw_params](vk::CommandBuffer cmdbuf) {
458 if (descriptor_set) { 470 if (descriptor_set) {
459 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 471 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
@@ -463,8 +475,6 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
463 }); 475 });
464 476
465 EndTransformFeedback(); 477 EndTransformFeedback();
466
467 system.GPU().TickWork();
468} 478}
469 479
470void RasterizerVulkan::Clear() { 480void RasterizerVulkan::Clear() {
@@ -1433,10 +1443,10 @@ void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
1433} 1443}
1434 1444
1435void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) { 1445void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) {
1436 if (!state_tracker.TouchPrimitiveTopology()) { 1446 const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
1447 if (!state_tracker.ChangePrimitiveTopology(primitive_topology)) {
1437 return; 1448 return;
1438 } 1449 }
1439 const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
1440 scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) { 1450 scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) {
1441 cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology)); 1451 cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology));
1442 }); 1452 });
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 923178b0b..f640ba649 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -32,6 +32,7 @@
32#include "video_core/renderer_vulkan/vk_texture_cache.h" 32#include "video_core/renderer_vulkan/vk_texture_cache.h"
33#include "video_core/renderer_vulkan/vk_update_descriptor.h" 33#include "video_core/renderer_vulkan/vk_update_descriptor.h"
34#include "video_core/renderer_vulkan/wrapper.h" 34#include "video_core/renderer_vulkan/wrapper.h"
35#include "video_core/shader/async_shaders.h"
35 36
36namespace Core { 37namespace Core {
37class System; 38class System;
@@ -136,6 +137,14 @@ public:
136 u32 pixel_stride) override; 137 u32 pixel_stride) override;
137 void SetupDirtyFlags() override; 138 void SetupDirtyFlags() override;
138 139
140 VideoCommon::Shader::AsyncShaders& GetAsyncShaders() {
141 return async_shaders;
142 }
143
144 const VideoCommon::Shader::AsyncShaders& GetAsyncShaders() const {
145 return async_shaders;
146 }
147
139 /// Maximum supported size that a constbuffer can have in bytes. 148 /// Maximum supported size that a constbuffer can have in bytes.
140 static constexpr std::size_t MaxConstbufferSize = 0x10000; 149 static constexpr std::size_t MaxConstbufferSize = 0x10000;
141 static_assert(MaxConstbufferSize % (4 * sizeof(float)) == 0, 150 static_assert(MaxConstbufferSize % (4 * sizeof(float)) == 0,
@@ -297,6 +306,7 @@ private:
297 vk::Buffer default_buffer; 306 vk::Buffer default_buffer;
298 VKMemoryCommit default_buffer_commit; 307 VKMemoryCommit default_buffer_commit;
299 vk::Event wfi_event; 308 vk::Event wfi_event;
309 VideoCommon::Shader::AsyncShaders async_shaders;
300 310
301 std::array<View, Maxwell::NumRenderTargets> color_attachments; 311 std::array<View, Maxwell::NumRenderTargets> color_attachments;
302 View zeta_attachment; 312 View zeta_attachment;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 9151d9fb1..4bd1009f9 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -42,7 +42,6 @@ Flags MakeInvalidationFlags() {
42 flags[DepthWriteEnable] = true; 42 flags[DepthWriteEnable] = true;
43 flags[DepthCompareOp] = true; 43 flags[DepthCompareOp] = true;
44 flags[FrontFace] = true; 44 flags[FrontFace] = true;
45 flags[PrimitiveTopology] = true;
46 flags[StencilOp] = true; 45 flags[StencilOp] = true;
47 flags[StencilTestEnable] = true; 46 flags[StencilTestEnable] = true;
48 return flags; 47 return flags;
@@ -112,10 +111,6 @@ void SetupDirtyFrontFace(Tables& tables) {
112 table[OFF(screen_y_control)] = FrontFace; 111 table[OFF(screen_y_control)] = FrontFace;
113} 112}
114 113
115void SetupDirtyPrimitiveTopology(Tables& tables) {
116 tables[0][OFF(draw.topology)] = PrimitiveTopology;
117}
118
119void SetupDirtyStencilOp(Tables& tables) { 114void SetupDirtyStencilOp(Tables& tables) {
120 auto& table = tables[0]; 115 auto& table = tables[0];
121 table[OFF(stencil_front_op_fail)] = StencilOp; 116 table[OFF(stencil_front_op_fail)] = StencilOp;
@@ -156,13 +151,13 @@ void StateTracker::Initialize() {
156 SetupDirtyDepthWriteEnable(tables); 151 SetupDirtyDepthWriteEnable(tables);
157 SetupDirtyDepthCompareOp(tables); 152 SetupDirtyDepthCompareOp(tables);
158 SetupDirtyFrontFace(tables); 153 SetupDirtyFrontFace(tables);
159 SetupDirtyPrimitiveTopology(tables);
160 SetupDirtyStencilOp(tables); 154 SetupDirtyStencilOp(tables);
161 SetupDirtyStencilTestEnable(tables); 155 SetupDirtyStencilTestEnable(tables);
162} 156}
163 157
164void StateTracker::InvalidateCommandBufferState() { 158void StateTracker::InvalidateCommandBufferState() {
165 system.GPU().Maxwell3D().dirty.flags |= invalidation_flags; 159 system.GPU().Maxwell3D().dirty.flags |= invalidation_flags;
160 current_topology = INVALID_TOPOLOGY;
166} 161}
167 162
168} // namespace Vulkan 163} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 54ca0d6c6..13a6ce786 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -32,7 +32,6 @@ enum : u8 {
32 DepthWriteEnable, 32 DepthWriteEnable,
33 DepthCompareOp, 33 DepthCompareOp,
34 FrontFace, 34 FrontFace,
35 PrimitiveTopology,
36 StencilOp, 35 StencilOp,
37 StencilTestEnable, 36 StencilTestEnable,
38 37
@@ -43,6 +42,8 @@ static_assert(Last <= std::numeric_limits<u8>::max());
43} // namespace Dirty 42} // namespace Dirty
44 43
45class StateTracker { 44class StateTracker {
45 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
46
46public: 47public:
47 explicit StateTracker(Core::System& system); 48 explicit StateTracker(Core::System& system);
48 49
@@ -102,10 +103,6 @@ public:
102 return Exchange(Dirty::FrontFace, false); 103 return Exchange(Dirty::FrontFace, false);
103 } 104 }
104 105
105 bool TouchPrimitiveTopology() {
106 return Exchange(Dirty::PrimitiveTopology, false);
107 }
108
109 bool TouchStencilOp() { 106 bool TouchStencilOp() {
110 return Exchange(Dirty::StencilOp, false); 107 return Exchange(Dirty::StencilOp, false);
111 } 108 }
@@ -114,7 +111,15 @@ public:
114 return Exchange(Dirty::StencilTestEnable, false); 111 return Exchange(Dirty::StencilTestEnable, false);
115 } 112 }
116 113
114 bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) {
115 const bool has_changed = current_topology != new_topology;
116 current_topology = new_topology;
117 return has_changed;
118 }
119
117private: 120private:
121 static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u);
122
118 bool Exchange(std::size_t id, bool new_value) const noexcept { 123 bool Exchange(std::size_t id, bool new_value) const noexcept {
119 auto& flags = system.GPU().Maxwell3D().dirty.flags; 124 auto& flags = system.GPU().Maxwell3D().dirty.flags;
120 const bool is_dirty = flags[id]; 125 const bool is_dirty = flags[id];
@@ -124,6 +129,7 @@ private:
124 129
125 Core::System& system; 130 Core::System& system;
126 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags; 131 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
132 Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY;
127}; 133};
128 134
129} // namespace Vulkan 135} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index 14cac38ea..013865aa4 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/renderer_vulkan/wrapper.cpp
@@ -644,7 +644,7 @@ ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) cons
644 return ShaderModule(object, handle, *dld); 644 return ShaderModule(object, handle, *dld);
645} 645}
646 646
647Event Device::CreateEvent() const { 647Event Device::CreateNewEvent() const {
648 static constexpr VkEventCreateInfo ci{ 648 static constexpr VkEventCreateInfo ci{
649 .sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO, 649 .sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO,
650 .pNext = nullptr, 650 .pNext = nullptr,
@@ -786,7 +786,7 @@ std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProp
786 VK_SUCCESS) { 786 VK_SUCCESS) {
787 return std::nullopt; 787 return std::nullopt;
788 } 788 }
789 return properties; 789 return std::move(properties);
790} 790}
791 791
792std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties( 792std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties(
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h
index 31885ef42..b9d3fedc1 100644
--- a/src/video_core/renderer_vulkan/wrapper.h
+++ b/src/video_core/renderer_vulkan/wrapper.h
@@ -721,7 +721,7 @@ public:
721 721
722 ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const; 722 ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
723 723
724 Event CreateEvent() const; 724 Event CreateNewEvent() const;
725 725
726 SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const; 726 SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
727 727
diff --git a/src/video_core/shader/async_shaders.cpp b/src/video_core/shader/async_shaders.cpp
index b7f66d7ee..f815584f7 100644
--- a/src/video_core/shader/async_shaders.cpp
+++ b/src/video_core/shader/async_shaders.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono>
6#include <condition_variable> 5#include <condition_variable>
7#include <mutex> 6#include <mutex>
8#include <thread> 7#include <thread>
@@ -20,9 +19,18 @@ AsyncShaders::~AsyncShaders() {
20 KillWorkers(); 19 KillWorkers();
21} 20}
22 21
23void AsyncShaders::AllocateWorkers(std::size_t num_workers) { 22void AsyncShaders::AllocateWorkers() {
24 // If we're already have workers queued or don't want to queue workers, ignore 23 // Max worker threads we should allow
25 if (num_workers == worker_threads.size() || num_workers == 0) { 24 constexpr u32 MAX_THREADS = 4;
25 // Deduce how many threads we can use
26 const u32 threads_used = std::thread::hardware_concurrency() / 4;
27 // Always allow at least 1 thread regardless of our settings
28 const auto max_worker_count = std::max(1U, threads_used);
29 // Don't use more than MAX_THREADS
30 const auto num_workers = std::min(max_worker_count, MAX_THREADS);
31
32 // If we already have workers queued, ignore
33 if (num_workers == worker_threads.size()) {
26 return; 34 return;
27 } 35 }
28 36
@@ -34,8 +42,8 @@ void AsyncShaders::AllocateWorkers(std::size_t num_workers) {
34 // Create workers 42 // Create workers
35 for (std::size_t i = 0; i < num_workers; i++) { 43 for (std::size_t i = 0; i < num_workers; i++) {
36 context_list.push_back(emu_window.CreateSharedContext()); 44 context_list.push_back(emu_window.CreateSharedContext());
37 worker_threads.push_back(std::move( 45 worker_threads.push_back(
38 std::thread(&AsyncShaders::ShaderCompilerThread, this, context_list[i].get()))); 46 std::thread(&AsyncShaders::ShaderCompilerThread, this, context_list[i].get()));
39 } 47 }
40} 48}
41 49
@@ -111,24 +119,50 @@ void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
111 VideoCommon::Shader::CompilerSettings compiler_settings, 119 VideoCommon::Shader::CompilerSettings compiler_settings,
112 const VideoCommon::Shader::Registry& registry, 120 const VideoCommon::Shader::Registry& registry,
113 VAddr cpu_addr) { 121 VAddr cpu_addr) {
114 WorkerParams params{device.UseAssemblyShaders() ? AsyncShaders::Backend::GLASM 122 WorkerParams params{
115 : AsyncShaders::Backend::OpenGL, 123 .backend = device.UseAssemblyShaders() ? Backend::GLASM : Backend::OpenGL,
116 device, 124 .device = &device,
117 shader_type, 125 .shader_type = shader_type,
118 uid, 126 .uid = uid,
119 std::move(code), 127 .code = std::move(code),
120 std::move(code_b), 128 .code_b = std::move(code_b),
121 main_offset, 129 .main_offset = main_offset,
122 compiler_settings, 130 .compiler_settings = compiler_settings,
123 registry, 131 .registry = registry,
124 cpu_addr}; 132 .cpu_address = cpu_addr,
133 };
125 std::unique_lock lock(queue_mutex); 134 std::unique_lock lock(queue_mutex);
126 pending_queue.push_back(std::move(params)); 135 pending_queue.push(std::move(params));
136 cv.notify_one();
137}
138
139void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
140 const Vulkan::VKDevice& device, Vulkan::VKScheduler& scheduler,
141 Vulkan::VKDescriptorPool& descriptor_pool,
142 Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue,
143 Vulkan::VKRenderPassCache& renderpass_cache,
144 std::vector<VkDescriptorSetLayoutBinding> bindings,
145 Vulkan::SPIRVProgram program,
146 Vulkan::GraphicsPipelineCacheKey key) {
147 WorkerParams params{
148 .backend = Backend::Vulkan,
149 .pp_cache = pp_cache,
150 .vk_device = &device,
151 .scheduler = &scheduler,
152 .descriptor_pool = &descriptor_pool,
153 .update_descriptor_queue = &update_descriptor_queue,
154 .renderpass_cache = &renderpass_cache,
155 .bindings = bindings,
156 .program = program,
157 .key = key,
158 };
159
160 std::unique_lock lock(queue_mutex);
161 pending_queue.push(std::move(params));
127 cv.notify_one(); 162 cv.notify_one();
128} 163}
129 164
130void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context) { 165void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context) {
131 using namespace std::chrono_literals;
132 while (!is_thread_exiting.load(std::memory_order_relaxed)) { 166 while (!is_thread_exiting.load(std::memory_order_relaxed)) {
133 std::unique_lock lock{queue_mutex}; 167 std::unique_lock lock{queue_mutex};
134 cv.wait(lock, [this] { return HasWorkQueued() || is_thread_exiting; }); 168 cv.wait(lock, [this] { return HasWorkQueued() || is_thread_exiting; });
@@ -144,18 +178,17 @@ void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context
144 if (pending_queue.empty()) { 178 if (pending_queue.empty()) {
145 continue; 179 continue;
146 } 180 }
181
147 // Pull work from queue 182 // Pull work from queue
148 WorkerParams work = std::move(pending_queue.front()); 183 WorkerParams work = std::move(pending_queue.front());
149 pending_queue.pop_front(); 184 pending_queue.pop();
150
151 lock.unlock(); 185 lock.unlock();
152 186
153 if (work.backend == AsyncShaders::Backend::OpenGL || 187 if (work.backend == Backend::OpenGL || work.backend == Backend::GLASM) {
154 work.backend == AsyncShaders::Backend::GLASM) { 188 const ShaderIR ir(work.code, work.main_offset, work.compiler_settings, *work.registry);
155 const ShaderIR ir(work.code, work.main_offset, work.compiler_settings, work.registry);
156 const auto scope = context->Acquire(); 189 const auto scope = context->Acquire();
157 auto program = 190 auto program =
158 OpenGL::BuildShader(work.device, work.shader_type, work.uid, ir, work.registry); 191 OpenGL::BuildShader(*work.device, work.shader_type, work.uid, ir, *work.registry);
159 Result result{}; 192 Result result{};
160 result.backend = work.backend; 193 result.backend = work.backend;
161 result.cpu_address = work.cpu_address; 194 result.cpu_address = work.cpu_address;
@@ -164,9 +197,9 @@ void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context
164 result.code_b = std::move(work.code_b); 197 result.code_b = std::move(work.code_b);
165 result.shader_type = work.shader_type; 198 result.shader_type = work.shader_type;
166 199
167 if (work.backend == AsyncShaders::Backend::OpenGL) { 200 if (work.backend == Backend::OpenGL) {
168 result.program.opengl = std::move(program->source_program); 201 result.program.opengl = std::move(program->source_program);
169 } else if (work.backend == AsyncShaders::Backend::GLASM) { 202 } else if (work.backend == Backend::GLASM) {
170 result.program.glasm = std::move(program->assembly_program); 203 result.program.glasm = std::move(program->assembly_program);
171 } 204 }
172 205
@@ -174,6 +207,13 @@ void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context
174 std::unique_lock complete_lock(completed_mutex); 207 std::unique_lock complete_lock(completed_mutex);
175 finished_work.push_back(std::move(result)); 208 finished_work.push_back(std::move(result));
176 } 209 }
210 } else if (work.backend == Backend::Vulkan) {
211 auto pipeline = std::make_unique<Vulkan::VKGraphicsPipeline>(
212 *work.vk_device, *work.scheduler, *work.descriptor_pool,
213 *work.update_descriptor_queue, *work.renderpass_cache, work.key, work.bindings,
214 work.program);
215
216 work.pp_cache->EmplacePipeline(std::move(pipeline));
177 } 217 }
178 } 218 }
179} 219}
diff --git a/src/video_core/shader/async_shaders.h b/src/video_core/shader/async_shaders.h
index 2f5ee94ad..d5ae814d5 100644
--- a/src/video_core/shader/async_shaders.h
+++ b/src/video_core/shader/async_shaders.h
@@ -14,6 +14,10 @@
14#include "video_core/renderer_opengl/gl_device.h" 14#include "video_core/renderer_opengl/gl_device.h"
15#include "video_core/renderer_opengl/gl_resource_manager.h" 15#include "video_core/renderer_opengl/gl_resource_manager.h"
16#include "video_core/renderer_opengl/gl_shader_decompiler.h" 16#include "video_core/renderer_opengl/gl_shader_decompiler.h"
17#include "video_core/renderer_vulkan/vk_device.h"
18#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
19#include "video_core/renderer_vulkan/vk_scheduler.h"
20#include "video_core/renderer_vulkan/vk_update_descriptor.h"
17 21
18namespace Core::Frontend { 22namespace Core::Frontend {
19class EmuWindow; 23class EmuWindow;
@@ -24,6 +28,10 @@ namespace Tegra {
24class GPU; 28class GPU;
25} 29}
26 30
31namespace Vulkan {
32class VKPipelineCache;
33}
34
27namespace VideoCommon::Shader { 35namespace VideoCommon::Shader {
28 36
29class AsyncShaders { 37class AsyncShaders {
@@ -31,6 +39,7 @@ public:
31 enum class Backend { 39 enum class Backend {
32 OpenGL, 40 OpenGL,
33 GLASM, 41 GLASM,
42 Vulkan,
34 }; 43 };
35 44
36 struct ResultPrograms { 45 struct ResultPrograms {
@@ -52,7 +61,7 @@ public:
52 ~AsyncShaders(); 61 ~AsyncShaders();
53 62
54 /// Start up shader worker threads 63 /// Start up shader worker threads
55 void AllocateWorkers(std::size_t num_workers); 64 void AllocateWorkers();
56 65
57 /// Clear the shader queue and kill all worker threads 66 /// Clear the shader queue and kill all worker threads
58 void FreeWorkers(); 67 void FreeWorkers();
@@ -76,6 +85,14 @@ public:
76 VideoCommon::Shader::CompilerSettings compiler_settings, 85 VideoCommon::Shader::CompilerSettings compiler_settings,
77 const VideoCommon::Shader::Registry& registry, VAddr cpu_addr); 86 const VideoCommon::Shader::Registry& registry, VAddr cpu_addr);
78 87
88 void QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, const Vulkan::VKDevice& device,
89 Vulkan::VKScheduler& scheduler,
90 Vulkan::VKDescriptorPool& descriptor_pool,
91 Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue,
92 Vulkan::VKRenderPassCache& renderpass_cache,
93 std::vector<VkDescriptorSetLayoutBinding> bindings,
94 Vulkan::SPIRVProgram program, Vulkan::GraphicsPipelineCacheKey key);
95
79private: 96private:
80 void ShaderCompilerThread(Core::Frontend::GraphicsContext* context); 97 void ShaderCompilerThread(Core::Frontend::GraphicsContext* context);
81 98
@@ -83,16 +100,28 @@ private:
83 bool HasWorkQueued(); 100 bool HasWorkQueued();
84 101
85 struct WorkerParams { 102 struct WorkerParams {
86 AsyncShaders::Backend backend; 103 Backend backend;
87 OpenGL::Device device; 104 // For OGL
105 const OpenGL::Device* device;
88 Tegra::Engines::ShaderType shader_type; 106 Tegra::Engines::ShaderType shader_type;
89 u64 uid; 107 u64 uid;
90 std::vector<u64> code; 108 std::vector<u64> code;
91 std::vector<u64> code_b; 109 std::vector<u64> code_b;
92 u32 main_offset; 110 u32 main_offset;
93 VideoCommon::Shader::CompilerSettings compiler_settings; 111 VideoCommon::Shader::CompilerSettings compiler_settings;
94 VideoCommon::Shader::Registry registry; 112 std::optional<VideoCommon::Shader::Registry> registry;
95 VAddr cpu_address; 113 VAddr cpu_address;
114
115 // For Vulkan
116 Vulkan::VKPipelineCache* pp_cache;
117 const Vulkan::VKDevice* vk_device;
118 Vulkan::VKScheduler* scheduler;
119 Vulkan::VKDescriptorPool* descriptor_pool;
120 Vulkan::VKUpdateDescriptorQueue* update_descriptor_queue;
121 Vulkan::VKRenderPassCache* renderpass_cache;
122 std::vector<VkDescriptorSetLayoutBinding> bindings;
123 Vulkan::SPIRVProgram program;
124 Vulkan::GraphicsPipelineCacheKey key;
96 }; 125 };
97 126
98 std::condition_variable cv; 127 std::condition_variable cv;
@@ -101,7 +130,7 @@ private:
101 std::atomic<bool> is_thread_exiting{}; 130 std::atomic<bool> is_thread_exiting{};
102 std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> context_list; 131 std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> context_list;
103 std::vector<std::thread> worker_threads; 132 std::vector<std::thread> worker_threads;
104 std::deque<WorkerParams> pending_queue; 133 std::queue<WorkerParams> pending_queue;
105 std::vector<AsyncShaders::Result> finished_work; 134 std::vector<AsyncShaders::Result> finished_work;
106 Core::Frontend::EmuWindow& emu_window; 135 Core::Frontend::EmuWindow& emu_window;
107}; 136};
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 474ae620a..16d46a018 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -228,24 +228,30 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
228 } 228 }
229} 229}
230 230
231void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, 231void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel,
232 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, 232 u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) {
233 u32 block_height_bit, u32 offset_x, u32 offset_y) { 233 const u32 stride = width * bytes_per_pixel;
234 const u32 block_height = 1U << block_height_bit; 234 const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
235 for (u32 line = 0; line < subrect_height; ++line) { 235 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height);
236 const u32 y2 = line + offset_y; 236
237 const u32 gob_address_y = (y2 / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height + 237 const u32 block_height_mask = (1U << block_height) - 1;
238 ((y2 % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE; 238 const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height;
239 const auto& table = LEGACY_SWIZZLE_TABLE[y2 % GOB_SIZE_Y]; 239
240 for (u32 x = 0; x < subrect_width; ++x) { 240 for (u32 line = 0; line < line_count; ++line) {
241 const u32 x2 = (x + offset_x) * bytes_per_pixel; 241 const u32 src_y = line + origin_y;
242 const u32 gob_address = gob_address_y + (x2 / GOB_SIZE_X) * GOB_SIZE * block_height; 242 const auto& table = LEGACY_SWIZZLE_TABLE[src_y % GOB_SIZE_Y];
243 const u32 swizzled_offset = gob_address + table[x2 % GOB_SIZE_X]; 243
244 const u32 unswizzled_offset = line * dest_pitch + x * bytes_per_pixel; 244 const u32 block_y = src_y >> GOB_SIZE_Y_SHIFT;
245 u8* dest_line = unswizzled_data + unswizzled_offset; 245 const u32 src_offset_y = (block_y >> block_height) * block_size +
246 u8* source_addr = swizzled_data + swizzled_offset; 246 ((block_y & block_height_mask) << GOB_SIZE_SHIFT);
247 for (u32 column = 0; column < line_length_in; ++column) {
248 const u32 src_x = (column + origin_x) * bytes_per_pixel;
249 const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift;
250
251 const u32 swizzled_offset = src_offset_y + src_offset_x + table[src_x % GOB_SIZE_X];
252 const u32 unswizzled_offset = line * pitch + column * bytes_per_pixel;
247 253
248 std::memcpy(dest_line, source_addr, bytes_per_pixel); 254 std::memcpy(output + unswizzled_offset, input + swizzled_offset, bytes_per_pixel);
249 } 255 }
250 } 256 }
251} 257}
@@ -261,7 +267,7 @@ void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 widt
261 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); 267 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
262 268
263 const u32 block_height_mask = (1U << block_height) - 1; 269 const u32 block_height_mask = (1U << block_height) - 1;
264 const u32 x_shift = Common::CountTrailingZeroes32(GOB_SIZE << (block_height + block_depth)); 270 const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height + block_depth;
265 271
266 for (u32 line = 0; line < line_count; ++line) { 272 for (u32 line = 0; line < line_count; ++line) {
267 const auto& table = LEGACY_SWIZZLE_TABLE[line % GOB_SIZE_Y]; 273 const auto& table = LEGACY_SWIZZLE_TABLE[line % GOB_SIZE_Y];
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index d6fe35d37..01e156bc8 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -48,9 +48,8 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
48 u32 block_height_bit, u32 offset_x, u32 offset_y); 48 u32 block_height_bit, u32 offset_x, u32 offset_y);
49 49
50/// Copies a tiled subrectangle into a linear surface. 50/// Copies a tiled subrectangle into a linear surface.
51void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, 51void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel,
52 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height, 52 u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input);
53 u32 offset_x, u32 offset_y);
54 53
55/// @brief Swizzles a 2D array of pixels into a 3D texture 54/// @brief Swizzles a 2D array of pixels into a 3D texture
56/// @param line_length_in Number of pixels per line 55/// @param line_length_in Number of pixels per line
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 45f360bdd..4e3a092c7 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6
6#include "common/logging/log.h" 7#include "common/logging/log.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/settings.h" 9#include "core/settings.h"
@@ -16,37 +17,46 @@
16#include "video_core/video_core.h" 17#include "video_core/video_core.h"
17 18
18namespace { 19namespace {
19std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, 20
20 Core::System& system, 21std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
21 Core::Frontend::GraphicsContext& context) { 22 Core::System& system, Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
23 std::unique_ptr<Core::Frontend::GraphicsContext> context) {
22 switch (Settings::values.renderer_backend.GetValue()) { 24 switch (Settings::values.renderer_backend.GetValue()) {
23 case Settings::RendererBackend::OpenGL: 25 case Settings::RendererBackend::OpenGL:
24 return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context); 26 return std::make_unique<OpenGL::RendererOpenGL>(system, emu_window, gpu,
27 std::move(context));
25#ifdef HAS_VULKAN 28#ifdef HAS_VULKAN
26 case Settings::RendererBackend::Vulkan: 29 case Settings::RendererBackend::Vulkan:
27 return std::make_unique<Vulkan::RendererVulkan>(emu_window, system); 30 return std::make_unique<Vulkan::RendererVulkan>(system, emu_window, gpu,
31 std::move(context));
28#endif 32#endif
29 default: 33 default:
30 return nullptr; 34 return nullptr;
31 } 35 }
32} 36}
37
33} // Anonymous namespace 38} // Anonymous namespace
34 39
35namespace VideoCore { 40namespace VideoCore {
36 41
37std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { 42std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
43 std::unique_ptr<Tegra::GPU> gpu;
44 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
45 gpu = std::make_unique<VideoCommon::GPUAsynch>(system);
46 } else {
47 gpu = std::make_unique<VideoCommon::GPUSynch>(system);
48 }
49
38 auto context = emu_window.CreateSharedContext(); 50 auto context = emu_window.CreateSharedContext();
39 const auto scope = context->Acquire(); 51 const auto scope = context->Acquire();
40 auto renderer = CreateRenderer(emu_window, system, *context); 52
53 auto renderer = CreateRenderer(system, emu_window, *gpu, std::move(context));
41 if (!renderer->Init()) { 54 if (!renderer->Init()) {
42 return nullptr; 55 return nullptr;
43 } 56 }
44 57
45 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) { 58 gpu->BindRenderer(std::move(renderer));
46 return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer), 59 return gpu;
47 std::move(context));
48 }
49 return std::make_unique<VideoCommon::GPUSynch>(system, std::move(renderer), std::move(context));
50} 60}
51 61
52u16 GetResolutionScaleFactor(const RendererBase& renderer) { 62u16 GetResolutionScaleFactor(const RendererBase& renderer) {
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt
index 06ab7c59d..7e484b906 100644
--- a/src/web_service/CMakeLists.txt
+++ b/src/web_service/CMakeLists.txt
@@ -5,6 +5,7 @@ add_library(web_service STATIC
5 verify_login.h 5 verify_login.h
6 web_backend.cpp 6 web_backend.cpp
7 web_backend.h 7 web_backend.h
8 web_result.h
8) 9)
9 10
10create_target_directory_groups(web_service) 11create_target_directory_groups(web_service)
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index 7a480e33c..6215c914f 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -4,12 +4,14 @@
4 4
5#include <nlohmann/json.hpp> 5#include <nlohmann/json.hpp>
6#include "common/detached_tasks.h" 6#include "common/detached_tasks.h"
7#include "common/web_result.h"
8#include "web_service/telemetry_json.h" 7#include "web_service/telemetry_json.h"
9#include "web_service/web_backend.h" 8#include "web_service/web_backend.h"
9#include "web_service/web_result.h"
10 10
11namespace WebService { 11namespace WebService {
12 12
13namespace Telemetry = Common::Telemetry;
14
13struct TelemetryJson::Impl { 15struct TelemetryJson::Impl {
14 Impl(std::string host, std::string username, std::string token) 16 Impl(std::string host, std::string username, std::string token)
15 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {} 17 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {}
@@ -123,7 +125,7 @@ bool TelemetryJson::SubmitTestcase() {
123 Client client(impl->host, impl->username, impl->token); 125 Client client(impl->host, impl->username, impl->token);
124 auto value = client.PostJson("/gamedb/testcase", content, false); 126 auto value = client.PostJson("/gamedb/testcase", content, false);
125 127
126 return value.result_code == Common::WebResult::Code::Success; 128 return value.result_code == WebResult::Code::Success;
127} 129}
128 130
129} // namespace WebService 131} // namespace WebService
diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h
index dfd202829..df51e00f8 100644
--- a/src/web_service/telemetry_json.h
+++ b/src/web_service/telemetry_json.h
@@ -14,25 +14,25 @@ namespace WebService {
14 * Implementation of VisitorInterface that serialized telemetry into JSON, and submits it to the 14 * Implementation of VisitorInterface that serialized telemetry into JSON, and submits it to the
15 * yuzu web service 15 * yuzu web service
16 */ 16 */
17class TelemetryJson : public Telemetry::VisitorInterface { 17class TelemetryJson : public Common::Telemetry::VisitorInterface {
18public: 18public:
19 TelemetryJson(std::string host, std::string username, std::string token); 19 TelemetryJson(std::string host, std::string username, std::string token);
20 ~TelemetryJson() override; 20 ~TelemetryJson() override;
21 21
22 void Visit(const Telemetry::Field<bool>& field) override; 22 void Visit(const Common::Telemetry::Field<bool>& field) override;
23 void Visit(const Telemetry::Field<double>& field) override; 23 void Visit(const Common::Telemetry::Field<double>& field) override;
24 void Visit(const Telemetry::Field<float>& field) override; 24 void Visit(const Common::Telemetry::Field<float>& field) override;
25 void Visit(const Telemetry::Field<u8>& field) override; 25 void Visit(const Common::Telemetry::Field<u8>& field) override;
26 void Visit(const Telemetry::Field<u16>& field) override; 26 void Visit(const Common::Telemetry::Field<u16>& field) override;
27 void Visit(const Telemetry::Field<u32>& field) override; 27 void Visit(const Common::Telemetry::Field<u32>& field) override;
28 void Visit(const Telemetry::Field<u64>& field) override; 28 void Visit(const Common::Telemetry::Field<u64>& field) override;
29 void Visit(const Telemetry::Field<s8>& field) override; 29 void Visit(const Common::Telemetry::Field<s8>& field) override;
30 void Visit(const Telemetry::Field<s16>& field) override; 30 void Visit(const Common::Telemetry::Field<s16>& field) override;
31 void Visit(const Telemetry::Field<s32>& field) override; 31 void Visit(const Common::Telemetry::Field<s32>& field) override;
32 void Visit(const Telemetry::Field<s64>& field) override; 32 void Visit(const Common::Telemetry::Field<s64>& field) override;
33 void Visit(const Telemetry::Field<std::string>& field) override; 33 void Visit(const Common::Telemetry::Field<std::string>& field) override;
34 void Visit(const Telemetry::Field<const char*>& field) override; 34 void Visit(const Common::Telemetry::Field<const char*>& field) override;
35 void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override; 35 void Visit(const Common::Telemetry::Field<std::chrono::microseconds>& field) override;
36 36
37 void Complete() override; 37 void Complete() override;
38 bool SubmitTestcase() override; 38 bool SubmitTestcase() override;
diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp
index bfaa5b70a..ceb55ca6b 100644
--- a/src/web_service/verify_login.cpp
+++ b/src/web_service/verify_login.cpp
@@ -3,9 +3,9 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <nlohmann/json.hpp> 5#include <nlohmann/json.hpp>
6#include "common/web_result.h"
7#include "web_service/verify_login.h" 6#include "web_service/verify_login.h"
8#include "web_service/web_backend.h" 7#include "web_service/web_backend.h"
8#include "web_service/web_result.h"
9 9
10namespace WebService { 10namespace WebService {
11 11
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 09d1651ac..74e287045 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -6,13 +6,14 @@
6#include <cstdlib> 6#include <cstdlib>
7#include <mutex> 7#include <mutex>
8#include <string> 8#include <string>
9
9#include <LUrlParser.h> 10#include <LUrlParser.h>
10#include <fmt/format.h> 11#include <fmt/format.h>
11#include <httplib.h> 12#include <httplib.h>
12#include "common/common_types.h" 13
13#include "common/logging/log.h" 14#include "common/logging/log.h"
14#include "common/web_result.h"
15#include "web_service/web_backend.h" 15#include "web_service/web_backend.h"
16#include "web_service/web_result.h"
16 17
17namespace WebService { 18namespace WebService {
18 19
@@ -33,17 +34,16 @@ struct Client::Impl {
33 } 34 }
34 35
35 /// A generic function handles POST, GET and DELETE request together 36 /// A generic function handles POST, GET and DELETE request together
36 Common::WebResult GenericRequest(const std::string& method, const std::string& path, 37 WebResult GenericRequest(const std::string& method, const std::string& path,
37 const std::string& data, bool allow_anonymous, 38 const std::string& data, bool allow_anonymous,
38 const std::string& accept) { 39 const std::string& accept) {
39 if (jwt.empty()) { 40 if (jwt.empty()) {
40 UpdateJWT(); 41 UpdateJWT();
41 } 42 }
42 43
43 if (jwt.empty() && !allow_anonymous) { 44 if (jwt.empty() && !allow_anonymous) {
44 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); 45 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
45 return Common::WebResult{Common::WebResult::Code::CredentialsMissing, 46 return WebResult{WebResult::Code::CredentialsMissing, "Credentials needed", ""};
46 "Credentials needed", ""};
47 } 47 }
48 48
49 auto result = GenericRequest(method, path, data, accept, jwt); 49 auto result = GenericRequest(method, path, data, accept, jwt);
@@ -62,10 +62,10 @@ struct Client::Impl {
62 * username + token is used if jwt is empty but username and token are 62 * username + token is used if jwt is empty but username and token are
63 * not empty anonymous if all of jwt, username and token are empty 63 * not empty anonymous if all of jwt, username and token are empty
64 */ 64 */
65 Common::WebResult GenericRequest(const std::string& method, const std::string& path, 65 WebResult GenericRequest(const std::string& method, const std::string& path,
66 const std::string& data, const std::string& accept, 66 const std::string& data, const std::string& accept,
67 const std::string& jwt = "", const std::string& username = "", 67 const std::string& jwt = "", const std::string& username = "",
68 const std::string& token = "") { 68 const std::string& token = "") {
69 if (cli == nullptr) { 69 if (cli == nullptr) {
70 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); 70 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
71 int port; 71 int port;
@@ -81,12 +81,12 @@ struct Client::Impl {
81 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port); 81 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port);
82 } else { 82 } else {
83 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme); 83 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
84 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme", ""}; 84 return WebResult{WebResult::Code::InvalidURL, "Bad URL scheme", ""};
85 } 85 }
86 } 86 }
87 if (cli == nullptr) { 87 if (cli == nullptr) {
88 LOG_ERROR(WebService, "Invalid URL {}", host + path); 88 LOG_ERROR(WebService, "Invalid URL {}", host + path);
89 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL", ""}; 89 return WebResult{WebResult::Code::InvalidURL, "Invalid URL", ""};
90 } 90 }
91 cli->set_timeout_sec(TIMEOUT_SECONDS); 91 cli->set_timeout_sec(TIMEOUT_SECONDS);
92 92
@@ -106,7 +106,7 @@ struct Client::Impl {
106 std::string(API_VERSION.begin(), API_VERSION.end())); 106 std::string(API_VERSION.begin(), API_VERSION.end()));
107 if (method != "GET") { 107 if (method != "GET") {
108 params.emplace(std::string("Content-Type"), std::string("application/json")); 108 params.emplace(std::string("Content-Type"), std::string("application/json"));
109 }; 109 }
110 110
111 httplib::Request request; 111 httplib::Request request;
112 request.method = method; 112 request.method = method;
@@ -118,29 +118,28 @@ struct Client::Impl {
118 118
119 if (!cli->send(request, response)) { 119 if (!cli->send(request, response)) {
120 LOG_ERROR(WebService, "{} to {} returned null", method, host + path); 120 LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
121 return Common::WebResult{Common::WebResult::Code::LibError, "Null response", ""}; 121 return WebResult{WebResult::Code::LibError, "Null response", ""};
122 } 122 }
123 123
124 if (response.status >= 400) { 124 if (response.status >= 400) {
125 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path, 125 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
126 response.status); 126 response.status);
127 return Common::WebResult{Common::WebResult::Code::HttpError, 127 return WebResult{WebResult::Code::HttpError, std::to_string(response.status), ""};
128 std::to_string(response.status), ""};
129 } 128 }
130 129
131 auto content_type = response.headers.find("content-type"); 130 auto content_type = response.headers.find("content-type");
132 131
133 if (content_type == response.headers.end()) { 132 if (content_type == response.headers.end()) {
134 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path); 133 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path);
135 return Common::WebResult{Common::WebResult::Code::WrongContent, "", ""}; 134 return WebResult{WebResult::Code::WrongContent, "", ""};
136 } 135 }
137 136
138 if (content_type->second.find(accept) == std::string::npos) { 137 if (content_type->second.find(accept) == std::string::npos) {
139 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path, 138 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path,
140 content_type->second); 139 content_type->second);
141 return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content", ""}; 140 return WebResult{WebResult::Code::WrongContent, "Wrong content", ""};
142 } 141 }
143 return Common::WebResult{Common::WebResult::Code::Success, "", response.body}; 142 return WebResult{WebResult::Code::Success, "", response.body};
144 } 143 }
145 144
146 // Retrieve a new JWT from given username and token 145 // Retrieve a new JWT from given username and token
@@ -150,7 +149,7 @@ struct Client::Impl {
150 } 149 }
151 150
152 auto result = GenericRequest("POST", "/jwt/internal", "", "text/html", "", username, token); 151 auto result = GenericRequest("POST", "/jwt/internal", "", "text/html", "", username, token);
153 if (result.result_code != Common::WebResult::Code::Success) { 152 if (result.result_code != WebResult::Code::Success) {
154 LOG_ERROR(WebService, "UpdateJWT failed"); 153 LOG_ERROR(WebService, "UpdateJWT failed");
155 } else { 154 } else {
156 std::lock_guard lock{jwt_cache.mutex}; 155 std::lock_guard lock{jwt_cache.mutex};
@@ -180,29 +179,28 @@ Client::Client(std::string host, std::string username, std::string token)
180 179
181Client::~Client() = default; 180Client::~Client() = default;
182 181
183Common::WebResult Client::PostJson(const std::string& path, const std::string& data, 182WebResult Client::PostJson(const std::string& path, const std::string& data, bool allow_anonymous) {
184 bool allow_anonymous) {
185 return impl->GenericRequest("POST", path, data, allow_anonymous, "application/json"); 183 return impl->GenericRequest("POST", path, data, allow_anonymous, "application/json");
186} 184}
187 185
188Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) { 186WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
189 return impl->GenericRequest("GET", path, "", allow_anonymous, "application/json"); 187 return impl->GenericRequest("GET", path, "", allow_anonymous, "application/json");
190} 188}
191 189
192Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data, 190WebResult Client::DeleteJson(const std::string& path, const std::string& data,
193 bool allow_anonymous) { 191 bool allow_anonymous) {
194 return impl->GenericRequest("DELETE", path, data, allow_anonymous, "application/json"); 192 return impl->GenericRequest("DELETE", path, data, allow_anonymous, "application/json");
195} 193}
196 194
197Common::WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) { 195WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) {
198 return impl->GenericRequest("GET", path, "", allow_anonymous, "text/plain"); 196 return impl->GenericRequest("GET", path, "", allow_anonymous, "text/plain");
199} 197}
200 198
201Common::WebResult Client::GetImage(const std::string& path, bool allow_anonymous) { 199WebResult Client::GetImage(const std::string& path, bool allow_anonymous) {
202 return impl->GenericRequest("GET", path, "", allow_anonymous, "image/png"); 200 return impl->GenericRequest("GET", path, "", allow_anonymous, "image/png");
203} 201}
204 202
205Common::WebResult Client::GetExternalJWT(const std::string& audience) { 203WebResult Client::GetExternalJWT(const std::string& audience) {
206 return impl->GenericRequest("POST", fmt::format("/jwt/external/{}", audience), "", false, 204 return impl->GenericRequest("POST", fmt::format("/jwt/external/{}", audience), "", false,
207 "text/html"); 205 "text/html");
208} 206}
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h
index 04121f17e..81f58583c 100644
--- a/src/web_service/web_backend.h
+++ b/src/web_service/web_backend.h
@@ -7,12 +7,10 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9 9
10namespace Common {
11struct WebResult;
12}
13
14namespace WebService { 10namespace WebService {
15 11
12struct WebResult;
13
16class Client { 14class Client {
17public: 15public:
18 Client(std::string host, std::string username, std::string token); 16 Client(std::string host, std::string username, std::string token);
@@ -25,8 +23,7 @@ public:
25 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 23 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
26 * @return the result of the request. 24 * @return the result of the request.
27 */ 25 */
28 Common::WebResult PostJson(const std::string& path, const std::string& data, 26 WebResult PostJson(const std::string& path, const std::string& data, bool allow_anonymous);
29 bool allow_anonymous);
30 27
31 /** 28 /**
32 * Gets JSON from the specified path. 29 * Gets JSON from the specified path.
@@ -34,7 +31,7 @@ public:
34 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 31 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
35 * @return the result of the request. 32 * @return the result of the request.
36 */ 33 */
37 Common::WebResult GetJson(const std::string& path, bool allow_anonymous); 34 WebResult GetJson(const std::string& path, bool allow_anonymous);
38 35
39 /** 36 /**
40 * Deletes JSON to the specified path. 37 * Deletes JSON to the specified path.
@@ -43,8 +40,7 @@ public:
43 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 40 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
44 * @return the result of the request. 41 * @return the result of the request.
45 */ 42 */
46 Common::WebResult DeleteJson(const std::string& path, const std::string& data, 43 WebResult DeleteJson(const std::string& path, const std::string& data, bool allow_anonymous);
47 bool allow_anonymous);
48 44
49 /** 45 /**
50 * Gets a plain string from the specified path. 46 * Gets a plain string from the specified path.
@@ -52,7 +48,7 @@ public:
52 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 48 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
53 * @return the result of the request. 49 * @return the result of the request.
54 */ 50 */
55 Common::WebResult GetPlain(const std::string& path, bool allow_anonymous); 51 WebResult GetPlain(const std::string& path, bool allow_anonymous);
56 52
57 /** 53 /**
58 * Gets an PNG image from the specified path. 54 * Gets an PNG image from the specified path.
@@ -60,14 +56,14 @@ public:
60 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 56 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
61 * @return the result of the request. 57 * @return the result of the request.
62 */ 58 */
63 Common::WebResult GetImage(const std::string& path, bool allow_anonymous); 59 WebResult GetImage(const std::string& path, bool allow_anonymous);
64 60
65 /** 61 /**
66 * Requests an external JWT for the specific audience provided. 62 * Requests an external JWT for the specific audience provided.
67 * @param audience the audience of the JWT requested. 63 * @param audience the audience of the JWT requested.
68 * @return the result of the request. 64 * @return the result of the request.
69 */ 65 */
70 Common::WebResult GetExternalJWT(const std::string& audience); 66 WebResult GetExternalJWT(const std::string& audience);
71 67
72private: 68private:
73 struct Impl; 69 struct Impl;
diff --git a/src/common/web_result.h b/src/web_service/web_result.h
index 8bfa2141d..3aeeb5288 100644
--- a/src/common/web_result.h
+++ b/src/web_service/web_result.h
@@ -7,7 +7,7 @@
7#include <string> 7#include <string>
8#include "common/common_types.h" 8#include "common/common_types.h"
9 9
10namespace Common { 10namespace WebService {
11struct WebResult { 11struct WebResult {
12 enum class Code : u32 { 12 enum class Code : u32 {
13 Success, 13 Success,
@@ -22,4 +22,4 @@ struct WebResult {
22 std::string result_string; 22 std::string result_string;
23 std::string returned_data; 23 std::string returned_data;
24}; 24};
25} // namespace Common 25} // namespace WebService
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp
index 4bc8ee726..dca8835ed 100644
--- a/src/yuzu/applets/profile_select.cpp
+++ b/src/yuzu/applets/profile_select.cpp
@@ -26,7 +26,7 @@ QString FormatUserEntryText(const QString& username, Common::UUID uuid) {
26} 26}
27 27
28QString GetImagePath(Common::UUID uuid) { 28QString GetImagePath(Common::UUID uuid) {
29 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 29 const auto path = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
30 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; 30 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
31 return QString::fromStdString(path); 31 return QString::fromStdString(path);
32} 32}
diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp
index 5477f050c..649912557 100644
--- a/src/yuzu/compatdb.cpp
+++ b/src/yuzu/compatdb.cpp
@@ -54,7 +54,8 @@ void CompatDB::Submit() {
54 back(); 54 back();
55 LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); 55 LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
56 Core::System::GetInstance().TelemetrySession().AddField( 56 Core::System::GetInstance().TelemetrySession().AddField(
57 Telemetry::FieldType::UserFeedback, "Compatibility", compatibility->checkedId()); 57 Common::Telemetry::FieldType::UserFeedback, "Compatibility",
58 compatibility->checkedId());
58 59
59 button(NextButton)->setEnabled(false); 60 button(NextButton)->setEnabled(false);
60 button(NextButton)->setText(tr("Submitting")); 61 button(NextButton)->setText(tr("Submitting"));
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index cb71b8d11..7af974d8d 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -13,10 +13,12 @@
13#include "input_common/udp/client.h" 13#include "input_common/udp/client.h"
14#include "yuzu/configuration/config.h" 14#include "yuzu/configuration/config.h"
15 15
16namespace FS = Common::FS;
17
16Config::Config(const std::string& config_file, bool is_global) { 18Config::Config(const std::string& config_file, bool is_global) {
17 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 19 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
18 qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + config_file; 20 qt_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + config_file;
19 FileUtil::CreateFullPath(qt_config_loc); 21 FS::CreateFullPath(qt_config_loc);
20 qt_config = 22 qt_config =
21 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); 23 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
22 global = is_global; 24 global = is_global;
@@ -464,41 +466,36 @@ void Config::ReadDataStorageValues() {
464 qt_config->beginGroup(QStringLiteral("Data Storage")); 466 qt_config->beginGroup(QStringLiteral("Data Storage"));
465 467
466 Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool(); 468 Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool();
467 FileUtil::GetUserPath( 469 FS::GetUserPath(FS::UserPath::NANDDir,
468 FileUtil::UserPath::NANDDir, 470 qt_config
469 qt_config 471 ->value(QStringLiteral("nand_directory"),
470 ->value(QStringLiteral("nand_directory"), 472 QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir)))
471 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))) 473 .toString()
472 .toString() 474 .toStdString());
473 .toStdString()); 475 FS::GetUserPath(FS::UserPath::SDMCDir,
474 FileUtil::GetUserPath( 476 qt_config
475 FileUtil::UserPath::SDMCDir, 477 ->value(QStringLiteral("sdmc_directory"),
476 qt_config 478 QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir)))
477 ->value(QStringLiteral("sdmc_directory"), 479 .toString()
478 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))) 480 .toStdString());
479 .toString() 481 FS::GetUserPath(FS::UserPath::LoadDir,
480 .toStdString()); 482 qt_config
481 FileUtil::GetUserPath( 483 ->value(QStringLiteral("load_directory"),
482 FileUtil::UserPath::LoadDir, 484 QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir)))
483 qt_config 485 .toString()
484 ->value(QStringLiteral("load_directory"), 486 .toStdString());
485 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))) 487 FS::GetUserPath(FS::UserPath::DumpDir,
486 .toString() 488 qt_config
487 .toStdString()); 489 ->value(QStringLiteral("dump_directory"),
488 FileUtil::GetUserPath( 490 QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)))
489 FileUtil::UserPath::DumpDir, 491 .toString()
490 qt_config 492 .toStdString());
491 ->value(QStringLiteral("dump_directory"), 493 FS::GetUserPath(FS::UserPath::CacheDir,
492 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))) 494 qt_config
493 .toString() 495 ->value(QStringLiteral("cache_directory"),
494 .toStdString()); 496 QString::fromStdString(FS::GetUserPath(FS::UserPath::CacheDir)))
495 FileUtil::GetUserPath( 497 .toString()
496 FileUtil::UserPath::CacheDir, 498 .toStdString());
497 qt_config
498 ->value(QStringLiteral("cache_directory"),
499 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)))
500 .toString()
501 .toStdString());
502 Settings::values.gamecard_inserted = 499 Settings::values.gamecard_inserted =
503 ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool(); 500 ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool();
504 Settings::values.gamecard_current_game = 501 Settings::values.gamecard_current_game =
@@ -638,6 +635,11 @@ void Config::ReadCpuValues() {
638 ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool(); 635 ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool();
639 Settings::values.cpuopt_reduce_misalign_checks = 636 Settings::values.cpuopt_reduce_misalign_checks =
640 ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool(); 637 ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool();
638
639 Settings::values.cpuopt_unsafe_unfuse_fma =
640 ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool();
641 Settings::values.cpuopt_unsafe_reduce_fp_error =
642 ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool();
641 } 643 }
642 644
643 qt_config->endGroup(); 645 qt_config->endGroup();
@@ -677,11 +679,11 @@ void Config::ReadScreenshotValues() {
677 679
678 UISettings::values.enable_screenshot_save_as = 680 UISettings::values.enable_screenshot_save_as =
679 ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool(); 681 ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool();
680 FileUtil::GetUserPath( 682 FS::GetUserPath(
681 FileUtil::UserPath::ScreenshotsDir, 683 FS::UserPath::ScreenshotsDir,
682 qt_config 684 qt_config
683 ->value(QStringLiteral("screenshot_path"), QString::fromStdString(FileUtil::GetUserPath( 685 ->value(QStringLiteral("screenshot_path"),
684 FileUtil::UserPath::ScreenshotsDir))) 686 QString::fromStdString(FS::GetUserPath(FS::UserPath::ScreenshotsDir)))
685 .toString() 687 .toString()
686 .toStdString()); 688 .toStdString());
687 689
@@ -1016,20 +1018,20 @@ void Config::SaveDataStorageValues() {
1016 1018
1017 WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true); 1019 WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true);
1018 WriteSetting(QStringLiteral("nand_directory"), 1020 WriteSetting(QStringLiteral("nand_directory"),
1019 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)), 1021 QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir)),
1020 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); 1022 QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir)));
1021 WriteSetting(QStringLiteral("sdmc_directory"), 1023 WriteSetting(QStringLiteral("sdmc_directory"),
1022 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)), 1024 QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir)),
1023 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 1025 QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir)));
1024 WriteSetting(QStringLiteral("load_directory"), 1026 WriteSetting(QStringLiteral("load_directory"),
1025 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)), 1027 QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir)),
1026 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))); 1028 QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir)));
1027 WriteSetting(QStringLiteral("dump_directory"), 1029 WriteSetting(QStringLiteral("dump_directory"),
1028 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)), 1030 QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)),
1029 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))); 1031 QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)));
1030 WriteSetting(QStringLiteral("cache_directory"), 1032 WriteSetting(QStringLiteral("cache_directory"),
1031 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)), 1033 QString::fromStdString(FS::GetUserPath(FS::UserPath::CacheDir)),
1032 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))); 1034 QString::fromStdString(FS::GetUserPath(FS::UserPath::CacheDir)));
1033 WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false); 1035 WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false);
1034 WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, 1036 WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game,
1035 false); 1037 false);
@@ -1135,6 +1137,11 @@ void Config::SaveCpuValues() {
1135 WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true); 1137 WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true);
1136 WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), 1138 WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"),
1137 Settings::values.cpuopt_reduce_misalign_checks, true); 1139 Settings::values.cpuopt_reduce_misalign_checks, true);
1140
1141 WriteSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"),
1142 Settings::values.cpuopt_unsafe_unfuse_fma, true);
1143 WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"),
1144 Settings::values.cpuopt_unsafe_reduce_fp_error, true);
1138 } 1145 }
1139 1146
1140 qt_config->endGroup(); 1147 qt_config->endGroup();
@@ -1180,7 +1187,7 @@ void Config::SaveScreenshotValues() {
1180 WriteSetting(QStringLiteral("enable_screenshot_save_as"), 1187 WriteSetting(QStringLiteral("enable_screenshot_save_as"),
1181 UISettings::values.enable_screenshot_save_as); 1188 UISettings::values.enable_screenshot_save_as);
1182 WriteSetting(QStringLiteral("screenshot_path"), 1189 WriteSetting(QStringLiteral("screenshot_path"),
1183 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))); 1190 QString::fromStdString(FS::GetUserPath(FS::UserPath::ScreenshotsDir)));
1184 1191
1185 qt_config->endGroup(); 1192 qt_config->endGroup();
1186} 1193}
diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp
index f9becab6e..18482795c 100644
--- a/src/yuzu/configuration/configuration_shared.cpp
+++ b/src/yuzu/configuration/configuration_shared.cpp
@@ -72,18 +72,18 @@ void ConfigurationShared::SetPerGameSetting(
72 ConfigurationShared::USE_GLOBAL_OFFSET); 72 ConfigurationShared::USE_GLOBAL_OFFSET);
73} 73}
74 74
75void ConfigurationShared::SetHighlight(QWidget* widget, const std::string& name, bool highlighted) { 75void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) {
76 if (highlighted) { 76 if (highlighted) {
77 widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }") 77 widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }")
78 .arg(QString::fromStdString(name))); 78 .arg(widget->objectName()));
79 } else { 79 } else {
80 widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,0,0,0) }") 80 widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,0,0,0) }")
81 .arg(QString::fromStdString(name))); 81 .arg(widget->objectName()));
82 } 82 }
83 widget->show(); 83 widget->show();
84} 84}
85 85
86void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, const std::string& name, 86void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox,
87 const Settings::Setting<bool>& setting, 87 const Settings::Setting<bool>& setting,
88 CheckState& tracker) { 88 CheckState& tracker) {
89 if (setting.UsingGlobal()) { 89 if (setting.UsingGlobal()) {
@@ -91,45 +91,39 @@ void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, const std::str
91 } else { 91 } else {
92 tracker = (setting.GetValue() == setting.GetValue(true)) ? CheckState::On : CheckState::Off; 92 tracker = (setting.GetValue() == setting.GetValue(true)) ? CheckState::On : CheckState::Off;
93 } 93 }
94 SetHighlight(checkbox, name, tracker != CheckState::Global); 94 SetHighlight(checkbox, tracker != CheckState::Global);
95 QObject::connect(checkbox, &QCheckBox::clicked, checkbox, 95 QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, setting, &tracker] {
96 [checkbox, name, setting, &tracker]() { 96 tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
97 tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) % 97 static_cast<int>(CheckState::Count));
98 static_cast<int>(CheckState::Count)); 98 if (tracker == CheckState::Global) {
99 if (tracker == CheckState::Global) { 99 checkbox->setChecked(setting.GetValue(true));
100 checkbox->setChecked(setting.GetValue(true)); 100 }
101 } 101 SetHighlight(checkbox, tracker != CheckState::Global);
102 SetHighlight(checkbox, name, tracker != CheckState::Global); 102 });
103 });
104} 103}
105 104
106void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, const std::string& name, 105void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, bool global, bool state,
107 bool global, bool state, bool global_state, 106 bool global_state, CheckState& tracker) {
108 CheckState& tracker) {
109 if (global) { 107 if (global) {
110 tracker = CheckState::Global; 108 tracker = CheckState::Global;
111 } else { 109 } else {
112 tracker = (state == global_state) ? CheckState::On : CheckState::Off; 110 tracker = (state == global_state) ? CheckState::On : CheckState::Off;
113 } 111 }
114 SetHighlight(checkbox, name, tracker != CheckState::Global); 112 SetHighlight(checkbox, tracker != CheckState::Global);
115 QObject::connect(checkbox, &QCheckBox::clicked, checkbox, 113 QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, global_state, &tracker] {
116 [checkbox, name, global_state, &tracker]() { 114 tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
117 tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) % 115 static_cast<int>(CheckState::Count));
118 static_cast<int>(CheckState::Count)); 116 if (tracker == CheckState::Global) {
119 if (tracker == CheckState::Global) { 117 checkbox->setChecked(global_state);
120 checkbox->setChecked(global_state); 118 }
121 } 119 SetHighlight(checkbox, tracker != CheckState::Global);
122 SetHighlight(checkbox, name, tracker != CheckState::Global); 120 });
123 });
124} 121}
125 122
126void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target, 123void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target, int global) {
127 const std::string& target_name, int global) {
128 InsertGlobalItem(combobox, global); 124 InsertGlobalItem(combobox, global);
129 QObject::connect(combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), target, 125 QObject::connect(combobox, qOverload<int>(&QComboBox::activated), target,
130 [target, target_name](int index) { 126 [target](int index) { SetHighlight(target, index != 0); });
131 ConfigurationShared::SetHighlight(target, target_name, index != 0);
132 });
133} 127}
134 128
135void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index) { 129void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index) {
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h
index 003148c68..312b9e549 100644
--- a/src/yuzu/configuration/configuration_shared.h
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -39,13 +39,12 @@ void SetPerGameSetting(QComboBox* combobox,
39void SetPerGameSetting(QComboBox* combobox, 39void SetPerGameSetting(QComboBox* combobox,
40 const Settings::Setting<Settings::GPUAccuracy>* setting); 40 const Settings::Setting<Settings::GPUAccuracy>* setting);
41 41
42void SetHighlight(QWidget* widget, const std::string& name, bool highlighted); 42void SetHighlight(QWidget* widget, bool highlighted);
43void SetColoredTristate(QCheckBox* checkbox, const std::string& name, 43void SetColoredTristate(QCheckBox* checkbox, const Settings::Setting<bool>& setting,
44 const Settings::Setting<bool>& setting, CheckState& tracker); 44 CheckState& tracker);
45void SetColoredTristate(QCheckBox* checkbox, const std::string& name, bool global, bool state, 45void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state,
46 bool global_state, CheckState& tracker); 46 CheckState& tracker);
47void SetColoredComboBox(QComboBox* combobox, QWidget* target, const std::string& target_name, 47void SetColoredComboBox(QComboBox* combobox, QWidget* target, int global);
48 int global);
49 48
50void InsertGlobalItem(QComboBox* combobox, int global_index); 49void InsertGlobalItem(QComboBox* combobox, int global_index);
51 50
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index fea632531..fa9124ecf 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -59,7 +59,7 @@ void ConfigureAudio::SetConfiguration() {
59 ui->volume_combo_box->setCurrentIndex(1); 59 ui->volume_combo_box->setCurrentIndex(1);
60 ui->volume_slider->setEnabled(true); 60 ui->volume_slider->setEnabled(true);
61 } 61 }
62 ConfigurationShared::SetHighlight(ui->volume_layout, "volume_layout", 62 ConfigurationShared::SetHighlight(ui->volume_layout,
63 !Settings::values.volume.UsingGlobal()); 63 !Settings::values.volume.UsingGlobal());
64 } 64 }
65 SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); 65 SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
@@ -173,14 +173,13 @@ void ConfigureAudio::SetupPerGameUI() {
173 return; 173 return;
174 } 174 }
175 175
176 ConfigurationShared::SetColoredTristate(ui->toggle_audio_stretching, "toggle_audio_stretching", 176 ConfigurationShared::SetColoredTristate(ui->toggle_audio_stretching,
177 Settings::values.enable_audio_stretching, 177 Settings::values.enable_audio_stretching,
178 enable_audio_stretching); 178 enable_audio_stretching);
179 connect(ui->volume_combo_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), 179 connect(ui->volume_combo_box, qOverload<int>(&QComboBox::activated), this, [this](int index) {
180 this, [this](int index) { 180 ui->volume_slider->setEnabled(index == 1);
181 ui->volume_slider->setEnabled(index == 1); 181 ConfigurationShared::SetHighlight(ui->volume_layout, index == 1);
182 ConfigurationShared::SetHighlight(ui->volume_layout, "volume_layout", index == 1); 182 });
183 });
184 183
185 ui->output_sink_combo_box->setVisible(false); 184 ui->output_sink_combo_box->setVisible(false);
186 ui->output_sink_label->setVisible(false); 185 ui->output_sink_label->setVisible(false);
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index 7493e5ffb..37fcd6adc 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -19,6 +19,8 @@ ConfigureCpu::ConfigureCpu(QWidget* parent) : QWidget(parent), ui(new Ui::Config
19 19
20 connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this, 20 connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this,
21 &ConfigureCpu::AccuracyUpdated); 21 &ConfigureCpu::AccuracyUpdated);
22 connect(ui->accuracy, qOverload<int>(&QComboBox::currentIndexChanged), this,
23 &ConfigureCpu::UpdateGroup);
22} 24}
23 25
24ConfigureCpu::~ConfigureCpu() = default; 26ConfigureCpu::~ConfigureCpu() = default;
@@ -28,6 +30,12 @@ void ConfigureCpu::SetConfiguration() {
28 30
29 ui->accuracy->setEnabled(runtime_lock); 31 ui->accuracy->setEnabled(runtime_lock);
30 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy)); 32 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy));
33 UpdateGroup(static_cast<int>(Settings::values.cpu_accuracy));
34
35 ui->cpuopt_unsafe_unfuse_fma->setEnabled(runtime_lock);
36 ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma);
37 ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock);
38 ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error);
31} 39}
32 40
33void ConfigureCpu::AccuracyUpdated(int index) { 41void ConfigureCpu::AccuracyUpdated(int index) {
@@ -38,14 +46,21 @@ void ConfigureCpu::AccuracyUpdated(int index) {
38 QMessageBox::Yes | QMessageBox::No); 46 QMessageBox::Yes | QMessageBox::No);
39 if (result == QMessageBox::No) { 47 if (result == QMessageBox::No) {
40 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate)); 48 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate));
41 return; 49 UpdateGroup(static_cast<int>(Settings::CPUAccuracy::Accurate));
42 } 50 }
43 } 51 }
44} 52}
45 53
54void ConfigureCpu::UpdateGroup(int index) {
55 ui->unsafe_group->setVisible(static_cast<Settings::CPUAccuracy>(index) ==
56 Settings::CPUAccuracy::Unsafe);
57}
58
46void ConfigureCpu::ApplyConfiguration() { 59void ConfigureCpu::ApplyConfiguration() {
47 Settings::values.cpu_accuracy = 60 Settings::values.cpu_accuracy =
48 static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()); 61 static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex());
62 Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked();
63 Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked();
49} 64}
50 65
51void ConfigureCpu::changeEvent(QEvent* event) { 66void ConfigureCpu::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h
index e4741d3a4..3c5683d81 100644
--- a/src/yuzu/configuration/configure_cpu.h
+++ b/src/yuzu/configuration/configure_cpu.h
@@ -26,6 +26,7 @@ private:
26 void RetranslateUI(); 26 void RetranslateUI();
27 27
28 void AccuracyUpdated(int index); 28 void AccuracyUpdated(int index);
29 void UpdateGroup(int index);
29 30
30 void SetConfiguration(); 31 void SetConfiguration();
31 32
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
index bf6ea79bb..ebdd2e6e9 100644
--- a/src/yuzu/configuration/configure_cpu.ui
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -40,6 +40,11 @@
40 </item> 40 </item>
41 <item> 41 <item>
42 <property name="text"> 42 <property name="text">
43 <string>Unsafe</string>
44 </property>
45 </item>
46 <item>
47 <property name="text">
43 <string>Enable Debug Mode</string> 48 <string>Enable Debug Mode</string>
44 </property> 49 </property>
45 </item> 50 </item>
@@ -63,6 +68,53 @@
63 </layout> 68 </layout>
64 </item> 69 </item>
65 <item> 70 <item>
71 <layout class="QVBoxLayout">
72 <item>
73 <widget class="QGroupBox" name="unsafe_group">
74 <property name="title">
75 <string>Unsafe CPU Optimization Settings</string>
76 </property>
77 <layout class="QVBoxLayout">
78 <item>
79 <widget class="QLabel">
80 <property name="wordWrap">
81 <bool>1</bool>
82 </property>
83 <property name="text">
84 <string>These settings reduce accuracy for speed.</string>
85 </property>
86 </widget>
87 </item>
88 <item>
89 <widget class="QCheckBox" name="cpuopt_unsafe_unfuse_fma">
90 <property name="text">
91 <string>Unfuse FMA (improve performance on CPUs without FMA)</string>
92 </property>
93 <property name="toolTip">
94 <string>
95 &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
96 </string>
97 </property>
98 </widget>
99 </item>
100 <item>
101 <widget class="QCheckBox" name="cpuopt_unsafe_reduce_fp_error">
102 <property name="text">
103 <string>Faster FRSQRTE and FRECPE</string>
104 </property>
105 <property name="toolTip">
106 <string>
107 &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
108 </string>
109 </property>
110 </widget>
111 </item>
112 </layout>
113 </widget>
114 </item>
115 </layout>
116 </item>
117 <item>
66 <spacer name="verticalSpacer"> 118 <spacer name="verticalSpacer">
67 <property name="orientation"> 119 <property name="orientation">
68 <enum>Qt::Vertical</enum> 120 <enum>Qt::Vertical</enum>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index d0e71dd60..2bfe2c306 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -19,7 +19,8 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
19 SetConfiguration(); 19 SetConfiguration();
20 20
21 connect(ui->open_log_button, &QPushButton::clicked, []() { 21 connect(ui->open_log_button, &QPushButton::clicked, []() {
22 QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir)); 22 const auto path =
23 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::LogDir));
23 QDesktopServices::openUrl(QUrl::fromLocalFile(path)); 24 QDesktopServices::openUrl(QUrl::fromLocalFile(path));
24 }); 25 });
25} 26}
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp
index a089f5733..7ab4a80f7 100644
--- a/src/yuzu/configuration/configure_filesystem.cpp
+++ b/src/yuzu/configuration/configure_filesystem.cpp
@@ -42,16 +42,16 @@ ConfigureFilesystem::~ConfigureFilesystem() = default;
42 42
43void ConfigureFilesystem::setConfiguration() { 43void ConfigureFilesystem::setConfiguration() {
44 ui->nand_directory_edit->setText( 44 ui->nand_directory_edit->setText(
45 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); 45 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)));
46 ui->sdmc_directory_edit->setText( 46 ui->sdmc_directory_edit->setText(
47 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 47 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)));
48 ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path)); 48 ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path));
49 ui->dump_path_edit->setText( 49 ui->dump_path_edit->setText(
50 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))); 50 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir)));
51 ui->load_path_edit->setText( 51 ui->load_path_edit->setText(
52 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))); 52 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir)));
53 ui->cache_directory_edit->setText( 53 ui->cache_directory_edit->setText(
54 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))); 54 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)));
55 55
56 ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted); 56 ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted);
57 ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game); 57 ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game);
@@ -64,14 +64,16 @@ void ConfigureFilesystem::setConfiguration() {
64} 64}
65 65
66void ConfigureFilesystem::applyConfiguration() { 66void ConfigureFilesystem::applyConfiguration() {
67 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir, 67 Common::FS::GetUserPath(Common::FS::UserPath::NANDDir,
68 ui->nand_directory_edit->text().toStdString()); 68 ui->nand_directory_edit->text().toStdString());
69 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, 69 Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir,
70 ui->sdmc_directory_edit->text().toStdString()); 70 ui->sdmc_directory_edit->text().toStdString());
71 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir, ui->dump_path_edit->text().toStdString()); 71 Common::FS::GetUserPath(Common::FS::UserPath::DumpDir,
72 FileUtil::GetUserPath(FileUtil::UserPath::LoadDir, ui->load_path_edit->text().toStdString()); 72 ui->dump_path_edit->text().toStdString());
73 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir, 73 Common::FS::GetUserPath(Common::FS::UserPath::LoadDir,
74 ui->cache_directory_edit->text().toStdString()); 74 ui->load_path_edit->text().toStdString());
75 Common::FS::GetUserPath(Common::FS::UserPath::CacheDir,
76 ui->cache_directory_edit->text().toStdString());
75 Settings::values.gamecard_path = ui->gamecard_path_edit->text().toStdString(); 77 Settings::values.gamecard_path = ui->gamecard_path_edit->text().toStdString();
76 78
77 Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked(); 79 Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked();
@@ -121,12 +123,13 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit)
121} 123}
122 124
123void ConfigureFilesystem::ResetMetadata() { 125void ConfigureFilesystem::ResetMetadata() {
124 if (!FileUtil::Exists(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + 126 if (!Common::FS::Exists(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
125 "game_list")) { 127 "game_list")) {
126 QMessageBox::information(this, tr("Reset Metadata Cache"), 128 QMessageBox::information(this, tr("Reset Metadata Cache"),
127 tr("The metadata cache is already empty.")); 129 tr("The metadata cache is already empty."));
128 } else if (FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + 130 } else if (Common::FS::DeleteDirRecursively(
129 DIR_SEP + "game_list")) { 131 Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
132 "game_list")) {
130 QMessageBox::information(this, tr("Reset Metadata Cache"), 133 QMessageBox::information(this, tr("Reset Metadata Cache"),
131 tr("The operation completed successfully.")); 134 tr("The operation completed successfully."));
132 UISettings::values.is_game_list_reload_pending.exchange(true); 135 UISettings::values.is_game_list_reload_pending.exchange(true);
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index c0dbd9855..830096ea0 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -105,10 +105,10 @@ void ConfigureGeneral::SetupPerGameUI() {
105 ui->toggle_background_pause->setVisible(false); 105 ui->toggle_background_pause->setVisible(false);
106 ui->toggle_hide_mouse->setVisible(false); 106 ui->toggle_hide_mouse->setVisible(false);
107 107
108 ConfigurationShared::SetColoredTristate(ui->toggle_frame_limit, "toggle_frame_limit", 108 ConfigurationShared::SetColoredTristate(ui->toggle_frame_limit,
109 Settings::values.use_frame_limit, use_frame_limit); 109 Settings::values.use_frame_limit, use_frame_limit);
110 ConfigurationShared::SetColoredTristate(ui->use_multi_core, "use_multi_core", 110 ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core,
111 Settings::values.use_multi_core, use_multi_core); 111 use_multi_core);
112 112
113 connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, [this]() { 113 connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, [this]() {
114 ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked() && 114 ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked() &&
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 3e42531c3..07d818548 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -34,9 +34,8 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
34 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] { 34 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
35 UpdateDeviceComboBox(); 35 UpdateDeviceComboBox();
36 if (!Settings::configuring_global) { 36 if (!Settings::configuring_global) {
37 ConfigurationShared::SetHighlight(ui->api_layout, "api_layout", 37 ConfigurationShared::SetHighlight(
38 ui->api->currentIndex() != 38 ui->api_layout, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX);
39 ConfigurationShared::USE_GLOBAL_INDEX);
40 } 39 }
41 }); 40 });
42 connect(ui->device, qOverload<int>(&QComboBox::activated), this, 41 connect(ui->device, qOverload<int>(&QComboBox::activated), this,
@@ -80,17 +79,16 @@ void ConfigureGraphics::SetConfiguration() {
80 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); 79 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
81 } else { 80 } else {
82 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); 81 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
83 ConfigurationShared::SetHighlight(ui->api_layout, "api_layout", 82 ConfigurationShared::SetHighlight(ui->api_layout,
84 !Settings::values.renderer_backend.UsingGlobal()); 83 !Settings::values.renderer_backend.UsingGlobal());
85 ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox, 84 ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox,
86 &Settings::values.aspect_ratio); 85 &Settings::values.aspect_ratio);
87 86
88 ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1); 87 ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
89 ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal()); 88 ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
90 ConfigurationShared::SetHighlight(ui->ar_label, "ar_label", 89 ConfigurationShared::SetHighlight(ui->ar_label,
91 !Settings::values.aspect_ratio.UsingGlobal()); 90 !Settings::values.aspect_ratio.UsingGlobal());
92 ConfigurationShared::SetHighlight(ui->bg_layout, "bg_layout", 91 ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal());
93 !Settings::values.bg_red.UsingGlobal());
94 } 92 }
95 93
96 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(), 94 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(),
@@ -248,20 +246,18 @@ void ConfigureGraphics::SetupPerGameUI() {
248 return; 246 return;
249 } 247 }
250 248
251 connect(ui->bg_combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, 249 connect(ui->bg_combobox, qOverload<int>(&QComboBox::activated), this, [this](int index) {
252 [this](int index) { 250 ui->bg_button->setEnabled(index == 1);
253 ui->bg_button->setEnabled(index == 1); 251 ConfigurationShared::SetHighlight(ui->bg_layout, index == 1);
254 ConfigurationShared::SetHighlight(ui->bg_layout, "bg_layout", index == 1); 252 });
255 });
256 253
257 ConfigurationShared::SetColoredTristate(ui->use_disk_shader_cache, "use_disk_shader_cache",
258 Settings::values.use_disk_shader_cache,
259 use_disk_shader_cache);
260 ConfigurationShared::SetColoredTristate( 254 ConfigurationShared::SetColoredTristate(
261 ui->use_asynchronous_gpu_emulation, "use_asynchronous_gpu_emulation", 255 ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache);
262 Settings::values.use_asynchronous_gpu_emulation, use_asynchronous_gpu_emulation); 256 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation,
257 Settings::values.use_asynchronous_gpu_emulation,
258 use_asynchronous_gpu_emulation);
263 259
264 ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label, "ar_label", 260 ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label,
265 Settings::values.aspect_ratio.GetValue(true)); 261 Settings::values.aspect_ratio.GetValue(true));
266 ConfigurationShared::InsertGlobalItem( 262 ConfigurationShared::InsertGlobalItem(
267 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); 263 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index c5d1a778c..73f276949 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -41,9 +41,9 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
41 ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy); 41 ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy);
42 ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox, 42 ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox,
43 &Settings::values.max_anisotropy); 43 &Settings::values.max_anisotropy);
44 ConfigurationShared::SetHighlight(ui->label_gpu_accuracy, "label_gpu_accuracy", 44 ConfigurationShared::SetHighlight(ui->label_gpu_accuracy,
45 !Settings::values.gpu_accuracy.UsingGlobal()); 45 !Settings::values.gpu_accuracy.UsingGlobal());
46 ConfigurationShared::SetHighlight(ui->af_label, "af_label", 46 ConfigurationShared::SetHighlight(ui->af_label,
47 !Settings::values.max_anisotropy.UsingGlobal()); 47 !Settings::values.max_anisotropy.UsingGlobal());
48 } 48 }
49} 49}
@@ -131,20 +131,18 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
131 return; 131 return;
132 } 132 }
133 133
134 ConfigurationShared::SetColoredTristate(ui->use_vsync, "use_vsync", Settings::values.use_vsync, 134 ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync);
135 use_vsync);
136 ConfigurationShared::SetColoredTristate(ui->use_assembly_shaders, "use_assembly_shaders",
137 Settings::values.use_assembly_shaders,
138 use_assembly_shaders);
139 ConfigurationShared::SetColoredTristate( 135 ConfigurationShared::SetColoredTristate(
140 ui->use_asynchronous_shaders, "use_asynchronous_shaders", 136 ui->use_assembly_shaders, Settings::values.use_assembly_shaders, use_assembly_shaders);
141 Settings::values.use_asynchronous_shaders, use_asynchronous_shaders); 137 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders,
142 ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, "use_fast_gpu_time", 138 Settings::values.use_asynchronous_shaders,
139 use_asynchronous_shaders);
140 ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time,
143 Settings::values.use_fast_gpu_time, use_fast_gpu_time); 141 Settings::values.use_fast_gpu_time, use_fast_gpu_time);
144 ConfigurationShared::SetColoredComboBox( 142 ConfigurationShared::SetColoredComboBox(
145 ui->gpu_accuracy, ui->label_gpu_accuracy, "label_gpu_accuracy", 143 ui->gpu_accuracy, ui->label_gpu_accuracy,
146 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); 144 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
147 ConfigurationShared::SetColoredComboBox( 145 ConfigurationShared::SetColoredComboBox(
148 ui->anisotropic_filtering_combobox, ui->af_label, "af_label", 146 ui->anisotropic_filtering_combobox, ui->af_label,
149 static_cast<int>(Settings::values.max_anisotropy.GetValue(true))); 147 static_cast<int>(Settings::values.max_anisotropy.GetValue(true)));
150} 148}
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index a793c803d..846a30586 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -92,7 +92,7 @@
92 <string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string> 92 <string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
93 </property> 93 </property>
94 <property name="text"> 94 <property name="text">
95 <string>Use asynchronous shader building (experimental, OpenGL or Assembly shaders only)</string> 95 <string>Use asynchronous shader building (experimental)</string>
96 </property> 96 </property>
97 </widget> 97 </widget>
98 </item> 98 </item>
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
index 6f7fd4414..cbee51a5e 100644
--- a/src/yuzu/configuration/configure_hotkeys.cpp
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -154,7 +154,7 @@ void ConfigureHotkeys::ClearAll() {
154 const QStandardItem* parent = model->item(r, 0); 154 const QStandardItem* parent = model->item(r, 0);
155 155
156 for (int r2 = 0; r2 < parent->rowCount(); ++r2) { 156 for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
157 model->item(r, 0)->child(r2, 1)->setText(tr("")); 157 model->item(r, 0)->child(r2, 1)->setText(QString{});
158 } 158 }
159 } 159 }
160} 160}
@@ -186,7 +186,7 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) {
186 model->setData(selected, default_key_sequence.toString(QKeySequence::NativeText)); 186 model->setData(selected, default_key_sequence.toString(QKeySequence::NativeText));
187 } 187 }
188 }); 188 });
189 connect(clear, &QAction::triggered, [this, selected] { model->setData(selected, tr("")); }); 189 connect(clear, &QAction::triggered, [this, selected] { model->setData(selected, QString{}); });
190 190
191 context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location)); 191 context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location));
192} 192}
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index 478d5d3a1..793fd8975 100644
--- a/src/yuzu/configuration/configure_per_game_addons.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -79,8 +79,8 @@ void ConfigurePerGameAddons::ApplyConfiguration() {
79 std::sort(disabled_addons.begin(), disabled_addons.end()); 79 std::sort(disabled_addons.begin(), disabled_addons.end());
80 std::sort(current.begin(), current.end()); 80 std::sort(current.begin(), current.end());
81 if (disabled_addons != current) { 81 if (disabled_addons != current) {
82 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + 82 Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
83 "game_list" + DIR_SEP + fmt::format("{:016X}.pv.txt", title_id)); 83 "game_list" + DIR_SEP + fmt::format("{:016X}.pv.txt", title_id));
84 } 84 }
85 85
86 Settings::values.disabled_addons[title_id] = disabled_addons; 86 Settings::values.disabled_addons[title_id] = disabled_addons;
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp
index f53423440..6334c4c50 100644
--- a/src/yuzu/configuration/configure_profile_manager.cpp
+++ b/src/yuzu/configuration/configure_profile_manager.cpp
@@ -34,7 +34,7 @@ constexpr std::array<u8, 107> backup_jpeg{
34}; 34};
35 35
36QString GetImagePath(Common::UUID uuid) { 36QString GetImagePath(Common::UUID uuid) {
37 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 37 const auto path = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
38 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; 38 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
39 return QString::fromStdString(path); 39 return QString::fromStdString(path);
40} 40}
@@ -282,7 +282,7 @@ void ConfigureProfileManager::SetUserImage() {
282 } 282 }
283 283
284 const auto raw_path = QString::fromStdString( 284 const auto raw_path = QString::fromStdString(
285 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"); 285 Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + "/system/save/8000000000000010");
286 const QFileInfo raw_info{raw_path}; 286 const QFileInfo raw_info{raw_path};
287 if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) { 287 if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) {
288 QMessageBox::warning(this, tr("Error deleting file"), 288 QMessageBox::warning(this, tr("Error deleting file"),
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 0c4daf147..9ad43ed8f 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -90,13 +90,13 @@ void ConfigureSystem::SetConfiguration() {
90 &Settings::values.time_zone_index); 90 &Settings::values.time_zone_index);
91 ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index); 91 ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index);
92 92
93 ConfigurationShared::SetHighlight(ui->label_language, "label_language", 93 ConfigurationShared::SetHighlight(ui->label_language,
94 !Settings::values.language_index.UsingGlobal()); 94 !Settings::values.language_index.UsingGlobal());
95 ConfigurationShared::SetHighlight(ui->label_region, "label_region", 95 ConfigurationShared::SetHighlight(ui->label_region,
96 !Settings::values.region_index.UsingGlobal()); 96 !Settings::values.region_index.UsingGlobal());
97 ConfigurationShared::SetHighlight(ui->label_timezone, "label_timezone", 97 ConfigurationShared::SetHighlight(ui->label_timezone,
98 !Settings::values.time_zone_index.UsingGlobal()); 98 !Settings::values.time_zone_index.UsingGlobal());
99 ConfigurationShared::SetHighlight(ui->label_sound, "label_sound", 99 ConfigurationShared::SetHighlight(ui->label_sound,
100 !Settings::values.sound_index.UsingGlobal()); 100 !Settings::values.sound_index.UsingGlobal());
101 } 101 }
102} 102}
@@ -224,22 +224,20 @@ void ConfigureSystem::SetupPerGameUI() {
224 } 224 }
225 225
226 ConfigurationShared::SetColoredComboBox(ui->combo_language, ui->label_language, 226 ConfigurationShared::SetColoredComboBox(ui->combo_language, ui->label_language,
227 "label_language",
228 Settings::values.language_index.GetValue(true)); 227 Settings::values.language_index.GetValue(true));
229 ConfigurationShared::SetColoredComboBox(ui->combo_region, ui->label_region, "label_region", 228 ConfigurationShared::SetColoredComboBox(ui->combo_region, ui->label_region,
230 Settings::values.region_index.GetValue(true)); 229 Settings::values.region_index.GetValue(true));
231 ConfigurationShared::SetColoredComboBox(ui->combo_time_zone, ui->label_timezone, 230 ConfigurationShared::SetColoredComboBox(ui->combo_time_zone, ui->label_timezone,
232 "label_timezone",
233 Settings::values.time_zone_index.GetValue(true)); 231 Settings::values.time_zone_index.GetValue(true));
234 ConfigurationShared::SetColoredComboBox(ui->combo_sound, ui->label_sound, "label_sound", 232 ConfigurationShared::SetColoredComboBox(ui->combo_sound, ui->label_sound,
235 Settings::values.sound_index.GetValue(true)); 233 Settings::values.sound_index.GetValue(true));
236 234
237 ConfigurationShared::SetColoredTristate( 235 ConfigurationShared::SetColoredTristate(
238 ui->rng_seed_checkbox, "rng_seed_checkbox", Settings::values.rng_seed.UsingGlobal(), 236 ui->rng_seed_checkbox, Settings::values.rng_seed.UsingGlobal(),
239 Settings::values.rng_seed.GetValue().has_value(), 237 Settings::values.rng_seed.GetValue().has_value(),
240 Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed); 238 Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed);
241 ConfigurationShared::SetColoredTristate( 239 ConfigurationShared::SetColoredTristate(
242 ui->custom_rtc_checkbox, "custom_rtc_checkbox", Settings::values.custom_rtc.UsingGlobal(), 240 ui->custom_rtc_checkbox, Settings::values.custom_rtc.UsingGlobal(),
243 Settings::values.custom_rtc.GetValue().has_value(), 241 Settings::values.custom_rtc.GetValue().has_value(),
244 Settings::values.custom_rtc.GetValue(true).has_value(), use_custom_rtc); 242 Settings::values.custom_rtc.GetValue(true).has_value(), use_custom_rtc);
245} 243}
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index 2c20b68d0..dbe3f78c8 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -61,9 +61,9 @@ ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::Configur
61 // Set screenshot path to user specification. 61 // Set screenshot path to user specification.
62 connect(ui->screenshot_path_button, &QToolButton::pressed, this, [this] { 62 connect(ui->screenshot_path_button, &QToolButton::pressed, this, [this] {
63 const QString& filename = 63 const QString& filename =
64 QFileDialog::getExistingDirectory( 64 QFileDialog::getExistingDirectory(this, tr("Select Screenshots Path..."),
65 this, tr("Select Screenshots Path..."), 65 QString::fromStdString(Common::FS::GetUserPath(
66 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))) + 66 Common::FS::UserPath::ScreenshotsDir))) +
67 QDir::separator(); 67 QDir::separator();
68 if (!filename.isEmpty()) { 68 if (!filename.isEmpty()) {
69 ui->screenshot_path_edit->setText(filename); 69 ui->screenshot_path_edit->setText(filename);
@@ -82,8 +82,8 @@ void ConfigureUi::ApplyConfiguration() {
82 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); 82 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
83 83
84 UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked(); 84 UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked();
85 FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir, 85 Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir,
86 ui->screenshot_path_edit->text().toStdString()); 86 ui->screenshot_path_edit->text().toStdString());
87 Settings::Apply(); 87 Settings::Apply();
88} 88}
89 89
@@ -101,7 +101,7 @@ void ConfigureUi::SetConfiguration() {
101 101
102 ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as); 102 ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as);
103 ui->screenshot_path_edit->setText( 103 ui->screenshot_path_edit->setText(
104 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))); 104 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir)));
105} 105}
106 106
107void ConfigureUi::changeEvent(QEvent* event) { 107void ConfigureUi::changeEvent(QEvent* event) {
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp
index 53049ffd6..0e26f765b 100644
--- a/src/yuzu/debugger/profiler.cpp
+++ b/src/yuzu/debugger/profiler.cpp
@@ -109,8 +109,7 @@ MicroProfileWidget::MicroProfileWidget(QWidget* parent) : QWidget(parent) {
109 MicroProfileSetDisplayMode(1); // Timers screen 109 MicroProfileSetDisplayMode(1); // Timers screen
110 MicroProfileInitUI(); 110 MicroProfileInitUI();
111 111
112 connect(&update_timer, &QTimer::timeout, this, 112 connect(&update_timer, &QTimer::timeout, this, qOverload<>(&MicroProfileWidget::update));
113 static_cast<void (MicroProfileWidget::*)()>(&MicroProfileWidget::update));
114} 113}
115 114
116void MicroProfileWidget::paintEvent(QPaintEvent* ev) { 115void MicroProfileWidget::paintEvent(QPaintEvent* ev) {
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 62acc3720..6a71d9644 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -406,7 +406,7 @@ bool GameList::isEmpty() const {
406 type == GameListItemType::SysNandDir)) { 406 type == GameListItemType::SysNandDir)) {
407 item_model->invisibleRootItem()->removeRow(child->row()); 407 item_model->invisibleRootItem()->removeRow(child->row());
408 i--; 408 i--;
409 }; 409 }
410 } 410 }
411 return !item_model->invisibleRootItem()->hasChildren(); 411 return !item_model->invisibleRootItem()->hasChildren();
412} 412}
@@ -502,10 +502,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string pat
502 navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0); 502 navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
503 503
504 connect(open_save_location, &QAction::triggered, [this, program_id, path]() { 504 connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
505 emit OpenFolderRequested(GameListOpenTarget::SaveData, path); 505 emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path);
506 }); 506 });
507 connect(open_mod_location, &QAction::triggered, [this, program_id, path]() { 507 connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
508 emit OpenFolderRequested(GameListOpenTarget::ModData, path); 508 emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path);
509 }); 509 });
510 connect(open_transferable_shader_cache, &QAction::triggered, 510 connect(open_transferable_shader_cache, &QAction::triggered,
511 [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); }); 511 [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); });
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 483835cce..78e2ba169 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -84,7 +84,8 @@ public:
84signals: 84signals:
85 void GameChosen(QString game_path); 85 void GameChosen(QString game_path);
86 void ShouldCancelWorker(); 86 void ShouldCancelWorker();
87 void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path); 87 void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
88 const std::string& game_path);
88 void OpenTransferableShaderCacheRequested(u64 program_id); 89 void OpenTransferableShaderCacheRequested(u64 program_id);
89 void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type); 90 void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type);
90 void RemoveFileRequested(u64 program_id, GameListRemoveTarget target); 91 void RemoveFileRequested(u64 program_id, GameListRemoveTarget target);
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 643ca6491..e0ce45fd9 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -39,12 +39,12 @@ QString GetGameListCachedObject(const std::string& filename, const std::string&
39 return generator(); 39 return generator();
40 } 40 }
41 41
42 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" + 42 const auto path = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
43 DIR_SEP + filename + '.' + ext; 43 "game_list" + DIR_SEP + filename + '.' + ext;
44 44
45 FileUtil::CreateFullPath(path); 45 Common::FS::CreateFullPath(path);
46 46
47 if (!FileUtil::Exists(path)) { 47 if (!Common::FS::Exists(path)) {
48 const auto str = generator(); 48 const auto str = generator();
49 49
50 QFile file{QString::fromStdString(path)}; 50 QFile file{QString::fromStdString(path)};
@@ -70,14 +70,14 @@ std::pair<std::vector<u8>, std::string> GetGameListCachedObject(
70 return generator(); 70 return generator();
71 } 71 }
72 72
73 const auto path1 = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" + 73 const auto path1 = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
74 DIR_SEP + filename + ".jpeg"; 74 "game_list" + DIR_SEP + filename + ".jpeg";
75 const auto path2 = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" + 75 const auto path2 = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
76 DIR_SEP + filename + ".appname.txt"; 76 "game_list" + DIR_SEP + filename + ".appname.txt";
77 77
78 FileUtil::CreateFullPath(path1); 78 Common::FS::CreateFullPath(path1);
79 79
80 if (!FileUtil::Exists(path1) || !FileUtil::Exists(path2)) { 80 if (!Common::FS::Exists(path1) || !Common::FS::Exists(path2)) {
81 const auto [icon, nacp] = generator(); 81 const auto [icon, nacp] = generator();
82 82
83 QFile file1{QString::fromStdString(path1)}; 83 QFile file1{QString::fromStdString(path1)};
@@ -208,7 +208,7 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
208 file_type_string, program_id), 208 file_type_string, program_id),
209 new GameListItemCompat(compatibility), 209 new GameListItemCompat(compatibility),
210 new GameListItem(file_type_string), 210 new GameListItem(file_type_string),
211 new GameListItemSize(FileUtil::GetSize(path)), 211 new GameListItemSize(Common::FS::GetSize(path)),
212 }; 212 };
213 213
214 if (UISettings::values.show_add_ons) { 214 if (UISettings::values.show_add_ons) {
@@ -289,7 +289,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
289 } 289 }
290 290
291 const std::string physical_name = directory + DIR_SEP + virtual_name; 291 const std::string physical_name = directory + DIR_SEP + virtual_name;
292 const bool is_dir = FileUtil::IsDirectory(physical_name); 292 const bool is_dir = Common::FS::IsDirectory(physical_name);
293 if (!is_dir && 293 if (!is_dir &&
294 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { 294 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
295 const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read); 295 const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read);
@@ -345,7 +345,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
345 return true; 345 return true;
346 }; 346 };
347 347
348 FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); 348 Common::FS::ForeachDirectoryEntry(nullptr, dir_path, callback);
349} 349}
350 350
351void GameListWorker::run() { 351void GameListWorker::run() {
@@ -374,7 +374,7 @@ void GameListWorker::run() {
374 ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), 374 ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(),
375 game_dir.deep_scan ? 256 : 0, game_list_dir); 375 game_dir.deep_scan ? 256 : 0, game_list_dir);
376 } 376 }
377 }; 377 }
378 378
379 emit Finished(watch_list); 379 emit Finished(watch_list);
380} 380}
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 592993c36..cd7e78eb4 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -177,8 +177,8 @@ static void InitializeLogging() {
177 log_filter.ParseFilterString(Settings::values.log_filter); 177 log_filter.ParseFilterString(Settings::values.log_filter);
178 Log::SetGlobalFilter(log_filter); 178 Log::SetGlobalFilter(log_filter);
179 179
180 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 180 const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
181 FileUtil::CreateFullPath(log_dir); 181 Common::FS::CreateFullPath(log_dir);
182 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); 182 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
183#ifdef _WIN32 183#ifdef _WIN32
184 Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); 184 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
@@ -894,6 +894,8 @@ void GMainWindow::ConnectMenuEvents() {
894 connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ); 894 connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ);
895 connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); 895 connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); });
896 connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); 896 connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
897 connect(ui.action_Configure_Current_Game, &QAction::triggered, this,
898 &GMainWindow::OnConfigurePerGame);
897 899
898 // View 900 // View
899 connect(ui.action_Single_Window_Mode, &QAction::triggered, this, 901 connect(ui.action_Single_Window_Mode, &QAction::triggered, this,
@@ -1039,7 +1041,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
1039 } 1041 }
1040 game_path = filename; 1042 game_path = filename;
1041 1043
1042 system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "Qt"); 1044 system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "Qt");
1043 return true; 1045 return true;
1044} 1046}
1045 1047
@@ -1121,7 +1123,7 @@ void GMainWindow::BootGame(const QString& filename) {
1121 title_name = metadata.first->GetApplicationName(); 1123 title_name = metadata.first->GetApplicationName();
1122 } 1124 }
1123 if (res != Loader::ResultStatus::Success || title_name.empty()) { 1125 if (res != Loader::ResultStatus::Success || title_name.empty()) {
1124 title_name = FileUtil::GetFilename(filename.toStdString()); 1126 title_name = Common::FS::GetFilename(filename.toStdString());
1125 } 1127 }
1126 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); 1128 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
1127 UpdateWindowTitle(title_name, title_version); 1129 UpdateWindowTitle(title_name, title_version);
@@ -1167,6 +1169,7 @@ void GMainWindow::ShutdownGame() {
1167 ui.action_Pause->setEnabled(false); 1169 ui.action_Pause->setEnabled(false);
1168 ui.action_Stop->setEnabled(false); 1170 ui.action_Stop->setEnabled(false);
1169 ui.action_Restart->setEnabled(false); 1171 ui.action_Restart->setEnabled(false);
1172 ui.action_Configure_Current_Game->setEnabled(false);
1170 ui.action_Report_Compatibility->setEnabled(false); 1173 ui.action_Report_Compatibility->setEnabled(false);
1171 ui.action_Load_Amiibo->setEnabled(false); 1174 ui.action_Load_Amiibo->setEnabled(false);
1172 ui.action_Capture_Screenshot->setEnabled(false); 1175 ui.action_Capture_Screenshot->setEnabled(false);
@@ -1239,27 +1242,36 @@ void GMainWindow::OnGameListLoadFile(QString game_path) {
1239 BootGame(game_path); 1242 BootGame(game_path);
1240} 1243}
1241 1244
1242void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path) { 1245void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
1246 const std::string& game_path) {
1243 std::string path; 1247 std::string path;
1244 QString open_target; 1248 QString open_target;
1245 1249
1246 const auto v_file = Core::GetGameFileFromPath(vfs, game_path); 1250 const auto [user_save_size, device_save_size] = [this, &program_id, &game_path] {
1247 const auto loader = Loader::GetLoader(v_file); 1251 FileSys::PatchManager pm{program_id};
1248 FileSys::NACP control{}; 1252 const auto control = pm.GetControlMetadata().first;
1249 u64 program_id{}; 1253 if (control != nullptr) {
1254 return std::make_pair(control->GetDefaultNormalSaveSize(),
1255 control->GetDeviceSaveDataSize());
1256 } else {
1257 const auto file = Core::GetGameFileFromPath(vfs, game_path);
1258 const auto loader = Loader::GetLoader(file);
1250 1259
1251 loader->ReadControlData(control); 1260 FileSys::NACP nacp{};
1252 loader->ReadProgramId(program_id); 1261 loader->ReadControlData(nacp);
1262 return std::make_pair(nacp.GetDefaultNormalSaveSize(), nacp.GetDeviceSaveDataSize());
1263 }
1264 }();
1253 1265
1254 const bool has_user_save{control.GetDefaultNormalSaveSize() > 0}; 1266 const bool has_user_save{user_save_size > 0};
1255 const bool has_device_save{control.GetDeviceSaveDataSize() > 0}; 1267 const bool has_device_save{device_save_size > 0};
1256 1268
1257 ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?"); 1269 ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?");
1258 1270
1259 switch (target) { 1271 switch (target) {
1260 case GameListOpenTarget::SaveData: { 1272 case GameListOpenTarget::SaveData: {
1261 open_target = tr("Save Data"); 1273 open_target = tr("Save Data");
1262 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); 1274 const std::string nand_dir = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
1263 1275
1264 if (has_user_save) { 1276 if (has_user_save) {
1265 // User save data 1277 // User save data
@@ -1294,16 +1306,16 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str
1294 FileSys::SaveDataType::SaveData, program_id, {}, 0); 1306 FileSys::SaveDataType::SaveData, program_id, {}, 0);
1295 } 1307 }
1296 1308
1297 if (!FileUtil::Exists(path)) { 1309 if (!Common::FS::Exists(path)) {
1298 FileUtil::CreateFullPath(path); 1310 Common::FS::CreateFullPath(path);
1299 FileUtil::CreateDir(path); 1311 Common::FS::CreateDir(path);
1300 } 1312 }
1301 1313
1302 break; 1314 break;
1303 } 1315 }
1304 case GameListOpenTarget::ModData: { 1316 case GameListOpenTarget::ModData: {
1305 open_target = tr("Mod Data"); 1317 open_target = tr("Mod Data");
1306 const auto load_dir = FileUtil::GetUserPath(FileUtil::UserPath::LoadDir); 1318 const auto load_dir = Common::FS::GetUserPath(Common::FS::UserPath::LoadDir);
1307 path = fmt::format("{}{:016X}", load_dir, program_id); 1319 path = fmt::format("{}{:016X}", load_dir, program_id);
1308 break; 1320 break;
1309 } 1321 }
@@ -1325,7 +1337,7 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str
1325 1337
1326void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { 1338void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
1327 const QString shader_dir = 1339 const QString shader_dir =
1328 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); 1340 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir));
1329 const QString transferable_shader_cache_folder_path = 1341 const QString transferable_shader_cache_folder_path =
1330 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); 1342 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
1331 const QString transferable_shader_cache_file_path = 1343 const QString transferable_shader_cache_file_path =
@@ -1428,8 +1440,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
1428 RemoveAddOnContent(program_id, entry_type); 1440 RemoveAddOnContent(program_id, entry_type);
1429 break; 1441 break;
1430 } 1442 }
1431 FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + 1443 Common::FS::DeleteDirRecursively(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
1432 "game_list"); 1444 DIR_SEP + "game_list");
1433 game_list->PopulateAsync(UISettings::values.game_dirs); 1445 game_list->PopulateAsync(UISettings::values.game_dirs);
1434} 1446}
1435 1447
@@ -1519,7 +1531,7 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ
1519 1531
1520void GMainWindow::RemoveTransferableShaderCache(u64 program_id) { 1532void GMainWindow::RemoveTransferableShaderCache(u64 program_id) {
1521 const QString shader_dir = 1533 const QString shader_dir =
1522 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); 1534 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir));
1523 const QString transferable_shader_cache_folder_path = 1535 const QString transferable_shader_cache_folder_path =
1524 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); 1536 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
1525 const QString transferable_shader_cache_file_path = 1537 const QString transferable_shader_cache_file_path =
@@ -1543,7 +1555,7 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id) {
1543 1555
1544void GMainWindow::RemoveCustomConfiguration(u64 program_id) { 1556void GMainWindow::RemoveCustomConfiguration(u64 program_id) {
1545 const QString config_dir = 1557 const QString config_dir =
1546 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir)); 1558 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir));
1547 const QString custom_config_file_path = 1559 const QString custom_config_file_path =
1548 config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id)); 1560 config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id));
1549 1561
@@ -1590,7 +1602,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1590 } 1602 }
1591 1603
1592 const auto path = fmt::format( 1604 const auto path = fmt::format(
1593 "{}{:016X}/romfs", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), *romfs_title_id); 1605 "{}{:016X}/romfs", Common::FS::GetUserPath(Common::FS::UserPath::DumpDir), *romfs_title_id);
1594 1606
1595 FileSys::VirtualFile romfs; 1607 FileSys::VirtualFile romfs;
1596 1608
@@ -1670,13 +1682,13 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
1670void GMainWindow::OnGameListOpenDirectory(const QString& directory) { 1682void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
1671 QString path; 1683 QString path;
1672 if (directory == QStringLiteral("SDMC")) { 1684 if (directory == QStringLiteral("SDMC")) {
1673 path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + 1685 path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
1674 "Nintendo/Contents/registered"); 1686 "Nintendo/Contents/registered");
1675 } else if (directory == QStringLiteral("UserNAND")) { 1687 } else if (directory == QStringLiteral("UserNAND")) {
1676 path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1688 path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1677 "user/Contents/registered"); 1689 "user/Contents/registered");
1678 } else if (directory == QStringLiteral("SysNAND")) { 1690 } else if (directory == QStringLiteral("SysNAND")) {
1679 path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1691 path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1680 "system/Contents/registered"); 1692 "system/Contents/registered");
1681 } else { 1693 } else {
1682 path = directory; 1694 path = directory;
@@ -1690,8 +1702,10 @@ void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
1690 1702
1691void GMainWindow::OnGameListAddDirectory() { 1703void GMainWindow::OnGameListAddDirectory() {
1692 const QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); 1704 const QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
1693 if (dir_path.isEmpty()) 1705 if (dir_path.isEmpty()) {
1694 return; 1706 return;
1707 }
1708
1695 UISettings::GameDir game_dir{dir_path, false, true}; 1709 UISettings::GameDir game_dir{dir_path, false, true};
1696 if (!UISettings::values.game_dirs.contains(game_dir)) { 1710 if (!UISettings::values.game_dirs.contains(game_dir)) {
1697 UISettings::values.game_dirs.append(game_dir); 1711 UISettings::values.game_dirs.append(game_dir);
@@ -1718,26 +1732,7 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
1718 return; 1732 return;
1719 } 1733 }
1720 1734
1721 ConfigurePerGame dialog(this, title_id); 1735 OpenPerGameConfiguration(title_id, file);
1722 dialog.LoadFromFile(v_file);
1723 auto result = dialog.exec();
1724 if (result == QDialog::Accepted) {
1725 dialog.ApplyConfiguration();
1726
1727 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
1728 if (reload) {
1729 game_list->PopulateAsync(UISettings::values.game_dirs);
1730 }
1731
1732 // Do not cause the global config to write local settings into the config file
1733 Settings::RestoreGlobalState();
1734
1735 if (!Core::System::GetInstance().IsPoweredOn()) {
1736 config->Save();
1737 }
1738 } else {
1739 Settings::RestoreGlobalState();
1740 }
1741} 1736}
1742 1737
1743void GMainWindow::OnMenuLoadFile() { 1738void GMainWindow::OnMenuLoadFile() {
@@ -1882,8 +1877,8 @@ void GMainWindow::OnMenuInstallToNAND() {
1882 : tr("%n file(s) failed to install\n", "", failed_files.size())); 1877 : tr("%n file(s) failed to install\n", "", failed_files.size()));
1883 1878
1884 QMessageBox::information(this, tr("Install Results"), install_results); 1879 QMessageBox::information(this, tr("Install Results"), install_results);
1885 FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + 1880 Common::FS::DeleteDirRecursively(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
1886 "game_list"); 1881 DIR_SEP + "game_list");
1887 game_list->PopulateAsync(UISettings::values.game_dirs); 1882 game_list->PopulateAsync(UISettings::values.game_dirs);
1888 ui.action_Install_File_NAND->setEnabled(true); 1883 ui.action_Install_File_NAND->setEnabled(true);
1889} 1884}
@@ -2066,6 +2061,7 @@ void GMainWindow::OnStartGame() {
2066 ui.action_Pause->setEnabled(true); 2061 ui.action_Pause->setEnabled(true);
2067 ui.action_Stop->setEnabled(true); 2062 ui.action_Stop->setEnabled(true);
2068 ui.action_Restart->setEnabled(true); 2063 ui.action_Restart->setEnabled(true);
2064 ui.action_Configure_Current_Game->setEnabled(true);
2069 ui.action_Report_Compatibility->setEnabled(true); 2065 ui.action_Report_Compatibility->setEnabled(true);
2070 2066
2071 discord_rpc->Update(); 2067 discord_rpc->Update();
@@ -2255,6 +2251,36 @@ void GMainWindow::OnConfigure() {
2255 UpdateStatusButtons(); 2251 UpdateStatusButtons();
2256} 2252}
2257 2253
2254void GMainWindow::OnConfigurePerGame() {
2255 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
2256 OpenPerGameConfiguration(title_id, game_path.toStdString());
2257}
2258
2259void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_name) {
2260 const auto v_file = Core::GetGameFileFromPath(vfs, file_name);
2261
2262 ConfigurePerGame dialog(this, title_id);
2263 dialog.LoadFromFile(v_file);
2264 auto result = dialog.exec();
2265 if (result == QDialog::Accepted) {
2266 dialog.ApplyConfiguration();
2267
2268 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
2269 if (reload) {
2270 game_list->PopulateAsync(UISettings::values.game_dirs);
2271 }
2272
2273 // Do not cause the global config to write local settings into the config file
2274 Settings::RestoreGlobalState();
2275
2276 if (!Core::System::GetInstance().IsPoweredOn()) {
2277 config->Save();
2278 }
2279 } else {
2280 Settings::RestoreGlobalState();
2281 }
2282}
2283
2258void GMainWindow::OnLoadAmiibo() { 2284void GMainWindow::OnLoadAmiibo() {
2259 const QString extensions{QStringLiteral("*.bin")}; 2285 const QString extensions{QStringLiteral("*.bin")};
2260 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); 2286 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
@@ -2302,7 +2328,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
2302 2328
2303void GMainWindow::OnOpenYuzuFolder() { 2329void GMainWindow::OnOpenYuzuFolder() {
2304 QDesktopServices::openUrl(QUrl::fromLocalFile( 2330 QDesktopServices::openUrl(QUrl::fromLocalFile(
2305 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir)))); 2331 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::UserDir))));
2306} 2332}
2307 2333
2308void GMainWindow::OnAbout() { 2334void GMainWindow::OnAbout() {
@@ -2324,7 +2350,7 @@ void GMainWindow::OnCaptureScreenshot() {
2324 2350
2325 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 2351 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
2326 const auto screenshot_path = 2352 const auto screenshot_path =
2327 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir)); 2353 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir));
2328 const auto date = 2354 const auto date =
2329 QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz")); 2355 QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz"));
2330 QString filename = QStringLiteral("%1%2_%3.png") 2356 QString filename = QStringLiteral("%1%2_%3.png")
@@ -2527,18 +2553,18 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
2527 if (res == QMessageBox::Cancel) 2553 if (res == QMessageBox::Cancel)
2528 return; 2554 return;
2529 2555
2530 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) + 2556 Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) +
2531 "prod.keys_autogenerated"); 2557 "prod.keys_autogenerated");
2532 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) + 2558 Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) +
2533 "console.keys_autogenerated"); 2559 "console.keys_autogenerated");
2534 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) + 2560 Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) +
2535 "title.keys_autogenerated"); 2561 "title.keys_autogenerated");
2536 } 2562 }
2537 2563
2538 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); 2564 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
2539 if (keys.BaseDeriveNecessary()) { 2565 if (keys.BaseDeriveNecessary()) {
2540 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory( 2566 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory(
2541 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)}; 2567 Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), FileSys::Mode::Read)};
2542 2568
2543 const auto function = [this, &keys, &pdm] { 2569 const auto function = [this, &keys, &pdm] {
2544 keys.PopulateFromPartitionData(pdm); 2570 keys.PopulateFromPartitionData(pdm);
@@ -2870,7 +2896,7 @@ int main(int argc, char* argv[]) {
2870 // If you start a bundle (binary) on OSX without the Terminal, the working directory is "/". 2896 // If you start a bundle (binary) on OSX without the Terminal, the working directory is "/".
2871 // But since we require the working directory to be the executable path for the location of 2897 // But since we require the working directory to be the executable path for the location of
2872 // the user folder in the Qt Frontend, we need to cd into that working directory 2898 // the user folder in the Qt Frontend, we need to cd into that working directory
2873 const std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + ".."; 2899 const std::string bin_path = Common::FS::GetBundleDirectory() + DIR_SEP + "..";
2874 chdir(bin_path.c_str()); 2900 chdir(bin_path.c_str());
2875#endif 2901#endif
2876 2902
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 73a44a3bf..01f9131e5 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -198,7 +198,8 @@ private slots:
198 void OnOpenFAQ(); 198 void OnOpenFAQ();
199 /// Called whenever a user selects a game in the game list widget. 199 /// Called whenever a user selects a game in the game list widget.
200 void OnGameListLoadFile(QString game_path); 200 void OnGameListLoadFile(QString game_path);
201 void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); 201 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
202 const std::string& game_path);
202 void OnTransferableShaderCacheOpenFile(u64 program_id); 203 void OnTransferableShaderCacheOpenFile(u64 program_id);
203 void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type); 204 void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type);
204 void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target); 205 void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target);
@@ -216,6 +217,7 @@ private slots:
216 void OnMenuInstallToNAND(); 217 void OnMenuInstallToNAND();
217 void OnMenuRecentFile(); 218 void OnMenuRecentFile();
218 void OnConfigure(); 219 void OnConfigure();
220 void OnConfigurePerGame();
219 void OnLoadAmiibo(); 221 void OnLoadAmiibo();
220 void OnOpenYuzuFolder(); 222 void OnOpenYuzuFolder();
221 void OnAbout(); 223 void OnAbout();
@@ -249,6 +251,7 @@ private:
249 void ShowMouseCursor(); 251 void ShowMouseCursor();
250 void OpenURL(const QUrl& url); 252 void OpenURL(const QUrl& url);
251 void LoadTranslation(); 253 void LoadTranslation();
254 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
252 255
253 Ui::MainWindow ui; 256 Ui::MainWindow ui;
254 257
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index c3a1d715e..87ea985d8 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -81,6 +81,7 @@
81 <addaction name="action_Restart"/> 81 <addaction name="action_Restart"/>
82 <addaction name="separator"/> 82 <addaction name="separator"/>
83 <addaction name="action_Configure"/> 83 <addaction name="action_Configure"/>
84 <addaction name="action_Configure_Current_Game"/>
84 </widget> 85 </widget>
85 <widget class="QMenu" name="menu_View"> 86 <widget class="QMenu" name="menu_View">
86 <property name="title"> 87 <property name="title">
@@ -287,6 +288,14 @@
287 <string>Capture Screenshot</string> 288 <string>Capture Screenshot</string>
288 </property> 289 </property>
289 </action> 290 </action>
291 <action name="action_Configure_Current_Game">
292 <property name="enabled">
293 <bool>false</bool>
294 </property>
295 <property name="text">
296 <string>Configure Current Game..</string>
297 </property>
298 </action>
290 </widget> 299 </widget>
291 <resources/> 300 <resources/>
292 <connections/> 301 <connections/>
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index bbfeafc55..2d2e82f15 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -29,14 +29,14 @@ extern const Themes themes;
29 29
30struct GameDir { 30struct GameDir {
31 QString path; 31 QString path;
32 bool deep_scan; 32 bool deep_scan = false;
33 bool expanded; 33 bool expanded = false;
34 bool operator==(const GameDir& rhs) const { 34 bool operator==(const GameDir& rhs) const {
35 return path == rhs.path; 35 return path == rhs.path;
36 }; 36 }
37 bool operator!=(const GameDir& rhs) const { 37 bool operator!=(const GameDir& rhs) const {
38 return !operator==(rhs); 38 return !operator==(rhs);
39 }; 39 }
40}; 40};
41 41
42struct Values { 42struct Values {
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index c2a2982fb..8a63fd191 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -16,9 +16,11 @@
16#include "yuzu_cmd/config.h" 16#include "yuzu_cmd/config.h"
17#include "yuzu_cmd/default_ini.h" 17#include "yuzu_cmd/default_ini.h"
18 18
19namespace FS = Common::FS;
20
19Config::Config() { 21Config::Config() {
20 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 22 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
21 sdl2_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "sdl2-config.ini"; 23 sdl2_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + "sdl2-config.ini";
22 sdl2_config = std::make_unique<INIReader>(sdl2_config_loc); 24 sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
23 25
24 Reload(); 26 Reload();
@@ -31,8 +33,8 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
31 if (sdl2_config->ParseError() < 0) { 33 if (sdl2_config->ParseError() < 0) {
32 if (retry) { 34 if (retry) {
33 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location); 35 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
34 FileUtil::CreateFullPath(location); 36 FS::CreateFullPath(location);
35 FileUtil::WriteStringToFile(true, location, default_contents); 37 FS::WriteStringToFile(true, location, default_contents);
36 sdl2_config = std::make_unique<INIReader>(location); // Reopen file 38 sdl2_config = std::make_unique<INIReader>(location); // Reopen file
37 39
38 return LoadINI(default_contents, false); 40 return LoadINI(default_contents, false);
@@ -315,21 +317,21 @@ void Config::ReadValues() {
315 // Data Storage 317 // Data Storage
316 Settings::values.use_virtual_sd = 318 Settings::values.use_virtual_sd =
317 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); 319 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
318 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir, 320 FS::GetUserPath(
319 sdl2_config->Get("Data Storage", "nand_directory", 321 FS::UserPath::NANDDir,
320 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); 322 sdl2_config->Get("Data Storage", "nand_directory", FS::GetUserPath(FS::UserPath::NANDDir)));
321 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, 323 FS::GetUserPath(
322 sdl2_config->Get("Data Storage", "sdmc_directory", 324 FS::UserPath::SDMCDir,
323 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 325 sdl2_config->Get("Data Storage", "sdmc_directory", FS::GetUserPath(FS::UserPath::SDMCDir)));
324 FileUtil::GetUserPath(FileUtil::UserPath::LoadDir, 326 FS::GetUserPath(
325 sdl2_config->Get("Data Storage", "load_directory", 327 FS::UserPath::LoadDir,
326 FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))); 328 sdl2_config->Get("Data Storage", "load_directory", FS::GetUserPath(FS::UserPath::LoadDir)));
327 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir, 329 FS::GetUserPath(
328 sdl2_config->Get("Data Storage", "dump_directory", 330 FS::UserPath::DumpDir,
329 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))); 331 sdl2_config->Get("Data Storage", "dump_directory", FS::GetUserPath(FS::UserPath::DumpDir)));
330 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir, 332 FS::GetUserPath(FS::UserPath::CacheDir,
331 sdl2_config->Get("Data Storage", "cache_directory", 333 sdl2_config->Get("Data Storage", "cache_directory",
332 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))); 334 FS::GetUserPath(FS::UserPath::CacheDir)));
333 Settings::values.gamecard_inserted = 335 Settings::values.gamecard_inserted =
334 sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false); 336 sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false);
335 Settings::values.gamecard_current_game = 337 Settings::values.gamecard_current_game =
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 512b060a7..8efe49390 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -82,8 +82,8 @@ static void InitializeLogging() {
82 82
83 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); 83 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
84 84
85 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 85 const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
86 FileUtil::CreateFullPath(log_dir); 86 Common::FS::CreateFullPath(log_dir);
87 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); 87 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
88#ifdef _WIN32 88#ifdef _WIN32
89 Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); 89 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
@@ -229,7 +229,7 @@ int main(int argc, char** argv) {
229 } 229 }
230 } 230 }
231 231
232 system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); 232 system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL");
233 233
234 // Core is loaded, start the GPU (makes the GPU contexts current to this thread) 234 // Core is loaded, start the GPU (makes the GPU contexts current to this thread)
235 system.GPU().Start(); 235 system.GPU().Start();
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
index acb22885e..74022af23 100644
--- a/src/yuzu_tester/config.cpp
+++ b/src/yuzu_tester/config.cpp
@@ -15,10 +15,11 @@
15#include "yuzu_tester/config.h" 15#include "yuzu_tester/config.h"
16#include "yuzu_tester/default_ini.h" 16#include "yuzu_tester/default_ini.h"
17 17
18namespace FS = Common::FS;
19
18Config::Config() { 20Config::Config() {
19 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 21 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
20 sdl2_config_loc = 22 sdl2_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + "sdl2-tester-config.ini";
21 FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "sdl2-tester-config.ini";
22 sdl2_config = std::make_unique<INIReader>(sdl2_config_loc); 23 sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
23 24
24 Reload(); 25 Reload();
@@ -31,8 +32,8 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
31 if (sdl2_config->ParseError() < 0) { 32 if (sdl2_config->ParseError() < 0) {
32 if (retry) { 33 if (retry) {
33 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location); 34 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
34 FileUtil::CreateFullPath(location); 35 FS::CreateFullPath(location);
35 FileUtil::WriteStringToFile(true, default_contents, location); 36 FS::WriteStringToFile(true, default_contents, location);
36 sdl2_config = std::make_unique<INIReader>(location); // Reopen file 37 sdl2_config = std::make_unique<INIReader>(location); // Reopen file
37 38
38 return LoadINI(default_contents, false); 39 return LoadINI(default_contents, false);
@@ -87,12 +88,12 @@ void Config::ReadValues() {
87 // Data Storage 88 // Data Storage
88 Settings::values.use_virtual_sd = 89 Settings::values.use_virtual_sd =
89 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); 90 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
90 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir, 91 FS::GetUserPath(Common::FS::UserPath::NANDDir,
91 sdl2_config->Get("Data Storage", "nand_directory", 92 sdl2_config->Get("Data Storage", "nand_directory",
92 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); 93 Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)));
93 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, 94 FS::GetUserPath(Common::FS::UserPath::SDMCDir,
94 sdl2_config->Get("Data Storage", "sdmc_directory", 95 sdl2_config->Get("Data Storage", "sdmc_directory",
95 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 96 Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)));
96 97
97 // System 98 // System
98 Settings::values.current_user = std::clamp<int>( 99 Settings::values.current_user = std::clamp<int>(
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index 083667baf..7acf0caad 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -79,8 +79,8 @@ static void InitializeLogging(bool console) {
79 if (console) 79 if (console)
80 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); 80 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
81 81
82 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 82 const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
83 FileUtil::CreateFullPath(log_dir); 83 Common::FS::CreateFullPath(log_dir);
84 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); 84 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
85#ifdef _WIN32 85#ifdef _WIN32
86 Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); 86 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
@@ -251,7 +251,8 @@ int main(int argc, char** argv) {
251 251
252 Service::Yuzu::InstallInterfaces(system.ServiceManager(), datastring, callback); 252 Service::Yuzu::InstallInterfaces(system.ServiceManager(), datastring, callback);
253 253
254 system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester"); 254 system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend",
255 "SDLHideTester");
255 256
256 system.GPU().Start(); 257 system.GPU().Start();
257 system.Renderer().Rasterizer().LoadDiskResources(); 258 system.Renderer().Rasterizer().LoadDiskResources();