summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt7
-rw-r--r--src/audio_core/cubeb_sink.cpp2
-rw-r--r--src/common/algorithm.h3
-rw-r--r--src/common/alignment.h14
-rw-r--r--src/common/atomic_ops.cpp37
-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.h34
-rw-r--r--src/common/common_funcs.h14
-rw-r--r--src/common/concepts.h10
-rw-r--r--src/common/detached_tasks.cpp3
-rw-r--r--src/common/dynamic_library.h13
-rw-r--r--src/common/fiber.h2
-rw-r--r--src/common/file_util.cpp64
-rw-r--r--src/common/file_util.h85
-rw-r--r--src/common/hash.h25
-rw-r--r--src/common/hex_util.cpp34
-rw-r--r--src/common/hex_util.h33
-rw-r--r--src/common/logging/backend.cpp22
-rw-r--r--src/common/logging/backend.h16
-rw-r--r--src/common/lz4_compression.cpp16
-rw-r--r--src/common/lz4_compression.h24
-rw-r--r--src/common/math_util.h12
-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.h12
-rw-r--r--src/common/thread_queue_list.h10
-rw-r--r--src/common/threadsafe_queue.h12
-rw-r--r--src/common/time_zone.cpp14
-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.cpp9
-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/core.cpp2
-rw-r--r--src/core/crypto/aes_util.cpp23
-rw-r--r--src/core/crypto/aes_util.h9
-rw-r--r--src/core/crypto/ctr_encryption_layer.cpp9
-rw-r--r--src/core/crypto/ctr_encryption_layer.h9
-rw-r--r--src/core/crypto/key_manager.cpp296
-rw-r--r--src/core/crypto/key_manager.h4
-rw-r--r--src/core/crypto/partition_data_manager.cpp215
-rw-r--r--src/core/file_sys/bis_factory.cpp2
-rw-r--r--src/core/file_sys/content_archive.cpp7
-rw-r--r--src/core/file_sys/nca_patch.cpp3
-rw-r--r--src/core/file_sys/registered_cache.cpp2
-rw-r--r--src/core/file_sys/registered_cache.h6
-rw-r--r--src/core/file_sys/system_archive/mii_model.cpp18
-rw-r--r--src/core/file_sys/system_archive/ng_word.cpp42
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.cpp9
-rw-r--r--src/core/file_sys/vfs.cpp56
-rw-r--r--src/core/file_sys/vfs_libzip.cpp2
-rw-r--r--src/core/file_sys/vfs_real.cpp177
-rw-r--r--src/core/file_sys/vfs_real.h8
-rw-r--r--src/core/file_sys/vfs_vector.h13
-rw-r--r--src/core/file_sys/xts_archive.cpp2
-rw-r--r--src/core/frontend/emu_window.h4
-rw-r--r--src/core/hle/ipc_helpers.h4
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp2
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp46
-rw-r--r--src/core/hle/kernel/memory/page_table.cpp23
-rw-r--r--src/core/hle/kernel/memory/system_control.cpp21
-rw-r--r--src/core/hle/kernel/memory/system_control.h5
-rw-r--r--src/core/hle/kernel/scheduler.cpp31
-rw-r--r--src/core/hle/kernel/scheduler.h2
-rw-r--r--src/core/hle/result.h12
-rw-r--r--src/core/hle/service/acc/acc.cpp8
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp22
-rw-r--r--src/core/hle/service/am/am.cpp6
-rw-r--r--src/core/hle/service/am/am.h7
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp3
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp19
-rw-r--r--src/core/hle/service/audio/audout_u.cpp2
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp26
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp80
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h3
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp21
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h10
-rw-r--r--src/core/hle/service/sm/sm.h2
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp4
-rw-r--r--src/core/loader/loader.cpp54
-rw-r--r--src/core/memory.cpp10
-rw-r--r--src/core/memory/cheat_engine.cpp26
-rw-r--r--src/core/perf_stats.cpp4
-rw-r--r--src/core/reporter.cpp9
-rw-r--r--src/core/settings.cpp4
-rw-r--r--src/core/telemetry_session.cpp12
-rw-r--r--src/core/tools/freezer.cpp31
-rw-r--r--src/core/tools/freezer.h7
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp14
-rw-r--r--src/input_common/sdl/sdl_impl.cpp25
-rw-r--r--src/input_common/udp/client.cpp6
-rw-r--r--src/video_core/engines/maxwell_3d.h2
-rw-r--r--src/video_core/engines/maxwell_dma.cpp21
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp34
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.h10
-rw-r--r--src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp14
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp2
-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.cpp26
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h10
-rw-r--r--src/video_core/renderer_vulkan/wrapper.cpp2
-rw-r--r--src/video_core/renderer_vulkan/wrapper.h30
-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/shader/control_flow.cpp30
-rw-r--r--src/video_core/shader/decode/memory.cpp6
-rw-r--r--src/video_core/texture_cache/surface_params.cpp1
-rw-r--r--src/video_core/textures/decoders.cpp42
-rw-r--r--src/video_core/textures/decoders.h5
-rw-r--r--src/yuzu/applets/profile_select.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp101
-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_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_input_player.cpp61
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.cpp11
-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.cpp2
-rw-r--r--src/yuzu/game_list_worker.cpp30
-rw-r--r--src/yuzu/main.cpp117
-rw-r--r--src/yuzu/main.h2
-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.cpp4
-rw-r--r--src/yuzu_tester/config.cpp21
-rw-r--r--src/yuzu_tester/yuzu.cpp4
161 files changed, 1950 insertions, 1682 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 54dca3302..71efbb40d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -60,9 +60,14 @@ else()
60 -Wmissing-declarations 60 -Wmissing-declarations
61 -Wno-attributes 61 -Wno-attributes
62 -Wno-unused-parameter 62 -Wno-unused-parameter
63 -fconcepts
64 ) 63 )
65 64
65 # TODO: Remove when we update to a GCC compiler that enables this
66 # by default (i.e. GCC 10 or newer).
67 if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
68 add_compile_options(-fconcepts)
69 endif()
70
66 if (ARCHITECTURE_x86_64) 71 if (ARCHITECTURE_x86_64)
67 add_compile_options("-mcx16") 72 add_compile_options("-mcx16")
68 endif() 73 endif()
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 41bf5cd4d..c27df946c 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -78,7 +78,7 @@ public:
78 const s16 surround_left{samples[i + 4]}; 78 const s16 surround_left{samples[i + 4]};
79 const s16 surround_right{samples[i + 5]}; 79 const s16 surround_right{samples[i + 5]};
80 // Not used in the ATSC reference implementation 80 // Not used in the ATSC reference implementation
81 [[maybe_unused]] const s16 low_frequency_effects { samples[i + 3] }; 81 [[maybe_unused]] const s16 low_frequency_effects{samples[i + 3]};
82 82
83 constexpr s32 clev{707}; // center mixing level coefficient 83 constexpr s32 clev{707}; // center mixing level coefficient
84 constexpr s32 slev{707}; // surround mixing level coefficient 84 constexpr s32 slev{707}; // surround mixing level coefficient
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.cpp b/src/common/atomic_ops.cpp
index 1098e21ff..1612d0e67 100644
--- a/src/common/atomic_ops.cpp
+++ b/src/common/atomic_ops.cpp
@@ -14,50 +14,55 @@ namespace Common {
14 14
15#if _MSC_VER 15#if _MSC_VER
16 16
17bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { 17bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
18 u8 result = _InterlockedCompareExchange8((char*)pointer, value, expected); 18 const u8 result =
19 _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
19 return result == expected; 20 return result == expected;
20} 21}
21 22
22bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { 23bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
23 u16 result = _InterlockedCompareExchange16((short*)pointer, value, expected); 24 const u16 result =
25 _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
24 return result == expected; 26 return result == expected;
25} 27}
26 28
27bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { 29bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
28 u32 result = _InterlockedCompareExchange((long*)pointer, value, expected); 30 const u32 result =
31 _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
29 return result == expected; 32 return result == expected;
30} 33}
31 34
32bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { 35bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
33 u64 result = _InterlockedCompareExchange64((__int64*)pointer, value, expected); 36 const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
37 value, expected);
34 return result == expected; 38 return result == expected;
35} 39}
36 40
37bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { 41bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
38 return _InterlockedCompareExchange128((__int64*)pointer, value[1], value[0], 42 return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
39 (__int64*)expected.data()) != 0; 43 value[0],
44 reinterpret_cast<__int64*>(expected.data())) != 0;
40} 45}
41 46
42#else 47#else
43 48
44bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { 49bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
45 return __sync_bool_compare_and_swap(pointer, expected, value); 50 return __sync_bool_compare_and_swap(pointer, expected, value);
46} 51}
47 52
48bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { 53bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
49 return __sync_bool_compare_and_swap(pointer, expected, value); 54 return __sync_bool_compare_and_swap(pointer, expected, value);
50} 55}
51 56
52bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { 57bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
53 return __sync_bool_compare_and_swap(pointer, expected, value); 58 return __sync_bool_compare_and_swap(pointer, expected, value);
54} 59}
55 60
56bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { 61bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
57 return __sync_bool_compare_and_swap(pointer, expected, value); 62 return __sync_bool_compare_and_swap(pointer, expected, value);
58} 63}
59 64
60bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { 65bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
61 unsigned __int128 value_a; 66 unsigned __int128 value_a;
62 unsigned __int128 expected_a; 67 unsigned __int128 expected_a;
63 std::memcpy(&value_a, value.data(), sizeof(u128)); 68 std::memcpy(&value_a, value.data(), sizeof(u128));
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h
index e6181d521..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(u8 volatile* pointer, u8 value, u8 expected); 11[[nodiscard]] bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected);
12bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected); 12[[nodiscard]] bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected);
13bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected); 13[[nodiscard]] bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected);
14bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected); 14[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected);
15bool AtomicCompareAndSwap(u64 volatile* 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..381d6332e 100644
--- a/src/common/color.h
+++ b/src/common/color.h
@@ -13,42 +13,42 @@
13namespace Color { 13namespace 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
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 db5fb373d..54252e778 100644
--- a/src/common/concepts.h
+++ b/src/common/concepts.h
@@ -23,10 +23,12 @@ concept IsSTLContainer = requires(T t) {
23 t.size(); 23 t.size();
24}; 24};
25 25
26// Check if type T is derived from T2 26// TODO: Replace with std::derived_from when the <concepts> header
27template <typename T, typename T2> 27// is available on all supported platforms.
28concept IsBaseOf = requires { 28template <typename Derived, typename Base>
29 std::is_base_of_v<T, T2>; 29concept DerivedFrom = requires {
30 std::is_base_of_v<Base, Derived>;
31 std::is_convertible_v<const volatile Derived*, const volatile Base*>;
30}; 32};
31 33
32} // namespace Common 34} // namespace Common
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp
index f268d6021..f2b4939df 100644
--- a/src/common/detached_tasks.cpp
+++ b/src/common/detached_tasks.cpp
@@ -34,8 +34,7 @@ void DetachedTasks::AddTask(std::function<void()> task) {
34 std::unique_lock lock{instance->mutex}; 34 std::unique_lock lock{instance->mutex};
35 --instance->count; 35 --instance->count;
36 std::notify_all_at_thread_exit(instance->cv, std::move(lock)); 36 std::notify_all_at_thread_exit(instance->cv, std::move(lock));
37 }) 37 }).detach();
38 .detach();
39} 38}
40 39
41} // namespace Common 40} // namespace Common
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..c869e7b82 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 {
@@ -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.cpp b/src/common/hex_util.cpp
index c2f6cf0f6..74f52dd11 100644
--- a/src/common/hex_util.cpp
+++ b/src/common/hex_util.cpp
@@ -3,21 +3,9 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/hex_util.h" 5#include "common/hex_util.h"
6#include "common/logging/log.h"
7 6
8namespace Common { 7namespace Common {
9 8
10u8 ToHexNibble(char c1) {
11 if (c1 >= 65 && c1 <= 70)
12 return c1 - 55;
13 if (c1 >= 97 && c1 <= 102)
14 return c1 - 87;
15 if (c1 >= 48 && c1 <= 57)
16 return c1 - 48;
17 LOG_ERROR(Common, "Invalid hex digit: 0x{:02X}", c1);
18 return 0;
19}
20
21std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) { 9std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
22 std::vector<u8> out(str.size() / 2); 10 std::vector<u8> out(str.size() / 2);
23 if (little_endian) { 11 if (little_endian) {
@@ -30,26 +18,4 @@ std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
30 return out; 18 return out;
31} 19}
32 20
33std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
34 if (len != 32) {
35 LOG_ERROR(Common,
36 "Attempting to parse string to array that is not of correct size (expected=32, "
37 "actual={}).",
38 len);
39 return {};
40 }
41 return HexStringToArray<16>(str);
42}
43
44std::array<u8, 32> operator""_array32(const char* str, std::size_t len) {
45 if (len != 64) {
46 LOG_ERROR(Common,
47 "Attempting to parse string to array that is not of correct size (expected=64, "
48 "actual={}).",
49 len);
50 return {};
51 }
52 return HexStringToArray<32>(str);
53}
54
55} // namespace Common 21} // namespace Common
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index bb4736f96..120f1a5e6 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -14,25 +14,37 @@
14 14
15namespace Common { 15namespace Common {
16 16
17u8 ToHexNibble(char c1); 17[[nodiscard]] constexpr u8 ToHexNibble(char c) {
18 if (c >= 65 && c <= 70) {
19 return c - 55;
20 }
21
22 if (c >= 97 && c <= 102) {
23 return c - 87;
24 }
25
26 return c - 48;
27}
18 28
19std::vector<u8> HexStringToVector(std::string_view str, bool little_endian); 29[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
20 30
21template <std::size_t Size, bool le = false> 31template <std::size_t Size, bool le = false>
22std::array<u8, Size> HexStringToArray(std::string_view str) { 32[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
23 std::array<u8, Size> out{}; 33 std::array<u8, Size> out{};
24 if constexpr (le) { 34 if constexpr (le) {
25 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) {
26 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); 36 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
37 }
27 } else { 38 } else {
28 for (std::size_t i = 0; i < 2 * Size; i += 2) 39 for (std::size_t i = 0; i < 2 * Size; i += 2) {
29 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); 40 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
41 }
30 } 42 }
31 return out; 43 return out;
32} 44}
33 45
34template <typename ContiguousContainer> 46template <typename ContiguousContainer>
35std::string HexToString(const ContiguousContainer& data, bool upper = true) { 47[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
36 static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>, 48 static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
37 "Underlying type within the contiguous container must be u8."); 49 "Underlying type within the contiguous container must be u8.");
38 50
@@ -48,7 +60,12 @@ std::string HexToString(const ContiguousContainer& data, bool upper = true) {
48 return out; 60 return out;
49} 61}
50 62
51std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len); 63[[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[17]) {
52std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len); 64 return HexStringToArray<16>(data);
65}
66
67[[nodiscard]] constexpr std::array<u8, 32> AsArray(const char (&data)[65]) {
68 return HexStringToArray<32>(data);
69}
53 70
54} // namespace Common 71} // namespace Common
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 04bc3128f..62cfde397 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -113,19 +113,19 @@ private:
113 Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, 113 Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
114 const char* function, std::string message) const { 114 const char* function, std::string message) const {
115 using std::chrono::duration_cast; 115 using std::chrono::duration_cast;
116 using std::chrono::microseconds;
116 using std::chrono::steady_clock; 117 using std::chrono::steady_clock;
117 118
118 Entry entry; 119 return {
119 entry.timestamp = 120 .timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
120 duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); 121 .log_class = log_class,
121 entry.log_class = log_class; 122 .log_level = log_level,
122 entry.log_level = log_level; 123 .filename = filename,
123 entry.filename = filename; 124 .line_num = line_nr,
124 entry.line_num = line_nr; 125 .function = function,
125 entry.function = function; 126 .message = std::move(message),
126 entry.message = std::move(message); 127 .final_entry = false,
127 128 };
128 return entry;
129 } 129 }
130 130
131 std::mutex writing_mutex; 131 std::mutex writing_mutex;
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index fc338c70d..da1c2f185 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -21,19 +21,13 @@ class Filter;
21 */ 21 */
22struct Entry { 22struct Entry {
23 std::chrono::microseconds timestamp; 23 std::chrono::microseconds timestamp;
24 Class log_class; 24 Class log_class{};
25 Level log_level; 25 Level log_level{};
26 const char* filename; 26 const char* filename = nullptr;
27 unsigned int line_num; 27 unsigned int line_num = 0;
28 std::string function; 28 std::string function;
29 std::string message; 29 std::string message;
30 bool final_entry = false; 30 bool final_entry = false;
31
32 Entry() = default;
33 Entry(Entry&& o) = default;
34
35 Entry& operator=(Entry&& o) = default;
36 Entry& operator=(const Entry& o) = default;
37}; 31};
38 32
39/** 33/**
@@ -100,7 +94,7 @@ public:
100 void Write(const Entry& entry) override; 94 void Write(const Entry& entry) override;
101 95
102private: 96private:
103 FileUtil::IOFile file; 97 Common::FS::IOFile file;
104 std::size_t bytes_written; 98 std::size_t bytes_written;
105}; 99};
106 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 83ef0201f..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,21 +39,21 @@ 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 }
54}; 54};
55 55
56template <typename T> 56template <typename T>
57Rectangle(T, T, T, T)->Rectangle<T>; 57Rectangle(T, T, T, T) -> Rectangle<T>;
58 58
59} // namespace Common 59} // namespace Common
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.h b/src/common/telemetry.h
index 854a73fae..4aa299f9a 100644
--- a/src/common/telemetry.h
+++ b/src/common/telemetry.h
@@ -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:
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.cpp b/src/common/time_zone.cpp
index ce239eb63..7aa1b59ea 100644
--- a/src/common/time_zone.cpp
+++ b/src/common/time_zone.cpp
@@ -3,8 +3,9 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono> 5#include <chrono>
6#include <iomanip> 6#include <ctime>
7#include <sstream> 7
8#include <fmt/chrono.h>
8 9
9#include "common/logging/log.h" 10#include "common/logging/log.h"
10#include "common/time_zone.h" 11#include "common/time_zone.h"
@@ -16,13 +17,8 @@ std::string GetDefaultTimeZone() {
16} 17}
17 18
18static std::string GetOsTimeZoneOffset() { 19static std::string GetOsTimeZoneOffset() {
19 const std::time_t t{std::time(nullptr)}; 20 // Get the current timezone offset, e.g. "-400", as a string
20 const std::tm tm{*std::localtime(&t)}; 21 return fmt::format("%z", fmt::localtime(std::time(nullptr)));
21
22 std::stringstream ss;
23 ss << std::put_time(&tm, "%z"); // Get the current timezone offset, e.g. "-400", as a string
24
25 return ss.str();
26} 22}
27 23
28static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) { 24static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) {
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.cpp b/src/common/virtual_buffer.cpp
index be5b67752..b009cb500 100644
--- a/src/common/virtual_buffer.cpp
+++ b/src/common/virtual_buffer.cpp
@@ -5,16 +5,7 @@
5#ifdef _WIN32 5#ifdef _WIN32
6#include <windows.h> 6#include <windows.h>
7#else 7#else
8#include <stdio.h>
9#include <sys/mman.h> 8#include <sys/mman.h>
10#include <sys/types.h>
11#if defined __APPLE__ || defined __FreeBSD__ || defined __OpenBSD__
12#include <sys/sysctl.h>
13#elif defined __HAIKU__
14#include <OS.h>
15#else
16#include <sys/sysinfo.h>
17#endif
18#endif 9#endif
19 10
20#include "common/assert.h" 11#include "common/assert.h"
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/core.cpp b/src/core/core.cpp
index 42277e2cd..1d8c0f1cd 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);
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp
index 4be76bb43..6a9734812 100644
--- a/src/core/crypto/aes_util.cpp
+++ b/src/core/crypto/aes_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 <array>
5#include <mbedtls/cipher.h> 6#include <mbedtls/cipher.h>
6#include "common/assert.h" 7#include "common/assert.h"
7#include "common/logging/log.h" 8#include "common/logging/log.h"
@@ -10,8 +11,10 @@
10 11
11namespace Core::Crypto { 12namespace Core::Crypto {
12namespace { 13namespace {
13std::vector<u8> CalculateNintendoTweak(std::size_t sector_id) { 14using NintendoTweak = std::array<u8, 16>;
14 std::vector<u8> out(0x10); 15
16NintendoTweak CalculateNintendoTweak(std::size_t sector_id) {
17 NintendoTweak out{};
15 for (std::size_t i = 0xF; i <= 0xF; --i) { 18 for (std::size_t i = 0xF; i <= 0xF; --i) {
16 out[i] = sector_id & 0xFF; 19 out[i] = sector_id & 0xFF;
17 sector_id >>= 8; 20 sector_id >>= 8;
@@ -64,13 +67,6 @@ AESCipher<Key, KeySize>::~AESCipher() {
64} 67}
65 68
66template <typename Key, std::size_t KeySize> 69template <typename Key, std::size_t KeySize>
67void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) {
68 ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) ||
69 mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0,
70 "Failed to set IV on mbedtls ciphers.");
71}
72
73template <typename Key, std::size_t KeySize>
74void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const { 70void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const {
75 auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context; 71 auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
76 72
@@ -120,10 +116,17 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8*
120 116
121 for (std::size_t i = 0; i < size; i += sector_size) { 117 for (std::size_t i = 0; i < size; i += sector_size) {
122 SetIV(CalculateNintendoTweak(sector_id++)); 118 SetIV(CalculateNintendoTweak(sector_id++));
123 Transcode<u8, u8>(src + i, sector_size, dest + i, op); 119 Transcode(src + i, sector_size, dest + i, op);
124 } 120 }
125} 121}
126 122
123template <typename Key, std::size_t KeySize>
124void AESCipher<Key, KeySize>::SetIVImpl(const u8* data, std::size_t size) {
125 ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, data, size) ||
126 mbedtls_cipher_set_iv(&ctx->decryption_context, data, size)) == 0,
127 "Failed to set IV on mbedtls ciphers.");
128}
129
127template class AESCipher<Key128>; 130template class AESCipher<Key128>;
128template class AESCipher<Key256>; 131template class AESCipher<Key256>;
129} // namespace Core::Crypto 132} // namespace Core::Crypto
diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h
index edc4ab910..e2a304186 100644
--- a/src/core/crypto/aes_util.h
+++ b/src/core/crypto/aes_util.h
@@ -6,7 +6,6 @@
6 6
7#include <memory> 7#include <memory>
8#include <type_traits> 8#include <type_traits>
9#include <vector>
10#include "common/common_types.h" 9#include "common/common_types.h"
11#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs.h"
12 11
@@ -32,10 +31,12 @@ class AESCipher {
32 31
33public: 32public:
34 AESCipher(Key key, Mode mode); 33 AESCipher(Key key, Mode mode);
35
36 ~AESCipher(); 34 ~AESCipher();
37 35
38 void SetIV(std::vector<u8> iv); 36 template <typename ContiguousContainer>
37 void SetIV(const ContiguousContainer& container) {
38 SetIVImpl(std::data(container), std::size(container));
39 }
39 40
40 template <typename Source, typename Dest> 41 template <typename Source, typename Dest>
41 void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const { 42 void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const {
@@ -59,6 +60,8 @@ public:
59 std::size_t sector_size, Op op); 60 std::size_t sector_size, Op op);
60 61
61private: 62private:
63 void SetIVImpl(const u8* data, std::size_t size);
64
62 std::unique_ptr<CipherContext> ctx; 65 std::unique_ptr<CipherContext> ctx;
63}; 66};
64} // namespace Core::Crypto 67} // namespace Core::Crypto
diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp
index 902841c77..5c84bb0a4 100644
--- a/src/core/crypto/ctr_encryption_layer.cpp
+++ b/src/core/crypto/ctr_encryption_layer.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 <algorithm>
5#include <cstring> 6#include <cstring>
6#include "common/assert.h" 7#include "common/assert.h"
7#include "core/crypto/ctr_encryption_layer.h" 8#include "core/crypto/ctr_encryption_layer.h"
@@ -10,8 +11,7 @@ namespace Core::Crypto {
10 11
11CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, 12CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_,
12 std::size_t base_offset) 13 std::size_t base_offset)
13 : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR), 14 : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR) {}
14 iv(16, 0) {}
15 15
16std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const { 16std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const {
17 if (length == 0) 17 if (length == 0)
@@ -39,9 +39,8 @@ std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t o
39 return read + Read(data + read, length - read, offset + read); 39 return read + Read(data + read, length - read, offset + read);
40} 40}
41 41
42void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) { 42void CTREncryptionLayer::SetIV(const IVData& iv_) {
43 const auto length = std::min(iv_.size(), iv.size()); 43 iv = iv_;
44 iv.assign(iv_.cbegin(), iv_.cbegin() + length);
45} 44}
46 45
47void CTREncryptionLayer::UpdateIV(std::size_t offset) const { 46void CTREncryptionLayer::UpdateIV(std::size_t offset) const {
diff --git a/src/core/crypto/ctr_encryption_layer.h b/src/core/crypto/ctr_encryption_layer.h
index a7bf810f4..a2429f001 100644
--- a/src/core/crypto/ctr_encryption_layer.h
+++ b/src/core/crypto/ctr_encryption_layer.h
@@ -4,7 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <vector> 7#include <array>
8
8#include "core/crypto/aes_util.h" 9#include "core/crypto/aes_util.h"
9#include "core/crypto/encryption_layer.h" 10#include "core/crypto/encryption_layer.h"
10#include "core/crypto/key_manager.h" 11#include "core/crypto/key_manager.h"
@@ -14,18 +15,20 @@ namespace Core::Crypto {
14// Sits on top of a VirtualFile and provides CTR-mode AES decription. 15// Sits on top of a VirtualFile and provides CTR-mode AES decription.
15class CTREncryptionLayer : public EncryptionLayer { 16class CTREncryptionLayer : public EncryptionLayer {
16public: 17public:
18 using IVData = std::array<u8, 16>;
19
17 CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset); 20 CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset);
18 21
19 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; 22 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
20 23
21 void SetIV(const std::vector<u8>& iv); 24 void SetIV(const IVData& iv);
22 25
23private: 26private:
24 std::size_t base_offset; 27 std::size_t base_offset;
25 28
26 // Must be mutable as operations modify cipher contexts. 29 // Must be mutable as operations modify cipher contexts.
27 mutable AESCipher<Key128> cipher; 30 mutable AESCipher<Key128> cipher;
28 mutable std::vector<u8> iv; 31 mutable IVData iv{};
29 32
30 void UpdateIV(std::size_t offset) const; 33 void UpdateIV(std::size_t offset) const;
31}; 34};
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index f87fe0abc..8783d1ac2 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -40,12 +40,14 @@ namespace Core::Crypto {
40constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; 40constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
41constexpr u64 FULL_TICKET_SIZE = 0x400; 41constexpr u64 FULL_TICKET_SIZE = 0x400;
42 42
43using namespace Common; 43using Common::AsArray;
44 44
45const std::array<SHA256Hash, 2> eticket_source_hashes{ 45// clang-format off
46 "B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source 46constexpr std::array eticket_source_hashes{
47 "E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source 47 AsArray("B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"), // eticket_rsa_kek_source
48 AsArray("E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"), // eticket_rsa_kekek_source
48}; 49};
50// clang-format on
49 51
50const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ 52const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
51 {{S128KeyType::Master, 0}, "master_key_"}, 53 {{S128KeyType::Master, 0}, "master_key_"},
@@ -94,13 +96,13 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
94} 96}
95 97
96SignatureType Ticket::GetSignatureType() const { 98SignatureType Ticket::GetSignatureType() const {
97 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 99 if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
98 return ticket->sig_type; 100 return ticket->sig_type;
99 } 101 }
100 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 102 if (const auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
101 return ticket->sig_type; 103 return ticket->sig_type;
102 } 104 }
103 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 105 if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
104 return ticket->sig_type; 106 return ticket->sig_type;
105 } 107 }
106 108
@@ -108,13 +110,13 @@ SignatureType Ticket::GetSignatureType() const {
108} 110}
109 111
110TicketData& Ticket::GetData() { 112TicketData& Ticket::GetData() {
111 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 113 if (auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
112 return ticket->data; 114 return ticket->data;
113 } 115 }
114 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 116 if (auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
115 return ticket->data; 117 return ticket->data;
116 } 118 }
117 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 119 if (auto* ticket = std::get_if<ECDSATicket>(&data)) {
118 return ticket->data; 120 return ticket->data;
119 } 121 }
120 122
@@ -122,13 +124,13 @@ TicketData& Ticket::GetData() {
122} 124}
123 125
124const TicketData& Ticket::GetData() const { 126const TicketData& Ticket::GetData() const {
125 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 127 if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
126 return ticket->data; 128 return ticket->data;
127 } 129 }
128 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 130 if (const auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
129 return ticket->data; 131 return ticket->data;
130 } 132 }
131 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 133 if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
132 return ticket->data; 134 return ticket->data;
133 } 135 }
134 136
@@ -231,8 +233,9 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
231} 233}
232 234
233RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { 235RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const {
234 if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) 236 if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) {
235 return {}; 237 return {};
238 }
236 239
237 const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); 240 const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek);
238 241
@@ -259,27 +262,30 @@ Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source)
259} 262}
260 263
261std::optional<Key128> DeriveSDSeed() { 264std::optional<Key128> DeriveSDSeed() {
262 const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 265 const Common::FS::IOFile save_43(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
263 "/system/save/8000000000000043", 266 "/system/save/8000000000000043",
264 "rb+"); 267 "rb+");
265 if (!save_43.IsOpen()) 268 if (!save_43.IsOpen()) {
266 return {}; 269 return std::nullopt;
270 }
267 271
268 const FileUtil::IOFile sd_private( 272 const Common::FS::IOFile sd_private(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
269 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); 273 "/Nintendo/Contents/private",
270 if (!sd_private.IsOpen()) 274 "rb+");
271 return {}; 275 if (!sd_private.IsOpen()) {
276 return std::nullopt;
277 }
272 278
273 std::array<u8, 0x10> private_seed{}; 279 std::array<u8, 0x10> private_seed{};
274 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) { 280 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
275 return {}; 281 return std::nullopt;
276 } 282 }
277 283
278 std::array<u8, 0x10> buffer{}; 284 std::array<u8, 0x10> buffer{};
279 std::size_t offset = 0; 285 std::size_t offset = 0;
280 for (; offset + 0x10 < save_43.GetSize(); ++offset) { 286 for (; offset + 0x10 < save_43.GetSize(); ++offset) {
281 if (!save_43.Seek(offset, SEEK_SET)) { 287 if (!save_43.Seek(offset, SEEK_SET)) {
282 return {}; 288 return std::nullopt;
283 } 289 }
284 290
285 save_43.ReadBytes(buffer.data(), buffer.size()); 291 save_43.ReadBytes(buffer.data(), buffer.size());
@@ -289,23 +295,26 @@ std::optional<Key128> DeriveSDSeed() {
289 } 295 }
290 296
291 if (!save_43.Seek(offset + 0x10, SEEK_SET)) { 297 if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
292 return {}; 298 return std::nullopt;
293 } 299 }
294 300
295 Key128 seed{}; 301 Key128 seed{};
296 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) { 302 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
297 return {}; 303 return std::nullopt;
298 } 304 }
299 return seed; 305 return seed;
300} 306}
301 307
302Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) { 308Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) {
303 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek))) 309 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek))) {
304 return Loader::ResultStatus::ErrorMissingSDKEKSource; 310 return Loader::ResultStatus::ErrorMissingSDKEKSource;
305 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration))) 311 }
312 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration))) {
306 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource; 313 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource;
307 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) 314 }
315 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) {
308 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource; 316 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource;
317 }
309 318
310 const auto sd_kek_source = 319 const auto sd_kek_source =
311 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek)); 320 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek));
@@ -318,14 +327,17 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
318 GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen); 327 GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen);
319 keys.SetKey(S128KeyType::SDKek, sd_kek); 328 keys.SetKey(S128KeyType::SDKek, sd_kek);
320 329
321 if (!keys.HasKey(S128KeyType::SDSeed)) 330 if (!keys.HasKey(S128KeyType::SDSeed)) {
322 return Loader::ResultStatus::ErrorMissingSDSeed; 331 return Loader::ResultStatus::ErrorMissingSDSeed;
332 }
323 const auto sd_seed = keys.GetKey(S128KeyType::SDSeed); 333 const auto sd_seed = keys.GetKey(S128KeyType::SDSeed);
324 334
325 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save))) 335 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save))) {
326 return Loader::ResultStatus::ErrorMissingSDSaveKeySource; 336 return Loader::ResultStatus::ErrorMissingSDSaveKeySource;
327 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA))) 337 }
338 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA))) {
328 return Loader::ResultStatus::ErrorMissingSDNCAKeySource; 339 return Loader::ResultStatus::ErrorMissingSDNCAKeySource;
340 }
329 341
330 std::array<Key256, 2> sd_key_sources{ 342 std::array<Key256, 2> sd_key_sources{
331 keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)), 343 keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)),
@@ -334,8 +346,9 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
334 346
335 // Combine sources and seed 347 // Combine sources and seed
336 for (auto& source : sd_key_sources) { 348 for (auto& source : sd_key_sources) {
337 for (std::size_t i = 0; i < source.size(); ++i) 349 for (std::size_t i = 0; i < source.size(); ++i) {
338 source[i] ^= sd_seed[i & 0xF]; 350 source[i] ^= sd_seed[i & 0xF];
351 }
339 } 352 }
340 353
341 AESCipher<Key128> cipher(sd_kek, Mode::ECB); 354 AESCipher<Key128> cipher(sd_kek, Mode::ECB);
@@ -353,9 +366,10 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
353 return Loader::ResultStatus::Success; 366 return Loader::ResultStatus::Success;
354} 367}
355 368
356std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save) { 369std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save) {
357 if (!ticket_save.IsOpen()) 370 if (!ticket_save.IsOpen()) {
358 return {}; 371 return {};
372 }
359 373
360 std::vector<u8> buffer(ticket_save.GetSize()); 374 std::vector<u8> buffer(ticket_save.GetSize());
361 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) { 375 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
@@ -415,7 +429,7 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
415 offset = i + 1; 429 offset = i + 1;
416 break; 430 break;
417 } else if (data[i] != 0x0) { 431 } else if (data[i] != 0x0) {
418 return {}; 432 return std::nullopt;
419 } 433 }
420 } 434 }
421 435
@@ -425,16 +439,18 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
425std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, 439std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
426 const RSAKeyPair<2048>& key) { 440 const RSAKeyPair<2048>& key) {
427 const auto issuer = ticket.GetData().issuer; 441 const auto issuer = ticket.GetData().issuer;
428 if (IsAllZeroArray(issuer)) 442 if (IsAllZeroArray(issuer)) {
429 return {}; 443 return std::nullopt;
444 }
430 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { 445 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') {
431 LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); 446 LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority.");
432 } 447 }
433 448
434 Key128 rights_id = ticket.GetData().rights_id; 449 Key128 rights_id = ticket.GetData().rights_id;
435 450
436 if (rights_id == Key128{}) 451 if (rights_id == Key128{}) {
437 return {}; 452 return std::nullopt;
453 }
438 454
439 if (!std::any_of(ticket.GetData().title_key_common_pad.begin(), 455 if (!std::any_of(ticket.GetData().title_key_common_pad.begin(),
440 ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) { 456 ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) {
@@ -466,15 +482,17 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
466 std::array<u8, 0xDF> m_2; 482 std::array<u8, 0xDF> m_2;
467 std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size()); 483 std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
468 484
469 if (m_0 != 0) 485 if (m_0 != 0) {
470 return {}; 486 return std::nullopt;
487 }
471 488
472 m_1 = m_1 ^ MGF1<0x20>(m_2); 489 m_1 = m_1 ^ MGF1<0x20>(m_2);
473 m_2 = m_2 ^ MGF1<0xDF>(m_1); 490 m_2 = m_2 ^ MGF1<0xDF>(m_1);
474 491
475 const auto offset = FindTicketOffset(m_2); 492 const auto offset = FindTicketOffset(m_2);
476 if (!offset) 493 if (!offset) {
477 return {}; 494 return std::nullopt;
495 }
478 ASSERT(*offset > 0); 496 ASSERT(*offset > 0);
479 497
480 Key128 key_temp{}; 498 Key128 key_temp{};
@@ -485,8 +503,8 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
485 503
486KeyManager::KeyManager() { 504KeyManager::KeyManager() {
487 // Initialize keys 505 // Initialize keys
488 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); 506 const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
489 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 507 const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
490 if (Settings::values.use_dev_keys) { 508 if (Settings::values.use_dev_keys) {
491 dev_mode = true; 509 dev_mode = true;
492 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false); 510 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);
@@ -504,34 +522,39 @@ KeyManager::KeyManager() {
504} 522}
505 523
506static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) { 524static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
507 if (base.size() < begin + length) 525 if (base.size() < begin + length) {
508 return false; 526 return false;
527 }
509 return std::all_of(base.begin() + begin, base.begin() + begin + length, 528 return std::all_of(base.begin() + begin, base.begin() + begin + length,
510 [](u8 c) { return std::isxdigit(c); }); 529 [](u8 c) { return std::isxdigit(c); });
511} 530}
512 531
513void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { 532void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
514 std::ifstream file; 533 std::ifstream file;
515 OpenFStream(file, filename, std::ios_base::in); 534 Common::FS::OpenFStream(file, filename, std::ios_base::in);
516 if (!file.is_open()) 535 if (!file.is_open()) {
517 return; 536 return;
537 }
518 538
519 std::string line; 539 std::string line;
520 while (std::getline(file, line)) { 540 while (std::getline(file, line)) {
521 std::vector<std::string> out; 541 std::vector<std::string> out;
522 std::stringstream stream(line); 542 std::stringstream stream(line);
523 std::string item; 543 std::string item;
524 while (std::getline(stream, item, '=')) 544 while (std::getline(stream, item, '=')) {
525 out.push_back(std::move(item)); 545 out.push_back(std::move(item));
546 }
526 547
527 if (out.size() != 2) 548 if (out.size() != 2) {
528 continue; 549 continue;
550 }
529 551
530 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end()); 552 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
531 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end()); 553 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
532 554
533 if (out[0].compare(0, 1, "#") == 0) 555 if (out[0].compare(0, 1, "#") == 0) {
534 continue; 556 continue;
557 }
535 558
536 if (is_title_keys) { 559 if (is_title_keys) {
537 auto rights_id_raw = Common::HexStringToArray<16>(out[0]); 560 auto rights_id_raw = Common::HexStringToArray<16>(out[0]);
@@ -551,14 +574,16 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
551 s256_keys[{index.type, index.field1, index.field2}] = key; 574 s256_keys[{index.type, index.field1, index.field2}] = key;
552 } else if (out[0].compare(0, 8, "keyblob_") == 0 && 575 } else if (out[0].compare(0, 8, "keyblob_") == 0 &&
553 out[0].compare(0, 9, "keyblob_k") != 0) { 576 out[0].compare(0, 9, "keyblob_k") != 0) {
554 if (!ValidCryptoRevisionString(out[0], 8, 2)) 577 if (!ValidCryptoRevisionString(out[0], 8, 2)) {
555 continue; 578 continue;
579 }
556 580
557 const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16); 581 const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16);
558 keyblobs[index] = Common::HexStringToArray<0x90>(out[1]); 582 keyblobs[index] = Common::HexStringToArray<0x90>(out[1]);
559 } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) { 583 } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) {
560 if (!ValidCryptoRevisionString(out[0], 18, 2)) 584 if (!ValidCryptoRevisionString(out[0], 18, 2)) {
561 continue; 585 continue;
586 }
562 587
563 const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16); 588 const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16);
564 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); 589 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
@@ -566,8 +591,9 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
566 eticket_extended_kek = Common::HexStringToArray<576>(out[1]); 591 eticket_extended_kek = Common::HexStringToArray<576>(out[1]);
567 } else { 592 } else {
568 for (const auto& kv : KEYS_VARIABLE_LENGTH) { 593 for (const auto& kv : KEYS_VARIABLE_LENGTH) {
569 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) 594 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) {
570 continue; 595 continue;
596 }
571 if (out[0].compare(0, kv.second.size(), kv.second) == 0) { 597 if (out[0].compare(0, kv.second.size(), kv.second) == 0) {
572 const auto index = 598 const auto index =
573 std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16); 599 std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16);
@@ -602,10 +628,11 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
602 628
603void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, 629void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
604 const std::string& filename, bool title) { 630 const std::string& filename, bool title) {
605 if (FileUtil::Exists(dir1 + DIR_SEP + filename)) 631 if (Common::FS::Exists(dir1 + DIR_SEP + filename)) {
606 LoadFromFile(dir1 + DIR_SEP + filename, title); 632 LoadFromFile(dir1 + DIR_SEP + filename, title);
607 else if (FileUtil::Exists(dir2 + DIR_SEP + filename)) 633 } else if (Common::FS::Exists(dir2 + DIR_SEP + filename)) {
608 LoadFromFile(dir2 + DIR_SEP + filename, title); 634 LoadFromFile(dir2 + DIR_SEP + filename, title);
635 }
609} 636}
610 637
611bool KeyManager::BaseDeriveNecessary() const { 638bool KeyManager::BaseDeriveNecessary() const {
@@ -613,8 +640,9 @@ bool KeyManager::BaseDeriveNecessary() const {
613 return !HasKey(key_type, index1, index2); 640 return !HasKey(key_type, index1, index2);
614 }; 641 };
615 642
616 if (check_key_existence(S256KeyType::Header)) 643 if (check_key_existence(S256KeyType::Header)) {
617 return true; 644 return true;
645 }
618 646
619 for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) { 647 for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) {
620 if (check_key_existence(S128KeyType::Master, i) || 648 if (check_key_existence(S128KeyType::Master, i) ||
@@ -639,14 +667,16 @@ bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) const {
639} 667}
640 668
641Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const { 669Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const {
642 if (!HasKey(id, field1, field2)) 670 if (!HasKey(id, field1, field2)) {
643 return {}; 671 return {};
672 }
644 return s128_keys.at({id, field1, field2}); 673 return s128_keys.at({id, field1, field2});
645} 674}
646 675
647Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const { 676Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
648 if (!HasKey(id, field1, field2)) 677 if (!HasKey(id, field1, field2)) {
649 return {}; 678 return {};
679 }
650 return s256_keys.at({id, field1, field2}); 680 return s256_keys.at({id, field1, field2});
651} 681}
652 682
@@ -668,7 +698,7 @@ Key256 KeyManager::GetBISKey(u8 partition_id) const {
668template <size_t Size> 698template <size_t Size>
669void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname, 699void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
670 const std::array<u8, Size>& key) { 700 const std::array<u8, Size>& key) {
671 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 701 const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
672 std::string filename = "title.keys_autogenerated"; 702 std::string filename = "title.keys_autogenerated";
673 if (category == KeyCategory::Standard) { 703 if (category == KeyCategory::Standard) {
674 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated"; 704 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
@@ -677,9 +707,9 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
677 } 707 }
678 708
679 const auto path = yuzu_keys_dir + DIR_SEP + filename; 709 const auto path = yuzu_keys_dir + DIR_SEP + filename;
680 const auto add_info_text = !FileUtil::Exists(path); 710 const auto add_info_text = !Common::FS::Exists(path);
681 FileUtil::CreateFullPath(path); 711 Common::FS::CreateFullPath(path);
682 FileUtil::IOFile file{path, "a"}; 712 Common::FS::IOFile file{path, "a"};
683 if (!file.IsOpen()) { 713 if (!file.IsOpen()) {
684 return; 714 return;
685 } 715 }
@@ -763,29 +793,31 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
763} 793}
764 794
765bool KeyManager::KeyFileExists(bool title) { 795bool KeyManager::KeyFileExists(bool title) {
766 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); 796 const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
767 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 797 const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
768 if (title) { 798 if (title) {
769 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "title.keys") || 799 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "title.keys") ||
770 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "title.keys"); 800 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "title.keys");
771 } 801 }
772 802
773 if (Settings::values.use_dev_keys) { 803 if (Settings::values.use_dev_keys) {
774 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") || 804 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") ||
775 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys"); 805 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys");
776 } 806 }
777 807
778 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") || 808 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") ||
779 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys"); 809 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys");
780} 810}
781 811
782void KeyManager::DeriveSDSeedLazy() { 812void KeyManager::DeriveSDSeedLazy() {
783 if (HasKey(S128KeyType::SDSeed)) 813 if (HasKey(S128KeyType::SDSeed)) {
784 return; 814 return;
815 }
785 816
786 const auto res = DeriveSDSeed(); 817 const auto res = DeriveSDSeed();
787 if (res) 818 if (res) {
788 SetKey(S128KeyType::SDSeed, *res); 819 SetKey(S128KeyType::SDSeed, *res);
820 }
789} 821}
790 822
791static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) { 823static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
@@ -797,11 +829,13 @@ static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
797} 829}
798 830
799void KeyManager::DeriveBase() { 831void KeyManager::DeriveBase() {
800 if (!BaseDeriveNecessary()) 832 if (!BaseDeriveNecessary()) {
801 return; 833 return;
834 }
802 835
803 if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC)) 836 if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC)) {
804 return; 837 return;
838 }
805 839
806 const auto has_bis = [this](u64 id) { 840 const auto has_bis = [this](u64 id) {
807 return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) && 841 return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) &&
@@ -818,10 +852,11 @@ void KeyManager::DeriveBase() {
818 static_cast<u64>(BISKeyType::Tweak)); 852 static_cast<u64>(BISKeyType::Tweak));
819 }; 853 };
820 854
821 if (has_bis(2) && !has_bis(3)) 855 if (has_bis(2) && !has_bis(3)) {
822 copy_bis(2, 3); 856 copy_bis(2, 3);
823 else if (has_bis(3) && !has_bis(2)) 857 } else if (has_bis(3) && !has_bis(2)) {
824 copy_bis(3, 2); 858 copy_bis(3, 2);
859 }
825 860
826 std::bitset<32> revisions(0xFFFFFFFF); 861 std::bitset<32> revisions(0xFFFFFFFF);
827 for (size_t i = 0; i < revisions.size(); ++i) { 862 for (size_t i = 0; i < revisions.size(); ++i) {
@@ -831,15 +866,17 @@ void KeyManager::DeriveBase() {
831 } 866 }
832 } 867 }
833 868
834 if (!revisions.any()) 869 if (!revisions.any()) {
835 return; 870 return;
871 }
836 872
837 const auto sbk = GetKey(S128KeyType::SecureBoot); 873 const auto sbk = GetKey(S128KeyType::SecureBoot);
838 const auto tsec = GetKey(S128KeyType::TSEC); 874 const auto tsec = GetKey(S128KeyType::TSEC);
839 875
840 for (size_t i = 0; i < revisions.size(); ++i) { 876 for (size_t i = 0; i < revisions.size(); ++i) {
841 if (!revisions[i]) 877 if (!revisions[i]) {
842 continue; 878 continue;
879 }
843 880
844 // Derive keyblob key 881 // Derive keyblob key
845 const auto key = DeriveKeyblobKey( 882 const auto key = DeriveKeyblobKey(
@@ -848,16 +885,18 @@ void KeyManager::DeriveBase() {
848 SetKey(S128KeyType::Keyblob, key, i); 885 SetKey(S128KeyType::Keyblob, key, i);
849 886
850 // Derive keyblob MAC key 887 // Derive keyblob MAC key
851 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))) 888 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))) {
852 continue; 889 continue;
890 }
853 891
854 const auto mac_key = DeriveKeyblobMACKey( 892 const auto mac_key = DeriveKeyblobMACKey(
855 key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))); 893 key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)));
856 SetKey(S128KeyType::KeyblobMAC, mac_key, i); 894 SetKey(S128KeyType::KeyblobMAC, mac_key, i);
857 895
858 Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key); 896 Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key);
859 if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0) 897 if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0) {
860 continue; 898 continue;
899 }
861 900
862 // Decrypt keyblob 901 // Decrypt keyblob
863 if (keyblobs[i] == std::array<u8, 0x90>{}) { 902 if (keyblobs[i] == std::array<u8, 0x90>{}) {
@@ -881,16 +920,19 @@ void KeyManager::DeriveBase() {
881 920
882 revisions.set(); 921 revisions.set();
883 for (size_t i = 0; i < revisions.size(); ++i) { 922 for (size_t i = 0; i < revisions.size(); ++i) {
884 if (!HasKey(S128KeyType::Master, i)) 923 if (!HasKey(S128KeyType::Master, i)) {
885 revisions.reset(i); 924 revisions.reset(i);
925 }
886 } 926 }
887 927
888 if (!revisions.any()) 928 if (!revisions.any()) {
889 return; 929 return;
930 }
890 931
891 for (size_t i = 0; i < revisions.size(); ++i) { 932 for (size_t i = 0; i < revisions.size(); ++i) {
892 if (!revisions[i]) 933 if (!revisions[i]) {
893 continue; 934 continue;
935 }
894 936
895 // Derive general purpose keys 937 // Derive general purpose keys
896 DeriveGeneralPurposeKeys(i); 938 DeriveGeneralPurposeKeys(i);
@@ -920,16 +962,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
920 const auto es = Core::System::GetInstance().GetContentProvider().GetEntry( 962 const auto es = Core::System::GetInstance().GetContentProvider().GetEntry(
921 0x0100000000000033, FileSys::ContentRecordType::Program); 963 0x0100000000000033, FileSys::ContentRecordType::Program);
922 964
923 if (es == nullptr) 965 if (es == nullptr) {
924 return; 966 return;
967 }
925 968
926 const auto exefs = es->GetExeFS(); 969 const auto exefs = es->GetExeFS();
927 if (exefs == nullptr) 970 if (exefs == nullptr) {
928 return; 971 return;
972 }
929 973
930 const auto main = exefs->GetFile("main"); 974 const auto main = exefs->GetFile("main");
931 if (main == nullptr) 975 if (main == nullptr) {
932 return; 976 return;
977 }
933 978
934 const auto bytes = main->ReadAllBytes(); 979 const auto bytes = main->ReadAllBytes();
935 980
@@ -939,16 +984,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
939 const auto seed3 = data.GetRSAKekSeed3(); 984 const auto seed3 = data.GetRSAKekSeed3();
940 const auto mask0 = data.GetRSAKekMask0(); 985 const auto mask0 = data.GetRSAKekMask0();
941 986
942 if (eticket_kek != Key128{}) 987 if (eticket_kek != Key128{}) {
943 SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek)); 988 SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek));
989 }
944 if (eticket_kekek != Key128{}) { 990 if (eticket_kekek != Key128{}) {
945 SetKey(S128KeyType::Source, eticket_kekek, 991 SetKey(S128KeyType::Source, eticket_kekek,
946 static_cast<size_t>(SourceKeyType::ETicketKekek)); 992 static_cast<size_t>(SourceKeyType::ETicketKekek));
947 } 993 }
948 if (seed3 != Key128{}) 994 if (seed3 != Key128{}) {
949 SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3)); 995 SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3));
950 if (mask0 != Key128{}) 996 }
997 if (mask0 != Key128{}) {
951 SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0)); 998 SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0));
999 }
952 if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} || 1000 if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} ||
953 mask0 == Key128{}) { 1001 mask0 == Key128{}) {
954 return; 1002 return;
@@ -974,8 +1022,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
974 AESCipher<Key128> es_kek(temp_kekek, Mode::ECB); 1022 AESCipher<Key128> es_kek(temp_kekek, Mode::ECB);
975 es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt); 1023 es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt);
976 1024
977 if (eticket_final == Key128{}) 1025 if (eticket_final == Key128{}) {
978 return; 1026 return;
1027 }
979 1028
980 SetKey(S128KeyType::ETicketRSAKek, eticket_final); 1029 SetKey(S128KeyType::ETicketRSAKek, eticket_final);
981 1030
@@ -990,18 +1039,20 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
990void KeyManager::PopulateTickets() { 1039void KeyManager::PopulateTickets() {
991 const auto rsa_key = GetETicketRSAKey(); 1040 const auto rsa_key = GetETicketRSAKey();
992 1041
993 if (rsa_key == RSAKeyPair<2048>{}) 1042 if (rsa_key == RSAKeyPair<2048>{}) {
994 return; 1043 return;
1044 }
995 1045
996 if (!common_tickets.empty() && !personal_tickets.empty()) 1046 if (!common_tickets.empty() && !personal_tickets.empty()) {
997 return; 1047 return;
1048 }
998 1049
999 const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1050 const Common::FS::IOFile save1(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1000 "/system/save/80000000000000e1", 1051 "/system/save/80000000000000e1",
1001 "rb+"); 1052 "rb+");
1002 const FileUtil::IOFile save2(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1053 const Common::FS::IOFile save2(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1003 "/system/save/80000000000000e2", 1054 "/system/save/80000000000000e2",
1004 "rb+"); 1055 "rb+");
1005 1056
1006 const auto blob2 = GetTicketblob(save2); 1057 const auto blob2 = GetTicketblob(save2);
1007 auto res = GetTicketblob(save1); 1058 auto res = GetTicketblob(save1);
@@ -1011,8 +1062,10 @@ void KeyManager::PopulateTickets() {
1011 for (std::size_t i = 0; i < res.size(); ++i) { 1062 for (std::size_t i = 0; i < res.size(); ++i) {
1012 const auto common = i < idx; 1063 const auto common = i < idx;
1013 const auto pair = ParseTicket(res[i], rsa_key); 1064 const auto pair = ParseTicket(res[i], rsa_key);
1014 if (!pair) 1065 if (!pair) {
1015 continue; 1066 continue;
1067 }
1068
1016 const auto& [rid, key] = *pair; 1069 const auto& [rid, key] = *pair;
1017 u128 rights_id; 1070 u128 rights_id;
1018 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1071 std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1041,27 +1094,33 @@ void KeyManager::SynthesizeTickets() {
1041} 1094}
1042 1095
1043void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) { 1096void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
1044 if (key == Key128{}) 1097 if (key == Key128{}) {
1045 return; 1098 return;
1099 }
1046 SetKey(id, key, field1, field2); 1100 SetKey(id, key, field1, field2);
1047} 1101}
1048 1102
1049void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) { 1103void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) {
1050 if (key == Key256{}) 1104 if (key == Key256{}) {
1051 return; 1105 return;
1106 }
1107
1052 SetKey(id, key, field1, field2); 1108 SetKey(id, key, field1, field2);
1053} 1109}
1054 1110
1055void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) { 1111void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1056 if (!BaseDeriveNecessary()) 1112 if (!BaseDeriveNecessary()) {
1057 return; 1113 return;
1114 }
1058 1115
1059 if (!data.HasBoot0()) 1116 if (!data.HasBoot0()) {
1060 return; 1117 return;
1118 }
1061 1119
1062 for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) { 1120 for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) {
1063 if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{}) 1121 if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{}) {
1064 continue; 1122 continue;
1123 }
1065 encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i); 1124 encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i);
1066 WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i), 1125 WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i),
1067 encrypted_keyblobs[i]); 1126 encrypted_keyblobs[i]);
@@ -1083,8 +1142,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1083 static_cast<u64>(SourceKeyType::Keyblob), i); 1142 static_cast<u64>(SourceKeyType::Keyblob), i);
1084 } 1143 }
1085 1144
1086 if (data.HasFuses()) 1145 if (data.HasFuses()) {
1087 SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey()); 1146 SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey());
1147 }
1088 1148
1089 DeriveBase(); 1149 DeriveBase();
1090 1150
@@ -1098,8 +1158,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1098 1158
1099 const auto masters = data.GetTZMasterKeys(latest_master); 1159 const auto masters = data.GetTZMasterKeys(latest_master);
1100 for (size_t i = 0; i < masters.size(); ++i) { 1160 for (size_t i = 0; i < masters.size(); ++i) {
1101 if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i)) 1161 if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i)) {
1102 SetKey(S128KeyType::Master, masters[i], i); 1162 SetKey(S128KeyType::Master, masters[i], i);
1163 }
1103 } 1164 }
1104 1165
1105 DeriveBase(); 1166 DeriveBase();
@@ -1109,8 +1170,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1109 1170
1110 std::array<Key128, 0x20> package2_keys{}; 1171 std::array<Key128, 0x20> package2_keys{};
1111 for (size_t i = 0; i < package2_keys.size(); ++i) { 1172 for (size_t i = 0; i < package2_keys.size(); ++i) {
1112 if (HasKey(S128KeyType::Package2, i)) 1173 if (HasKey(S128KeyType::Package2, i)) {
1113 package2_keys[i] = GetKey(S128KeyType::Package2, i); 1174 package2_keys[i] = GetKey(S128KeyType::Package2, i);
1175 }
1114 } 1176 }
1115 data.DecryptPackage2(package2_keys, Package2Type::NormalMain); 1177 data.DecryptPackage2(package2_keys, Package2Type::NormalMain);
1116 1178
@@ -1148,12 +1210,15 @@ const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const {
1148 1210
1149bool KeyManager::AddTicketCommon(Ticket raw) { 1211bool KeyManager::AddTicketCommon(Ticket raw) {
1150 const auto rsa_key = GetETicketRSAKey(); 1212 const auto rsa_key = GetETicketRSAKey();
1151 if (rsa_key == RSAKeyPair<2048>{}) 1213 if (rsa_key == RSAKeyPair<2048>{}) {
1152 return false; 1214 return false;
1215 }
1153 1216
1154 const auto pair = ParseTicket(raw, rsa_key); 1217 const auto pair = ParseTicket(raw, rsa_key);
1155 if (!pair) 1218 if (!pair) {
1156 return false; 1219 return false;
1220 }
1221
1157 const auto& [rid, key] = *pair; 1222 const auto& [rid, key] = *pair;
1158 u128 rights_id; 1223 u128 rights_id;
1159 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1224 std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1164,12 +1229,15 @@ bool KeyManager::AddTicketCommon(Ticket raw) {
1164 1229
1165bool KeyManager::AddTicketPersonalized(Ticket raw) { 1230bool KeyManager::AddTicketPersonalized(Ticket raw) {
1166 const auto rsa_key = GetETicketRSAKey(); 1231 const auto rsa_key = GetETicketRSAKey();
1167 if (rsa_key == RSAKeyPair<2048>{}) 1232 if (rsa_key == RSAKeyPair<2048>{}) {
1168 return false; 1233 return false;
1234 }
1169 1235
1170 const auto pair = ParseTicket(raw, rsa_key); 1236 const auto pair = ParseTicket(raw, rsa_key);
1171 if (!pair) 1237 if (!pair) {
1172 return false; 1238 return false;
1239 }
1240
1173 const auto& [rid, key] = *pair; 1241 const auto& [rid, key] = *pair;
1174 u128 rights_id; 1242 u128 rights_id;
1175 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1243 std::memcpy(rights_id.data(), rid.data(), rid.size());
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 9269a73f2..bdca3770a 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -17,7 +17,7 @@
17#include "core/crypto/partition_data_manager.h" 17#include "core/crypto/partition_data_manager.h"
18#include "core/file_sys/vfs_types.h" 18#include "core/file_sys/vfs_types.h"
19 19
20namespace FileUtil { 20namespace Common::FS {
21class IOFile; 21class IOFile;
22} 22}
23 23
@@ -308,7 +308,7 @@ std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblo
308std::optional<Key128> DeriveSDSeed(); 308std::optional<Key128> DeriveSDSeed();
309Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys); 309Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
310 310
311std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save); 311std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save);
312 312
313// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority 313// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority
314// (offset 0x140-0x144 is zero) 314// (offset 0x140-0x144 is zero)
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index 7ed71ac3a..46136d04a 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -27,7 +27,7 @@
27#include "core/file_sys/vfs_offset.h" 27#include "core/file_sys/vfs_offset.h"
28#include "core/file_sys/vfs_vector.h" 28#include "core/file_sys/vfs_vector.h"
29 29
30using namespace Common; 30using Common::AsArray;
31 31
32namespace Core::Crypto { 32namespace Core::Crypto {
33 33
@@ -47,105 +47,123 @@ struct Package2Header {
47}; 47};
48static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); 48static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
49 49
50const std::array<SHA256Hash, 0x10> source_hashes{ 50// clang-format off
51 "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source 51constexpr std::array source_hashes{
52 "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source 52 AsArray("B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"), // keyblob_mac_key_source
53 "21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source 53 AsArray("7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"), // master_key_source
54 "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source 54 AsArray("21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"), // package2_key_source
55 "FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source 55 AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // aes_kek_generation_source
56 "C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source 56 AsArray("FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"), // aes_key_generation_source
57 "04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source 57 AsArray("C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"), // titlekek_source
58 "FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source 58 AsArray("04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"), // key_area_key_application_source
59 "1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source 59 AsArray("FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"), // key_area_key_ocean_source
60 "6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source 60 AsArray("1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"), // key_area_key_system_source
61 "D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source 61 AsArray("6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"), // sd_card_kek_source
62 "2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source 62 AsArray("D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"), // sd_card_save_key_source
63 "1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source 63 AsArray("2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"), // sd_card_nca_key_source
64 "8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source 64 AsArray("1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"), // header_kek_source
65 "D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3 65 AsArray("8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"), // header_key_source
66 "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0 66 AsArray("D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"), // rsa_kek_seed3
67 AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // rsa_kek_mask0
67}; 68};
68 69// clang-format on
69const std::array<SHA256Hash, 0x20> keyblob_source_hashes{ 70
70 "8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00 71// clang-format off
71 "2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01 72constexpr std::array keyblob_source_hashes{
72 "61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02 73 AsArray("8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"), // keyblob_key_source_00
73 "8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03 74 AsArray("2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"), // keyblob_key_source_01
74 "95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04 75 AsArray("61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"), // keyblob_key_source_02
75 "3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05 76 AsArray("8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"), // keyblob_key_source_03
76 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06 77 AsArray("95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"), // keyblob_key_source_04
77 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07 78 AsArray("3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"), // keyblob_key_source_05
78 79 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_06
79 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08 80 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_07
80 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09 81
81 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A 82 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_08
82 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B 83 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_09
83 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C 84 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0A
84 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D 85 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0B
85 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E 86 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0C
86 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F 87 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0D
87 88 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0E
88 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10 89 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0F
89 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11 90
90 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12 91 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_10
91 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13 92 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_11
92 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14 93 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_12
93 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15 94 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_13
94 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16 95 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_14
95 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17 96 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_15
96 97 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_16
97 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18 98 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_17
98 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19 99
99 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A 100 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_18
100 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B 101 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_19
101 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C 102 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1A
102 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D 103 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1B
103 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E 104 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1C
104 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F 105 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1D
106 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1E
107 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1F
105}; 108};
106 109// clang-format on
107const std::array<SHA256Hash, 0x20> master_key_hashes{ 110
108 "0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00 111// clang-format off
109 "4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01 112constexpr std::array master_key_hashes{
110 "79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02 113 AsArray("0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"), // master_key_00
111 "4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03 114 AsArray("4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"), // master_key_01
112 "75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04 115 AsArray("79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"), // master_key_02
113 "EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05 116 AsArray("4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"), // master_key_03
114 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06 117 AsArray("75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"), // master_key_04
115 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07 118 AsArray("EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"), // master_key_05
116 119 AsArray("9497E6779F5D840F2BBA1DE4E95BA1D6F21EFC94717D5AE5CA37D7EC5BD37A19"), // master_key_06
117 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08 120 AsArray("4EC96B8CB01B8DCE382149443430B2B6EBCB2983348AFA04A25E53609DABEDF6"), // master_key_07
118 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09 121
119 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A 122 AsArray("2998E2E23609BC2675FF062A2D64AF5B1B78DFF463B24119D64A1B64F01B2D51"), // master_key_08
120 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B 123 AsArray("9D486A98067C44B37CF173D3BF577891EB6081FF6B4A166347D9DBBF7025076B"), // master_key_09
121 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C 124 AsArray("4EC5A237A75A083A9C5F6CF615601522A7F822D06BD4BA32612C9CEBBB29BD45"), // master_key_0A
122 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D 125 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0B
123 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E 126 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0C
124 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F 127 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0D
125 128 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0E
126 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10 129 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0F
127 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11 130
128 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12 131 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_10
129 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13 132 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_11
130 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14 133 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_12
131 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15 134 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_13
132 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16 135 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_14
133 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17 136 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_15
134 137 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_16
135 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18 138 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_17
136 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19 139
137 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A 140 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_18
138 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B 141 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_19
139 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C 142 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1A
140 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D 143 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1B
141 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E 144 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1C
142 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F 145 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1D
146 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1E
147 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1F
143}; 148};
149// clang-format on
150
151static constexpr u8 CalculateMaxKeyblobSourceHash() {
152 const auto is_zero = [](const auto& data) {
153 // TODO: Replace with std::all_of whenever mingw decides to update their
154 // libraries to include the constexpr variant of it.
155 for (const auto element : data) {
156 if (element != 0) {
157 return false;
158 }
159 }
160 return true;
161 };
144 162
145static u8 CalculateMaxKeyblobSourceHash() {
146 for (s8 i = 0x1F; i >= 0; --i) { 163 for (s8 i = 0x1F; i >= 0; --i) {
147 if (keyblob_source_hashes[i] != SHA256Hash{}) 164 if (!is_zero(keyblob_source_hashes[i])) {
148 return static_cast<u8>(i + 1); 165 return static_cast<u8>(i + 1);
166 }
149 } 167 }
150 168
151 return 0; 169 return 0;
@@ -346,12 +364,11 @@ FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) con
346} 364}
347 365
348static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) { 366static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
349 const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end());
350 Package2Header temp = header; 367 Package2Header temp = header;
351 AESCipher<Key128> cipher(key, Mode::CTR); 368 AESCipher<Key128> cipher(key, Mode::CTR);
352 cipher.SetIV(iv); 369 cipher.SetIV(header.header_ctr);
353 cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr, 370 cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - sizeof(Package2Header::signature),
354 Op::Decrypt); 371 &temp.header_ctr, Op::Decrypt);
355 if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) { 372 if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
356 header = temp; 373 header = temp;
357 return true; 374 return true;
@@ -388,7 +405,7 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
388 auto c = a->ReadAllBytes(); 405 auto c = a->ReadAllBytes();
389 406
390 AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR); 407 AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR);
391 cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()}); 408 cipher.SetIV(header.section_ctr[1]);
392 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); 409 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
393 410
394 const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c); 411 const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c);
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/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 473245d5a..5039341c7 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -495,9 +495,10 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
495 495
496 auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key, 496 auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key,
497 starting_offset); 497 starting_offset);
498 std::vector<u8> iv(16); 498 Core::Crypto::CTREncryptionLayer::IVData iv{};
499 for (u8 i = 0; i < 8; ++i) 499 for (std::size_t i = 0; i < 8; ++i) {
500 iv[i] = s_header.raw.section_ctr[0x8 - i - 1]; 500 iv[i] = s_header.raw.section_ctr[8 - i - 1];
501 }
501 out->SetIV(iv); 502 out->SetIV(iv);
502 return std::static_pointer_cast<VfsFile>(out); 503 return std::static_pointer_cast<VfsFile>(out);
503 } 504 }
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp
index 0090cc6c4..fe7375e84 100644
--- a/src/core/file_sys/nca_patch.cpp
+++ b/src/core/file_sys/nca_patch.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 <algorithm> 5#include <algorithm>
6#include <array>
6#include <cstddef> 7#include <cstddef>
7#include <cstring> 8#include <cstring>
8 9
@@ -66,7 +67,7 @@ std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const {
66 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR); 67 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR);
67 68
68 // Calculate AES IV 69 // Calculate AES IV
69 std::vector<u8> iv(16); 70 std::array<u8, 16> iv{};
70 auto subsection_ctr = subsection.ctr; 71 auto subsection_ctr = subsection.ctr;
71 auto offset_iv = section_offset + base_offset; 72 auto offset_iv = section_offset + base_offset;
72 for (std::size_t i = 0; i < section_ctr.size(); ++i) 73 for (std::size_t i = 0; i < section_ctr.size(); ++i)
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index f831487dd..e42b677f7 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -728,7 +728,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
728 LOG_WARNING(Loader, "Overwriting existing NCA..."); 728 LOG_WARNING(Loader, "Overwriting existing NCA...");
729 VirtualDir c_dir; 729 VirtualDir c_dir;
730 { c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); } 730 { c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); }
731 c_dir->DeleteFile(FileUtil::GetFilename(path)); 731 c_dir->DeleteFile(Common::FS::GetFilename(path));
732 } 732 }
733 733
734 auto out = dir->CreateFileRelative(path); 734 auto out = dir->CreateFileRelative(path);
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index ec1d54f27..5b414b0f0 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -133,9 +133,9 @@ public:
133 // Parsing function defines the conversion from raw file to NCA. If there are other steps 133 // Parsing function defines the conversion from raw file to NCA. If there are other steps
134 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom 134 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
135 // parsing function. 135 // parsing function.
136 explicit RegisteredCache(VirtualDir dir, 136 explicit RegisteredCache(
137 ContentProviderParsingFunction parsing_function = 137 VirtualDir dir, ContentProviderParsingFunction parsing_function =
138 [](const VirtualFile& file, const NcaID& id) { return file; }); 138 [](const VirtualFile& file, const NcaID& id) { return file; });
139 ~RegisteredCache() override; 139 ~RegisteredCache() override;
140 140
141 void Refresh() override; 141 void Refresh() override;
diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp
index 61bb67945..d65c7d234 100644
--- a/src/core/file_sys/system_archive/mii_model.cpp
+++ b/src/core/file_sys/system_archive/mii_model.cpp
@@ -27,18 +27,12 @@ VirtualDir MiiModel() {
27 auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, 27 auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{},
28 std::vector<VirtualDir>{}, "data"); 28 std::vector<VirtualDir>{}, "data");
29 29
30 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_LINEAR.size()>>( 30 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat"));
31 MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat")); 31 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat"));
32 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_SRGB.size()>>( 32 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat"));
33 MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat")); 33 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat"));
34 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_LINEAR.size()>>( 34 out->AddFile(MakeArrayFile(MiiModelData::SHAPE_HIGH, "ShapeHigh.dat"));
35 MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat")); 35 out->AddFile(MakeArrayFile(MiiModelData::SHAPE_MID, "ShapeMid.dat"));
36 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_SRGB.size()>>(
37 MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat"));
38 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_HIGH.size()>>(
39 MiiModelData::SHAPE_HIGH, "ShapeHigh.dat"));
40 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>(
41 MiiModelData::SHAPE_MID, "ShapeMid.dat"));
42 36
43 return out; 37 return out;
44} 38}
diff --git a/src/core/file_sys/system_archive/ng_word.cpp b/src/core/file_sys/system_archive/ng_word.cpp
index f4443784d..100d3c5db 100644
--- a/src/core/file_sys/system_archive/ng_word.cpp
+++ b/src/core/file_sys/system_archive/ng_word.cpp
@@ -24,19 +24,18 @@ constexpr std::array<u8, 30> WORD_TXT{
24} // namespace NgWord1Data 24} // namespace NgWord1Data
25 25
26VirtualDir NgWord1() { 26VirtualDir NgWord1() {
27 std::vector<VirtualFile> files(NgWord1Data::NUMBER_WORD_TXT_FILES); 27 std::vector<VirtualFile> files;
28 files.reserve(NgWord1Data::NUMBER_WORD_TXT_FILES);
28 29
29 for (std::size_t i = 0; i < files.size(); ++i) { 30 for (std::size_t i = 0; i < files.size(); ++i) {
30 files[i] = std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( 31 files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, fmt::format("{}.txt", i)));
31 NgWord1Data::WORD_TXT, fmt::format("{}.txt", i));
32 } 32 }
33 33
34 files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( 34 files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, "common.txt"));
35 NgWord1Data::WORD_TXT, "common.txt")); 35 files.push_back(MakeArrayFile(NgWord1Data::VERSION_DAT, "version.dat"));
36 files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::VERSION_DAT.size()>>(
37 NgWord1Data::VERSION_DAT, "version.dat"));
38 36
39 return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); 37 return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{},
38 "data");
40} 39}
41 40
42namespace NgWord2Data { 41namespace NgWord2Data {
@@ -55,27 +54,22 @@ constexpr std::array<u8, 0x2C> AC_NX_DATA{
55} // namespace NgWord2Data 54} // namespace NgWord2Data
56 55
57VirtualDir NgWord2() { 56VirtualDir NgWord2() {
58 std::vector<VirtualFile> files(NgWord2Data::NUMBER_AC_NX_FILES * 3); 57 std::vector<VirtualFile> files;
58 files.reserve(NgWord2Data::NUMBER_AC_NX_FILES * 3);
59 59
60 for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) { 60 for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) {
61 files[3 * i] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 61 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i)));
62 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i)); 62 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i)));
63 files[3 * i + 1] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 63 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i)));
64 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i));
65 files[3 * i + 2] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
66 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i));
67 } 64 }
68 65
69 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 66 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b1_nx"));
70 NgWord2Data::AC_NX_DATA, "ac_common_b1_nx")); 67 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b2_nx"));
71 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 68 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx"));
72 NgWord2Data::AC_NX_DATA, "ac_common_b2_nx")); 69 files.push_back(MakeArrayFile(NgWord2Data::VERSION_DAT, "version.dat"));
73 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
74 NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx"));
75 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::VERSION_DAT.size()>>(
76 NgWord2Data::VERSION_DAT, "version.dat"));
77 70
78 return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); 71 return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{},
72 "data");
79} 73}
80 74
81} // namespace FileSys::SystemArchive 75} // namespace FileSys::SystemArchive
diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp
index d1de63f20..8fd005012 100644
--- a/src/core/file_sys/system_archive/time_zone_binary.cpp
+++ b/src/core/file_sys/system_archive/time_zone_binary.cpp
@@ -654,12 +654,13 @@ static VirtualFile GenerateDefaultTimeZoneFile() {
654} 654}
655 655
656VirtualDir TimeZoneBinary() { 656VirtualDir TimeZoneBinary() {
657 const std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>( 657 std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>(
658 std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{}, 658 std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{},
659 "zoneinfo")}; 659 "zoneinfo")};
660 const std::vector<VirtualFile> root_files{ 660 std::vector<VirtualFile> root_files{MakeArrayFile(LOCATION_NAMES, "binaryList.txt")};
661 std::make_shared<ArrayVfsFile<LOCATION_NAMES.size()>>(LOCATION_NAMES, "binaryList.txt")}; 661
662 return std::make_shared<VectorVfsDirectory>(root_files, root_dirs, "data"); 662 return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(root_dirs),
663 "data");
663} 664}
664 665
665} // namespace FileSys::SystemArchive 666} // namespace FileSys::SystemArchive
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index e33327ef0..a4c3f67c4 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -30,7 +30,7 @@ bool VfsFilesystem::IsWritable() const {
30} 30}
31 31
32VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { 32VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
33 const auto path = FileUtil::SanitizePath(path_); 33 const auto path = Common::FS::SanitizePath(path_);
34 if (root->GetFileRelative(path) != nullptr) 34 if (root->GetFileRelative(path) != nullptr)
35 return VfsEntryType::File; 35 return VfsEntryType::File;
36 if (root->GetDirectoryRelative(path) != nullptr) 36 if (root->GetDirectoryRelative(path) != nullptr)
@@ -40,22 +40,22 @@ VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
40} 40}
41 41
42VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { 42VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
43 const auto path = FileUtil::SanitizePath(path_); 43 const auto path = Common::FS::SanitizePath(path_);
44 return root->GetFileRelative(path); 44 return root->GetFileRelative(path);
45} 45}
46 46
47VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { 47VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
48 const auto path = FileUtil::SanitizePath(path_); 48 const auto path = Common::FS::SanitizePath(path_);
49 return root->CreateFileRelative(path); 49 return root->CreateFileRelative(path);
50} 50}
51 51
52VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { 52VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
53 const auto old_path = FileUtil::SanitizePath(old_path_); 53 const auto old_path = Common::FS::SanitizePath(old_path_);
54 const auto new_path = FileUtil::SanitizePath(new_path_); 54 const auto new_path = Common::FS::SanitizePath(new_path_);
55 55
56 // VfsDirectory impls are only required to implement copy across the current directory. 56 // VfsDirectory impls are only required to implement copy across the current directory.
57 if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) { 57 if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) {
58 if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path))) 58 if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path)))
59 return nullptr; 59 return nullptr;
60 return OpenFile(new_path, Mode::ReadWrite); 60 return OpenFile(new_path, Mode::ReadWrite);
61 } 61 }
@@ -76,8 +76,8 @@ VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view
76} 76}
77 77
78VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) { 78VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) {
79 const auto sanitized_old_path = FileUtil::SanitizePath(old_path); 79 const auto sanitized_old_path = Common::FS::SanitizePath(old_path);
80 const auto sanitized_new_path = FileUtil::SanitizePath(new_path); 80 const auto sanitized_new_path = Common::FS::SanitizePath(new_path);
81 81
82 // Again, non-default impls are highly encouraged to provide a more optimized version of this. 82 // Again, non-default impls are highly encouraged to provide a more optimized version of this.
83 auto out = CopyFile(sanitized_old_path, sanitized_new_path); 83 auto out = CopyFile(sanitized_old_path, sanitized_new_path);
@@ -89,26 +89,26 @@ VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view
89} 89}
90 90
91bool VfsFilesystem::DeleteFile(std::string_view path_) { 91bool VfsFilesystem::DeleteFile(std::string_view path_) {
92 const auto path = FileUtil::SanitizePath(path_); 92 const auto path = Common::FS::SanitizePath(path_);
93 auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); 93 auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write);
94 if (parent == nullptr) 94 if (parent == nullptr)
95 return false; 95 return false;
96 return parent->DeleteFile(FileUtil::GetFilename(path)); 96 return parent->DeleteFile(Common::FS::GetFilename(path));
97} 97}
98 98
99VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { 99VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
100 const auto path = FileUtil::SanitizePath(path_); 100 const auto path = Common::FS::SanitizePath(path_);
101 return root->GetDirectoryRelative(path); 101 return root->GetDirectoryRelative(path);
102} 102}
103 103
104VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { 104VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
105 const auto path = FileUtil::SanitizePath(path_); 105 const auto path = Common::FS::SanitizePath(path_);
106 return root->CreateDirectoryRelative(path); 106 return root->CreateDirectoryRelative(path);
107} 107}
108 108
109VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { 109VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) {
110 const auto old_path = FileUtil::SanitizePath(old_path_); 110 const auto old_path = Common::FS::SanitizePath(old_path_);
111 const auto new_path = FileUtil::SanitizePath(new_path_); 111 const auto new_path = Common::FS::SanitizePath(new_path_);
112 112
113 // Non-default impls are highly encouraged to provide a more optimized version of this. 113 // Non-default impls are highly encouraged to provide a more optimized version of this.
114 auto old_dir = OpenDirectory(old_path, Mode::Read); 114 auto old_dir = OpenDirectory(old_path, Mode::Read);
@@ -139,8 +139,8 @@ VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_
139} 139}
140 140
141VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) { 141VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) {
142 const auto sanitized_old_path = FileUtil::SanitizePath(old_path); 142 const auto sanitized_old_path = Common::FS::SanitizePath(old_path);
143 const auto sanitized_new_path = FileUtil::SanitizePath(new_path); 143 const auto sanitized_new_path = Common::FS::SanitizePath(new_path);
144 144
145 // Non-default impls are highly encouraged to provide a more optimized version of this. 145 // Non-default impls are highly encouraged to provide a more optimized version of this.
146 auto out = CopyDirectory(sanitized_old_path, sanitized_new_path); 146 auto out = CopyDirectory(sanitized_old_path, sanitized_new_path);
@@ -152,17 +152,17 @@ VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_v
152} 152}
153 153
154bool VfsFilesystem::DeleteDirectory(std::string_view path_) { 154bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
155 const auto path = FileUtil::SanitizePath(path_); 155 const auto path = Common::FS::SanitizePath(path_);
156 auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); 156 auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write);
157 if (parent == nullptr) 157 if (parent == nullptr)
158 return false; 158 return false;
159 return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path)); 159 return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path));
160} 160}
161 161
162VfsFile::~VfsFile() = default; 162VfsFile::~VfsFile() = default;
163 163
164std::string VfsFile::GetExtension() const { 164std::string VfsFile::GetExtension() const {
165 return std::string(FileUtil::GetExtensionFromFilename(GetName())); 165 return std::string(Common::FS::GetExtensionFromFilename(GetName()));
166} 166}
167 167
168VfsDirectory::~VfsDirectory() = default; 168VfsDirectory::~VfsDirectory() = default;
@@ -203,7 +203,7 @@ std::string VfsFile::GetFullPath() const {
203} 203}
204 204
205std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const { 205std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const {
206 auto vec = FileUtil::SplitPathComponents(path); 206 auto vec = Common::FS::SplitPathComponents(path);
207 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 207 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
208 vec.end()); 208 vec.end());
209 if (vec.empty()) { 209 if (vec.empty()) {
@@ -239,7 +239,7 @@ std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(std::string_view path) co
239} 239}
240 240
241std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const { 241std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const {
242 auto vec = FileUtil::SplitPathComponents(path); 242 auto vec = Common::FS::SplitPathComponents(path);
243 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 243 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
244 vec.end()); 244 vec.end());
245 if (vec.empty()) { 245 if (vec.empty()) {
@@ -301,7 +301,7 @@ std::size_t VfsDirectory::GetSize() const {
301} 301}
302 302
303std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) { 303std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) {
304 auto vec = FileUtil::SplitPathComponents(path); 304 auto vec = Common::FS::SplitPathComponents(path);
305 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 305 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
306 vec.end()); 306 vec.end());
307 if (vec.empty()) { 307 if (vec.empty()) {
@@ -320,7 +320,7 @@ std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path)
320 } 320 }
321 } 321 }
322 322
323 return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path)); 323 return dir->CreateFileRelative(Common::FS::GetPathWithoutTop(path));
324} 324}
325 325
326std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) { 326std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) {
@@ -332,7 +332,7 @@ std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path)
332} 332}
333 333
334std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) { 334std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) {
335 auto vec = FileUtil::SplitPathComponents(path); 335 auto vec = Common::FS::SplitPathComponents(path);
336 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 336 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
337 vec.end()); 337 vec.end());
338 if (vec.empty()) { 338 if (vec.empty()) {
@@ -351,7 +351,7 @@ std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_
351 } 351 }
352 } 352 }
353 353
354 return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path)); 354 return dir->CreateDirectoryRelative(Common::FS::GetPathWithoutTop(path));
355} 355}
356 356
357std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(std::string_view path) { 357std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(std::string_view path) {
diff --git a/src/core/file_sys/vfs_libzip.cpp b/src/core/file_sys/vfs_libzip.cpp
index d69952940..429d7bc8b 100644
--- a/src/core/file_sys/vfs_libzip.cpp
+++ b/src/core/file_sys/vfs_libzip.cpp
@@ -49,7 +49,7 @@ VirtualDir ExtractZIP(VirtualFile file) {
49 if (zip_fread(file2.get(), buf.data(), buf.size()) != s64(buf.size())) 49 if (zip_fread(file2.get(), buf.data(), buf.size()) != s64(buf.size()))
50 return nullptr; 50 return nullptr;
51 51
52 const auto parts = FileUtil::SplitPathComponents(stat.name); 52 const auto parts = Common::FS::SplitPathComponents(stat.name);
53 const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back()); 53 const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back());
54 54
55 std::shared_ptr<VectorVfsDirectory> dtrv = out; 55 std::shared_ptr<VectorVfsDirectory> dtrv = out;
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 0db0091f6..1dbf632c1 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,17 +59,19 @@ 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 if (cache.find(path) != cache.end()) {
72 auto weak = cache[path]; 76 auto weak = cache[path];
73 if (!weak.expired()) { 77 if (!weak.expired()) {
@@ -75,11 +79,11 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
75 } 79 }
76 } 80 }
77 81
78 if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) { 82 if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) {
79 FileUtil::CreateEmptyFile(path); 83 FS::CreateEmptyFile(path);
80 } 84 }
81 85
82 auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str()); 86 auto backing = std::make_shared<FS::IOFile>(path, ModeFlagsToString(perms).c_str());
83 cache[path] = backing; 87 cache[path] = backing;
84 88
85 // Cannot use make_shared as RealVfsFile constructor is private 89 // Cannot use make_shared as RealVfsFile constructor is private
@@ -87,33 +91,31 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
87} 91}
88 92
89VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { 93VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
90 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 94 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
91 const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash); 95 const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash);
92 if (!FileUtil::Exists(path)) { 96 if (!FS::Exists(path)) {
93 FileUtil::CreateFullPath(path_fwd); 97 FS::CreateFullPath(path_fwd);
94 if (!FileUtil::CreateEmptyFile(path)) 98 if (!FS::CreateEmptyFile(path)) {
95 return nullptr; 99 return nullptr;
100 }
96 } 101 }
97 return OpenFile(path, perms); 102 return OpenFile(path, perms);
98} 103}
99 104
100VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { 105VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
101 const auto old_path = 106 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
102 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 107 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 108
106 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || 109 if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
107 FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path)) 110 !FS::Copy(old_path, new_path)) {
108 return nullptr; 111 return nullptr;
112 }
109 return OpenFile(new_path, Mode::ReadWrite); 113 return OpenFile(new_path, Mode::ReadWrite);
110} 114}
111 115
112VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { 116VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
113 const auto old_path = 117 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
114 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 118 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
115 const auto new_path =
116 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
117 119
118 if (cache.find(old_path) != cache.end()) { 120 if (cache.find(old_path) != cache.end()) {
119 auto file = cache[old_path].lock(); 121 auto file = cache[old_path].lock();
@@ -122,8 +124,8 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
122 file->Close(); 124 file->Close();
123 } 125 }
124 126
125 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || 127 if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
126 FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) { 128 !FS::Rename(old_path, new_path)) {
127 return nullptr; 129 return nullptr;
128 } 130 }
129 131
@@ -139,28 +141,30 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
139} 141}
140 142
141bool RealVfsFilesystem::DeleteFile(std::string_view path_) { 143bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
142 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 144 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
143 if (cache.find(path) != cache.end()) { 145 if (cache.find(path) != cache.end()) {
144 if (!cache[path].expired()) 146 if (!cache[path].expired()) {
145 cache[path].lock()->Close(); 147 cache[path].lock()->Close();
148 }
146 cache.erase(path); 149 cache.erase(path);
147 } 150 }
148 return FileUtil::Delete(path); 151 return FS::Delete(path);
149} 152}
150 153
151VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { 154VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
152 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 155 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
153 // Cannot use make_shared as RealVfsDirectory constructor is private 156 // Cannot use make_shared as RealVfsDirectory constructor is private
154 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); 157 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
155} 158}
156 159
157VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { 160VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
158 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 161 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
159 const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash); 162 const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash);
160 if (!FileUtil::Exists(path)) { 163 if (!FS::Exists(path)) {
161 FileUtil::CreateFullPath(path_fwd); 164 FS::CreateFullPath(path_fwd);
162 if (!FileUtil::CreateDir(path)) 165 if (!FS::CreateDir(path)) {
163 return nullptr; 166 return nullptr;
167 }
164 } 168 }
165 // Cannot use make_shared as RealVfsDirectory constructor is private 169 // Cannot use make_shared as RealVfsDirectory constructor is private
166 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); 170 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
@@ -168,35 +172,33 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms
168 172
169VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, 173VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
170 std::string_view new_path_) { 174 std::string_view new_path_) {
171 const auto old_path = 175 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
172 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 176 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
173 const auto new_path = 177 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; 178 return nullptr;
178 FileUtil::CopyDir(old_path, new_path); 179 }
180 FS::CopyDir(old_path, new_path);
179 return OpenDirectory(new_path, Mode::ReadWrite); 181 return OpenDirectory(new_path, Mode::ReadWrite);
180} 182}
181 183
182VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, 184VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
183 std::string_view new_path_) { 185 std::string_view new_path_) {
184 const auto old_path = 186 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
185 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 187 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
186 const auto new_path = 188
187 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); 189 if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
188 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || 190 !FS::Rename(old_path, new_path)) {
189 FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
190 return nullptr; 191 return nullptr;
192 }
191 193
192 for (auto& kv : cache) { 194 for (auto& kv : cache) {
193 // Path in cache starts with old_path 195 // Path in cache starts with old_path
194 if (kv.first.rfind(old_path, 0) == 0) { 196 if (kv.first.rfind(old_path, 0) == 0) {
195 const auto file_old_path = 197 const auto file_old_path =
196 FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault); 198 FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault);
197 const auto file_new_path = 199 const auto file_new_path =
198 FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), 200 FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
199 FileUtil::DirectorySeparator::PlatformDefault); 201 FS::DirectorySeparator::PlatformDefault);
200 auto cached = cache[file_old_path]; 202 auto cached = cache[file_old_path];
201 if (!cached.expired()) { 203 if (!cached.expired()) {
202 auto file = cached.lock(); 204 auto file = cached.lock();
@@ -211,24 +213,24 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
211} 213}
212 214
213bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { 215bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
214 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 216 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
215 for (auto& kv : cache) { 217 for (auto& kv : cache) {
216 // Path in cache starts with old_path 218 // Path in cache starts with old_path
217 if (kv.first.rfind(path, 0) == 0) { 219 if (kv.first.rfind(path, 0) == 0) {
218 if (!cache[kv.first].expired()) 220 if (!cache[kv.first].expired()) {
219 cache[kv.first].lock()->Close(); 221 cache[kv.first].lock()->Close();
222 }
220 cache.erase(kv.first); 223 cache.erase(kv.first);
221 } 224 }
222 } 225 }
223 return FileUtil::DeleteDirRecursively(path); 226 return FS::DeleteDirRecursively(path);
224} 227}
225 228
226RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_, 229RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FS::IOFile> backing_,
227 const std::string& path_, Mode perms_) 230 const std::string& path_, Mode perms_)
228 : base(base_), backing(std::move(backing_)), path(path_), 231 : base(base_), backing(std::move(backing_)), path(path_), parent_path(FS::GetParentPath(path_)),
229 parent_path(FileUtil::GetParentPath(path_)), 232 path_components(FS::SplitPathComponents(path_)),
230 path_components(FileUtil::SplitPathComponents(path_)), 233 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_) {} 234 perms(perms_) {}
233 235
234RealVfsFile::~RealVfsFile() = default; 236RealVfsFile::~RealVfsFile() = default;
@@ -258,14 +260,16 @@ bool RealVfsFile::IsReadable() const {
258} 260}
259 261
260std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { 262std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
261 if (!backing->Seek(offset, SEEK_SET)) 263 if (!backing->Seek(offset, SEEK_SET)) {
262 return 0; 264 return 0;
265 }
263 return backing->ReadBytes(data, length); 266 return backing->ReadBytes(data, length);
264} 267}
265 268
266std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { 269std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
267 if (!backing->Seek(offset, SEEK_SET)) 270 if (!backing->Seek(offset, SEEK_SET)) {
268 return 0; 271 return 0;
272 }
269 return backing->WriteBytes(data, length); 273 return backing->WriteBytes(data, length);
270} 274}
271 275
@@ -282,16 +286,18 @@ bool RealVfsFile::Close() {
282 286
283template <> 287template <>
284std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const { 288std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const {
285 if (perms == Mode::Append) 289 if (perms == Mode::Append) {
286 return {}; 290 return {};
291 }
287 292
288 std::vector<VirtualFile> out; 293 std::vector<VirtualFile> out;
289 FileUtil::ForeachDirectoryEntry( 294 FS::ForeachDirectoryEntry(
290 nullptr, path, 295 nullptr, path,
291 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { 296 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
292 const std::string full_path = directory + DIR_SEP + filename; 297 const std::string full_path = directory + DIR_SEP + filename;
293 if (!FileUtil::IsDirectory(full_path)) 298 if (!FS::IsDirectory(full_path)) {
294 out.emplace_back(base.OpenFile(full_path, perms)); 299 out.emplace_back(base.OpenFile(full_path, perms));
300 }
295 return true; 301 return true;
296 }); 302 });
297 303
@@ -300,16 +306,18 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>(
300 306
301template <> 307template <>
302std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const { 308std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const {
303 if (perms == Mode::Append) 309 if (perms == Mode::Append) {
304 return {}; 310 return {};
311 }
305 312
306 std::vector<VirtualDir> out; 313 std::vector<VirtualDir> out;
307 FileUtil::ForeachDirectoryEntry( 314 FS::ForeachDirectoryEntry(
308 nullptr, path, 315 nullptr, path,
309 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { 316 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
310 const std::string full_path = directory + DIR_SEP + filename; 317 const std::string full_path = directory + DIR_SEP + filename;
311 if (FileUtil::IsDirectory(full_path)) 318 if (FS::IsDirectory(full_path)) {
312 out.emplace_back(base.OpenDirectory(full_path, perms)); 319 out.emplace_back(base.OpenDirectory(full_path, perms));
320 }
313 return true; 321 return true;
314 }); 322 });
315 323
@@ -317,29 +325,30 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi
317} 325}
318 326
319RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) 327RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
320 : base(base_), path(FileUtil::RemoveTrailingSlash(path_)), 328 : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)),
321 parent_path(FileUtil::GetParentPath(path)), 329 path_components(FS::SplitPathComponents(path)),
322 path_components(FileUtil::SplitPathComponents(path)), 330 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_) { 331 perms(perms_) {
325 if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) { 332 if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) {
326 FileUtil::CreateDir(path); 333 FS::CreateDir(path);
327 } 334 }
328} 335}
329 336
330RealVfsDirectory::~RealVfsDirectory() = default; 337RealVfsDirectory::~RealVfsDirectory() = default;
331 338
332std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const { 339std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
333 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 340 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
334 if (!FileUtil::Exists(full_path) || FileUtil::IsDirectory(full_path)) 341 if (!FS::Exists(full_path) || FS::IsDirectory(full_path)) {
335 return nullptr; 342 return nullptr;
343 }
336 return base.OpenFile(full_path, perms); 344 return base.OpenFile(full_path, perms);
337} 345}
338 346
339std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const { 347std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const {
340 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 348 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
341 if (!FileUtil::Exists(full_path) || !FileUtil::IsDirectory(full_path)) 349 if (!FS::Exists(full_path) || !FS::IsDirectory(full_path)) {
342 return nullptr; 350 return nullptr;
351 }
343 return base.OpenDirectory(full_path, perms); 352 return base.OpenDirectory(full_path, perms);
344} 353}
345 354
@@ -352,17 +361,17 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view
352} 361}
353 362
354std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) { 363std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) {
355 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));
356 return base.CreateFile(full_path, perms); 365 return base.CreateFile(full_path, perms);
357} 366}
358 367
359std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) { 368std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) {
360 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 369 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
361 return base.CreateDirectory(full_path, perms); 370 return base.CreateDirectory(full_path, perms);
362} 371}
363 372
364bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { 373bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
365 auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name)); 374 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(name));
366 return base.DeleteDirectory(full_path); 375 return base.DeleteDirectory(full_path);
367} 376}
368 377
@@ -387,8 +396,9 @@ std::string RealVfsDirectory::GetName() const {
387} 396}
388 397
389std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const { 398std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
390 if (path_components.size() <= 1) 399 if (path_components.size() <= 1) {
391 return nullptr; 400 return nullptr;
401 }
392 402
393 return base.OpenDirectory(parent_path, perms); 403 return base.OpenDirectory(parent_path, perms);
394} 404}
@@ -425,16 +435,17 @@ std::string RealVfsDirectory::GetFullPath() const {
425} 435}
426 436
427std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const { 437std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const {
428 if (perms == Mode::Append) 438 if (perms == Mode::Append) {
429 return {}; 439 return {};
440 }
430 441
431 std::map<std::string, VfsEntryType, std::less<>> out; 442 std::map<std::string, VfsEntryType, std::less<>> out;
432 FileUtil::ForeachDirectoryEntry( 443 FS::ForeachDirectoryEntry(
433 nullptr, path, 444 nullptr, path,
434 [&out](u64* entries_out, const std::string& directory, const std::string& filename) { 445 [&out](u64* entries_out, const std::string& directory, const std::string& filename) {
435 const std::string full_path = directory + DIR_SEP + filename; 446 const std::string full_path = directory + DIR_SEP + filename;
436 out.emplace(filename, FileUtil::IsDirectory(full_path) ? VfsEntryType::Directory 447 out.emplace(filename,
437 : VfsEntryType::File); 448 FS::IsDirectory(full_path) ? VfsEntryType::Directory : VfsEntryType::File);
438 return true; 449 return true;
439 }); 450 });
440 451
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index a0a857a31..0b537b22c 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -9,7 +9,7 @@
9#include "core/file_sys/mode.h" 9#include "core/file_sys/mode.h"
10#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs.h"
11 11
12namespace FileUtil { 12namespace Common::FS {
13class IOFile; 13class IOFile;
14} 14}
15 15
@@ -36,7 +36,7 @@ public:
36 bool DeleteDirectory(std::string_view path) override; 36 bool DeleteDirectory(std::string_view path) override;
37 37
38private: 38private:
39 boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache; 39 boost::container::flat_map<std::string, std::weak_ptr<Common::FS::IOFile>> cache;
40}; 40};
41 41
42// An implmentation of VfsFile that represents a file on the user's computer. 42// An implmentation of VfsFile that represents a file on the user's computer.
@@ -58,13 +58,13 @@ public:
58 bool Rename(std::string_view name) override; 58 bool Rename(std::string_view name) override;
59 59
60private: 60private:
61 RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing, 61 RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<Common::FS::IOFile> backing,
62 const std::string& path, Mode perms = Mode::Read); 62 const std::string& path, Mode perms = Mode::Read);
63 63
64 bool Close(); 64 bool Close();
65 65
66 RealVfsFilesystem& base; 66 RealVfsFilesystem& base;
67 std::shared_ptr<FileUtil::IOFile> backing; 67 std::shared_ptr<Common::FS::IOFile> backing;
68 std::string path; 68 std::string path;
69 std::string parent_path; 69 std::string parent_path;
70 std::vector<std::string> path_components; 70 std::vector<std::string> path_components;
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
index ac36cb2ee..95d3da2f2 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs_vector.h
@@ -4,7 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <cstring> 8#include <cstring>
9#include <memory>
10#include <string>
11#include <vector>
8#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
9 13
10namespace FileSys { 14namespace FileSys {
@@ -13,7 +17,8 @@ namespace FileSys {
13template <std::size_t size> 17template <std::size_t size>
14class ArrayVfsFile : public VfsFile { 18class ArrayVfsFile : public VfsFile {
15public: 19public:
16 ArrayVfsFile(std::array<u8, size> data, std::string name = "", VirtualDir parent = nullptr) 20 explicit ArrayVfsFile(const std::array<u8, size>& data, std::string name = "",
21 VirtualDir parent = nullptr)
17 : data(data), name(std::move(name)), parent(std::move(parent)) {} 22 : data(data), name(std::move(name)), parent(std::move(parent)) {}
18 23
19 std::string GetName() const override { 24 std::string GetName() const override {
@@ -61,6 +66,12 @@ private:
61 VirtualDir parent; 66 VirtualDir parent;
62}; 67};
63 68
69template <std::size_t Size, typename... Args>
70std::shared_ptr<ArrayVfsFile<Size>> MakeArrayFile(const std::array<u8, Size>& data,
71 Args&&... args) {
72 return std::make_shared<ArrayVfsFile<Size>>(data, std::forward<Args>(args)...);
73}
74
64// An implementation of VfsFile that is backed by a vector optionally supplied upon construction 75// An implementation of VfsFile that is backed by a vector optionally supplied upon construction
65class VectorVfsFile : public VfsFile { 76class VectorVfsFile : public VfsFile {
66public: 77public:
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index 81413c684..ccf5966d0 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -44,7 +44,7 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t
44} 44}
45 45
46NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) { 46NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
47 std::string path = FileUtil::SanitizePath(file->GetFullPath()); 47 std::string path = Common::FS::SanitizePath(file->GetFullPath());
48 static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca", 48 static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca",
49 std::regex_constants::ECMAScript | 49 std::regex_constants::ECMAScript |
50 std::regex_constants::icase); 50 std::regex_constants::icase);
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 13aa14934..3e8780243 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -39,7 +39,7 @@ public:
39 39
40 class Scoped { 40 class Scoped {
41 public: 41 public:
42 explicit Scoped(GraphicsContext& context_) : context(context_) { 42 [[nodiscard]] explicit Scoped(GraphicsContext& context_) : context(context_) {
43 context.MakeCurrent(); 43 context.MakeCurrent();
44 } 44 }
45 ~Scoped() { 45 ~Scoped() {
@@ -52,7 +52,7 @@ public:
52 52
53 /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value 53 /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
54 /// ends 54 /// ends
55 Scoped Acquire() { 55 [[nodiscard]] Scoped Acquire() {
56 return Scoped{*this}; 56 return Scoped{*this};
57 } 57 }
58}; 58};
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 0dc6a4a43..1b503331f 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -229,6 +229,8 @@ inline void ResponseBuilder::Push(u32 value) {
229 229
230template <typename T> 230template <typename T>
231void ResponseBuilder::PushRaw(const T& value) { 231void ResponseBuilder::PushRaw(const T& value) {
232 static_assert(std::is_trivially_copyable_v<T>,
233 "It's undefined behavior to use memcpy with non-trivially copyable objects");
232 std::memcpy(cmdbuf + index, &value, sizeof(T)); 234 std::memcpy(cmdbuf + index, &value, sizeof(T));
233 index += (sizeof(T) + 3) / 4; // round up to word length 235 index += (sizeof(T) + 3) / 4; // round up to word length
234} 236}
@@ -384,6 +386,8 @@ inline s32 RequestParser::Pop() {
384 386
385template <typename T> 387template <typename T>
386void RequestParser::PopRaw(T& value) { 388void RequestParser::PopRaw(T& value) {
389 static_assert(std::is_trivially_copyable_v<T>,
390 "It's undefined behavior to use memcpy with non-trivially copyable objects");
387 std::memcpy(&value, cmdbuf + index, sizeof(T)); 391 std::memcpy(&value, cmdbuf + index, sizeof(T));
388 index += (sizeof(T) + 3) / 4; // round up to word length 392 index += (sizeof(T) + 3) / 4; // round up to word length
389} 393}
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index df0debe1b..b882eaa0f 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -81,7 +81,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32
81 do { 81 do {
82 current_value = monitor.ExclusiveRead32(current_core, address); 82 current_value = monitor.ExclusiveRead32(current_core, address);
83 83
84 if (current_value != value) { 84 if (current_value != static_cast<u32>(value)) {
85 return ERR_INVALID_STATE; 85 return ERR_INVALID_STATE;
86 } 86 }
87 current_value++; 87 current_value++;
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 9277b5d08..81f85643b 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -293,13 +293,15 @@ std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
293 BufferDescriptorA()[buffer_index].Size()}; 293 BufferDescriptorA()[buffer_index].Size()};
294 294
295 if (is_buffer_a) { 295 if (is_buffer_a) {
296 ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return buffer; }, 296 ASSERT_OR_EXECUTE_MSG(
297 "BufferDescriptorA invalid buffer_index {}", buffer_index); 297 BufferDescriptorA().size() > buffer_index, { return buffer; },
298 "BufferDescriptorA invalid buffer_index {}", buffer_index);
298 buffer.resize(BufferDescriptorA()[buffer_index].Size()); 299 buffer.resize(BufferDescriptorA()[buffer_index].Size());
299 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); 300 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
300 } else { 301 } else {
301 ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return buffer; }, 302 ASSERT_OR_EXECUTE_MSG(
302 "BufferDescriptorX invalid buffer_index {}", buffer_index); 303 BufferDescriptorX().size() > buffer_index, { return buffer; },
304 "BufferDescriptorX invalid buffer_index {}", buffer_index);
303 buffer.resize(BufferDescriptorX()[buffer_index].Size()); 305 buffer.resize(BufferDescriptorX()[buffer_index].Size());
304 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); 306 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
305 } 307 }
@@ -324,16 +326,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
324 } 326 }
325 327
326 if (is_buffer_b) { 328 if (is_buffer_b) {
327 ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index && 329 ASSERT_OR_EXECUTE_MSG(
328 BufferDescriptorB()[buffer_index].Size() >= size, 330 BufferDescriptorB().size() > buffer_index &&
329 { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", 331 BufferDescriptorB()[buffer_index].Size() >= size,
330 buffer_index, size); 332 { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size);
331 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); 333 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
332 } else { 334 } else {
333 ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index && 335 ASSERT_OR_EXECUTE_MSG(
334 BufferDescriptorC()[buffer_index].Size() >= size, 336 BufferDescriptorC().size() > buffer_index &&
335 { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", 337 BufferDescriptorC()[buffer_index].Size() >= size,
336 buffer_index, size); 338 { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size);
337 memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); 339 memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
338 } 340 }
339 341
@@ -344,12 +346,14 @@ std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const
344 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && 346 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
345 BufferDescriptorA()[buffer_index].Size()}; 347 BufferDescriptorA()[buffer_index].Size()};
346 if (is_buffer_a) { 348 if (is_buffer_a) {
347 ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return 0; }, 349 ASSERT_OR_EXECUTE_MSG(
348 "BufferDescriptorA invalid buffer_index {}", buffer_index); 350 BufferDescriptorA().size() > buffer_index, { return 0; },
351 "BufferDescriptorA invalid buffer_index {}", buffer_index);
349 return BufferDescriptorA()[buffer_index].Size(); 352 return BufferDescriptorA()[buffer_index].Size();
350 } else { 353 } else {
351 ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return 0; }, 354 ASSERT_OR_EXECUTE_MSG(
352 "BufferDescriptorX invalid buffer_index {}", buffer_index); 355 BufferDescriptorX().size() > buffer_index, { return 0; },
356 "BufferDescriptorX invalid buffer_index {}", buffer_index);
353 return BufferDescriptorX()[buffer_index].Size(); 357 return BufferDescriptorX()[buffer_index].Size();
354 } 358 }
355} 359}
@@ -358,12 +362,14 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons
358 const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && 362 const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
359 BufferDescriptorB()[buffer_index].Size()}; 363 BufferDescriptorB()[buffer_index].Size()};
360 if (is_buffer_b) { 364 if (is_buffer_b) {
361 ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index, { return 0; }, 365 ASSERT_OR_EXECUTE_MSG(
362 "BufferDescriptorB invalid buffer_index {}", buffer_index); 366 BufferDescriptorB().size() > buffer_index, { return 0; },
367 "BufferDescriptorB invalid buffer_index {}", buffer_index);
363 return BufferDescriptorB()[buffer_index].Size(); 368 return BufferDescriptorB()[buffer_index].Size();
364 } else { 369 } else {
365 ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index, { return 0; }, 370 ASSERT_OR_EXECUTE_MSG(
366 "BufferDescriptorC invalid buffer_index {}", buffer_index); 371 BufferDescriptorC().size() > buffer_index, { return 0; },
372 "BufferDescriptorC invalid buffer_index {}", buffer_index);
367 return BufferDescriptorC()[buffer_index].Size(); 373 return BufferDescriptorC()[buffer_index].Size();
368 } 374 }
369 return 0; 375 return 0;
diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp
index 5d6aac00f..a3fadb533 100644
--- a/src/core/hle/kernel/memory/page_table.cpp
+++ b/src/core/hle/kernel/memory/page_table.cpp
@@ -604,7 +604,6 @@ ResultCode PageTable::MapPages(VAddr addr, const PageLinkedList& page_linked_lis
604 if (const auto result{ 604 if (const auto result{
605 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; 605 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())};
606 result.IsError()) { 606 result.IsError()) {
607 const MemoryInfo info{block_manager->FindBlock(cur_addr).GetMemoryInfo()};
608 const std::size_t num_pages{(addr - cur_addr) / PageSize}; 607 const std::size_t num_pages{(addr - cur_addr) / PageSize};
609 608
610 ASSERT( 609 ASSERT(
@@ -852,11 +851,12 @@ ResultCode PageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
852 return result; 851 return result;
853 } 852 }
854 853
855 block_manager->UpdateLock(addr, size / PageSize, 854 block_manager->UpdateLock(
856 [](MemoryBlockManager::iterator block, MemoryPermission perm) { 855 addr, size / PageSize,
857 block->ShareToDevice(perm); 856 [](MemoryBlockManager::iterator block, MemoryPermission perm) {
858 }, 857 block->ShareToDevice(perm);
859 perm); 858 },
859 perm);
860 860
861 return RESULT_SUCCESS; 861 return RESULT_SUCCESS;
862} 862}
@@ -874,11 +874,12 @@ ResultCode PageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
874 return result; 874 return result;
875 } 875 }
876 876
877 block_manager->UpdateLock(addr, size / PageSize, 877 block_manager->UpdateLock(
878 [](MemoryBlockManager::iterator block, MemoryPermission perm) { 878 addr, size / PageSize,
879 block->UnshareToDevice(perm); 879 [](MemoryBlockManager::iterator block, MemoryPermission perm) {
880 }, 880 block->UnshareToDevice(perm);
881 perm); 881 },
882 perm);
882 883
883 return RESULT_SUCCESS; 884 return RESULT_SUCCESS;
884} 885}
diff --git a/src/core/hle/kernel/memory/system_control.cpp b/src/core/hle/kernel/memory/system_control.cpp
index 2f98e9c4c..11d204bc2 100644
--- a/src/core/hle/kernel/memory/system_control.cpp
+++ b/src/core/hle/kernel/memory/system_control.cpp
@@ -7,22 +7,15 @@
7#include "core/hle/kernel/memory/system_control.h" 7#include "core/hle/kernel/memory/system_control.h"
8 8
9namespace Kernel::Memory::SystemControl { 9namespace Kernel::Memory::SystemControl {
10 10namespace {
11u64 GenerateRandomU64ForInit() {
12 static std::random_device device;
13 static std::mt19937 gen(device());
14 static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
15 return distribution(gen);
16}
17
18template <typename F> 11template <typename F>
19u64 GenerateUniformRange(u64 min, u64 max, F f) { 12u64 GenerateUniformRange(u64 min, u64 max, F f) {
20 /* Handle the case where the difference is too large to represent. */ 13 // Handle the case where the difference is too large to represent.
21 if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) { 14 if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
22 return f(); 15 return f();
23 } 16 }
24 17
25 /* Iterate until we get a value in range. */ 18 // Iterate until we get a value in range.
26 const u64 range_size = ((max + 1) - min); 19 const u64 range_size = ((max + 1) - min);
27 const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size; 20 const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
28 while (true) { 21 while (true) {
@@ -32,6 +25,14 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) {
32 } 25 }
33} 26}
34 27
28u64 GenerateRandomU64ForInit() {
29 static std::random_device device;
30 static std::mt19937 gen(device());
31 static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
32 return distribution(gen);
33}
34} // Anonymous namespace
35
35u64 GenerateRandomRange(u64 min, u64 max) { 36u64 GenerateRandomRange(u64 min, u64 max) {
36 return GenerateUniformRange(min, max, GenerateRandomU64ForInit); 37 return GenerateUniformRange(min, max, GenerateRandomU64ForInit);
37} 38}
diff --git a/src/core/hle/kernel/memory/system_control.h b/src/core/hle/kernel/memory/system_control.h
index 3fa93111d..19cab8cbc 100644
--- a/src/core/hle/kernel/memory/system_control.h
+++ b/src/core/hle/kernel/memory/system_control.h
@@ -8,11 +8,6 @@
8 8
9namespace Kernel::Memory::SystemControl { 9namespace Kernel::Memory::SystemControl {
10 10
11u64 GenerateRandomU64ForInit();
12
13template <typename F>
14u64 GenerateUniformRange(u64 min, u64 max, F f);
15
16u64 GenerateRandomRange(u64 min, u64 max); 11u64 GenerateRandomRange(u64 min, u64 max);
17 12
18} // namespace Kernel::Memory::SystemControl 13} // namespace Kernel::Memory::SystemControl
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index f93e5e4b0..a4b234424 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -131,7 +131,8 @@ u32 GlobalScheduler::SelectThreads() {
131 u32 cores_needing_context_switch{}; 131 u32 cores_needing_context_switch{};
132 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 132 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
133 Scheduler& sched = kernel.Scheduler(core); 133 Scheduler& sched = kernel.Scheduler(core);
134 ASSERT(top_threads[core] == nullptr || top_threads[core]->GetProcessorID() == core); 134 ASSERT(top_threads[core] == nullptr ||
135 static_cast<u32>(top_threads[core]->GetProcessorID()) == core);
135 if (update_thread(top_threads[core], sched)) { 136 if (update_thread(top_threads[core], sched)) {
136 cores_needing_context_switch |= (1ul << core); 137 cores_needing_context_switch |= (1ul << core);
137 } 138 }
@@ -663,32 +664,26 @@ void Scheduler::Reload() {
663} 664}
664 665
665void Scheduler::SwitchContextStep2() { 666void Scheduler::SwitchContextStep2() {
666 Thread* previous_thread = current_thread_prev.get();
667 Thread* new_thread = selected_thread.get();
668
669 // Load context of new thread 667 // Load context of new thread
670 Process* const previous_process = 668 if (selected_thread) {
671 previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr; 669 ASSERT_MSG(selected_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
672
673 if (new_thread) {
674 ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
675 "Thread must be runnable."); 670 "Thread must be runnable.");
676 671
677 // Cancel any outstanding wakeup events for this thread 672 // Cancel any outstanding wakeup events for this thread
678 new_thread->SetIsRunning(true); 673 selected_thread->SetIsRunning(true);
679 new_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); 674 selected_thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
680 new_thread->SetWasRunning(false); 675 selected_thread->SetWasRunning(false);
681 676
682 auto* const thread_owner_process = current_thread->GetOwnerProcess(); 677 auto* const thread_owner_process = current_thread->GetOwnerProcess();
683 if (thread_owner_process != nullptr) { 678 if (thread_owner_process != nullptr) {
684 system.Kernel().MakeCurrentProcess(thread_owner_process); 679 system.Kernel().MakeCurrentProcess(thread_owner_process);
685 } 680 }
686 if (!new_thread->IsHLEThread()) { 681 if (!selected_thread->IsHLEThread()) {
687 Core::ARM_Interface& cpu_core = new_thread->ArmInterface(); 682 Core::ARM_Interface& cpu_core = selected_thread->ArmInterface();
688 cpu_core.LoadContext(new_thread->GetContext32()); 683 cpu_core.LoadContext(selected_thread->GetContext32());
689 cpu_core.LoadContext(new_thread->GetContext64()); 684 cpu_core.LoadContext(selected_thread->GetContext64());
690 cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); 685 cpu_core.SetTlsAddress(selected_thread->GetTLSAddress());
691 cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); 686 cpu_core.SetTPIDR_EL0(selected_thread->GetTPIDR_EL0());
692 cpu_core.ChangeProcessorID(this->core_id); 687 cpu_core.ChangeProcessorID(this->core_id);
693 cpu_core.ClearExclusiveState(); 688 cpu_core.ClearExclusiveState();
694 } 689 }
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index b3b4b5169..36e3c26fb 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -289,7 +289,7 @@ private:
289 289
290class SchedulerLock { 290class SchedulerLock {
291public: 291public:
292 explicit SchedulerLock(KernelCore& kernel); 292 [[nodiscard]] explicit SchedulerLock(KernelCore& kernel);
293 ~SchedulerLock(); 293 ~SchedulerLock();
294 294
295protected: 295protected:
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 450f61fea..b6bdbd988 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -342,8 +342,9 @@ ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) {
342 */ 342 */
343#define CASCADE_RESULT(target, source) \ 343#define CASCADE_RESULT(target, source) \
344 auto CONCAT2(check_result_L, __LINE__) = source; \ 344 auto CONCAT2(check_result_L, __LINE__) = source; \
345 if (CONCAT2(check_result_L, __LINE__).Failed()) \ 345 if (CONCAT2(check_result_L, __LINE__).Failed()) { \
346 return CONCAT2(check_result_L, __LINE__).Code(); \ 346 return CONCAT2(check_result_L, __LINE__).Code(); \
347 } \
347 target = std::move(*CONCAT2(check_result_L, __LINE__)) 348 target = std::move(*CONCAT2(check_result_L, __LINE__))
348 349
349/** 350/**
@@ -351,6 +352,9 @@ ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) {
351 * non-success, or discarded otherwise. 352 * non-success, or discarded otherwise.
352 */ 353 */
353#define CASCADE_CODE(source) \ 354#define CASCADE_CODE(source) \
354 auto CONCAT2(check_result_L, __LINE__) = source; \ 355 do { \
355 if (CONCAT2(check_result_L, __LINE__).IsError()) \ 356 auto CONCAT2(check_result_L, __LINE__) = source; \
356 return CONCAT2(check_result_L, __LINE__); 357 if (CONCAT2(check_result_L, __LINE__).IsError()) { \
358 return CONCAT2(check_result_L, __LINE__); \
359 } \
360 } while (false)
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 63e4aeca0..eb54cb123 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -35,7 +35,7 @@ constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 30};
35constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; 35constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
36 36
37static std::string GetImagePath(Common::UUID uuid) { 37static std::string GetImagePath(Common::UUID uuid) {
38 return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 38 return Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
39 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; 39 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
40} 40}
41 41
@@ -318,7 +318,7 @@ protected:
318 IPC::ResponseBuilder rb{ctx, 3}; 318 IPC::ResponseBuilder rb{ctx, 3};
319 rb.Push(RESULT_SUCCESS); 319 rb.Push(RESULT_SUCCESS);
320 320
321 const FileUtil::IOFile image(GetImagePath(user_id), "rb"); 321 const Common::FS::IOFile image(GetImagePath(user_id), "rb");
322 if (!image.IsOpen()) { 322 if (!image.IsOpen()) {
323 LOG_WARNING(Service_ACC, 323 LOG_WARNING(Service_ACC,
324 "Failed to load user provided image! Falling back to built-in backup..."); 324 "Failed to load user provided image! Falling back to built-in backup...");
@@ -340,7 +340,7 @@ protected:
340 IPC::ResponseBuilder rb{ctx, 3}; 340 IPC::ResponseBuilder rb{ctx, 3};
341 rb.Push(RESULT_SUCCESS); 341 rb.Push(RESULT_SUCCESS);
342 342
343 const FileUtil::IOFile image(GetImagePath(user_id), "rb"); 343 const Common::FS::IOFile image(GetImagePath(user_id), "rb");
344 344
345 if (!image.IsOpen()) { 345 if (!image.IsOpen()) {
346 LOG_WARNING(Service_ACC, 346 LOG_WARNING(Service_ACC,
@@ -405,7 +405,7 @@ protected:
405 ProfileData data; 405 ProfileData data;
406 std::memcpy(&data, user_data.data(), sizeof(ProfileData)); 406 std::memcpy(&data, user_data.data(), sizeof(ProfileData));
407 407
408 FileUtil::IOFile image(GetImagePath(user_id), "wb"); 408 Common::FS::IOFile image(GetImagePath(user_id), "wb");
409 409
410 if (!image.IsOpen() || !image.Resize(image_data.size()) || 410 if (!image.IsOpen() || !image.Resize(image_data.size()) ||
411 image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() || 411 image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() ||
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index a98d57b5c..9b829e957 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -13,6 +13,7 @@
13 13
14namespace Service::Account { 14namespace Service::Account {
15 15
16namespace FS = Common::FS;
16using Common::UUID; 17using Common::UUID;
17 18
18struct UserRaw { 19struct UserRaw {
@@ -318,9 +319,8 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase&
318} 319}
319 320
320void ProfileManager::ParseUserSaveFile() { 321void ProfileManager::ParseUserSaveFile() {
321 FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 322 const FS::IOFile save(
322 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", 323 FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", "rb");
323 "rb");
324 324
325 if (!save.IsOpen()) { 325 if (!save.IsOpen()) {
326 LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new " 326 LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
@@ -366,22 +366,22 @@ void ProfileManager::WriteUserSaveFile() {
366 }; 366 };
367 } 367 }
368 368
369 const auto raw_path = 369 const auto raw_path = FS::GetUserPath(FS::UserPath::NANDDir) + "/system/save/8000000000000010";
370 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; 370 if (FS::Exists(raw_path) && !FS::IsDirectory(raw_path)) {
371 if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) 371 FS::Delete(raw_path);
372 FileUtil::Delete(raw_path); 372 }
373 373
374 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 374 const auto path =
375 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat"; 375 FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
376 376
377 if (!FileUtil::CreateFullPath(path)) { 377 if (!FS::CreateFullPath(path)) {
378 LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory " 378 LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory "
379 "nand/system/save/8000000000000010/su/avators to mitigate this " 379 "nand/system/save/8000000000000010/su/avators to mitigate this "
380 "issue."); 380 "issue.");
381 return; 381 return;
382 } 382 }
383 383
384 FileUtil::IOFile save(path, "wb"); 384 FS::IOFile save(path, "wb");
385 385
386 if (!save.IsOpen()) { 386 if (!save.IsOpen()) {
387 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " 387 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 55a1edf1a..7d92b25a3 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -378,7 +378,11 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext&
378} 378}
379 379
380void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { 380void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
381 LOG_WARNING(Service_AM, "(STUBBED) called"); 381 IPC::RequestParser rp{ctx};
382 const auto permission = rp.PopEnum<ScreenshotPermission>();
383 LOG_DEBUG(Service_AM, "called, permission={}", permission);
384
385 screenshot_permission = permission;
382 386
383 IPC::ResponseBuilder rb{ctx, 2}; 387 IPC::ResponseBuilder rb{ctx, 2};
384 rb.Push(RESULT_SUCCESS); 388 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 6cfb11b48..6e69796ec 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -149,6 +149,12 @@ private:
149 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); 149 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
150 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); 150 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
151 151
152 enum class ScreenshotPermission : u32 {
153 Inherit = 0,
154 Enable = 1,
155 Disable = 2,
156 };
157
152 Core::System& system; 158 Core::System& system;
153 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 159 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
154 Kernel::EventPair launchable_event; 160 Kernel::EventPair launchable_event;
@@ -157,6 +163,7 @@ private:
157 u32 idle_time_detection_extension = 0; 163 u32 idle_time_detection_extension = 0;
158 u64 num_fatal_sections_entered = 0; 164 u64 num_fatal_sections_entered = 0;
159 bool is_auto_sleep_disabled = false; 165 bool is_auto_sleep_disabled = false;
166 ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit;
160}; 167};
161 168
162class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { 169class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index 289da2619..bdeb0737a 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -122,8 +122,7 @@ void SoftwareKeyboard::ExecuteInteractive() {
122 122
123 switch (request) { 123 switch (request) {
124 case Request::Calc: { 124 case Request::Calc: {
125 broker.PushNormalDataFromApplet( 125 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{1}));
126 std::make_shared<IStorage>(std::move(std::vector<u8>{1})));
127 broker.SignalStateChanged(); 126 broker.SignalStateChanged();
128 break; 127 break;
129 } 128 }
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 9f30e167d..efe595c4f 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -293,8 +293,8 @@ void WebBrowser::Finalize() {
293 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data))); 293 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data)));
294 broker.SignalStateChanged(); 294 broker.SignalStateChanged();
295 295
296 if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) { 296 if (!temporary_dir.empty() && Common::FS::IsDirectory(temporary_dir)) {
297 FileUtil::DeleteDirRecursively(temporary_dir); 297 Common::FS::DeleteDirRecursively(temporary_dir);
298 } 298 }
299} 299}
300 300
@@ -452,10 +452,10 @@ void WebBrowser::InitializeOffline() {
452 }; 452 };
453 453
454 temporary_dir = 454 temporary_dir =
455 FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + "web_applet_" + 455 Common::FS::SanitizePath(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
456 WEB_SOURCE_NAMES[static_cast<u32>(source) - 1], 456 "web_applet_" + WEB_SOURCE_NAMES[static_cast<u32>(source) - 1],
457 FileUtil::DirectorySeparator::PlatformDefault); 457 Common::FS::DirectorySeparator::PlatformDefault);
458 FileUtil::DeleteDirRecursively(temporary_dir); 458 Common::FS::DeleteDirRecursively(temporary_dir);
459 459
460 u64 title_id = 0; // 0 corresponds to current process 460 u64 title_id = 0; // 0 corresponds to current process
461 ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8); 461 ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8);
@@ -492,8 +492,8 @@ void WebBrowser::InitializeOffline() {
492 } 492 }
493 493
494 filename = 494 filename =
495 FileUtil::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename, 495 Common::FS::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename,
496 FileUtil::DirectorySeparator::PlatformDefault); 496 Common::FS::DirectorySeparator::PlatformDefault);
497} 497}
498 498
499void WebBrowser::ExecuteShop() { 499void WebBrowser::ExecuteShop() {
@@ -551,7 +551,8 @@ void WebBrowser::ExecuteShop() {
551} 551}
552 552
553void WebBrowser::ExecuteOffline() { 553void WebBrowser::ExecuteOffline() {
554 frontend.OpenPageLocal(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); 554 frontend.OpenPageLocal(
555 filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
555} 556}
556 557
557} // namespace Service::AM::Applets 558} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index dd80dd1dc..9b4910e53 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -206,7 +206,7 @@ private:
206 AudioCore::StreamPtr stream; 206 AudioCore::StreamPtr stream;
207 std::string device_name; 207 std::string device_name;
208 208
209 [[maybe_unused]] AudoutParams audio_params {}; 209 [[maybe_unused]] AudoutParams audio_params{};
210 210
211 /// This is the event handle used to check if the audio buffer was released 211 /// This is the event handle used to check if the audio buffer was released
212 Kernel::EventPair buffer_event; 212 Kernel::EventPair buffer_event;
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp
index d29e78d7e..ca021a99f 100644
--- a/src/core/hle/service/bcat/backend/boxcat.cpp
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -89,12 +89,12 @@ constexpr u32 TIMEOUT_SECONDS = 30;
89 89
90std::string GetBINFilePath(u64 title_id) { 90std::string GetBINFilePath(u64 title_id) {
91 return fmt::format("{}bcat/{:016X}/launchparam.bin", 91 return fmt::format("{}bcat/{:016X}/launchparam.bin",
92 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); 92 Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id);
93} 93}
94 94
95std::string GetZIPFilePath(u64 title_id) { 95std::string GetZIPFilePath(u64 title_id) {
96 return fmt::format("{}bcat/{:016X}/data.zip", 96 return fmt::format("{}bcat/{:016X}/data.zip",
97 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); 97 Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id);
98} 98}
99 99
100// If the error is something the user should know about (build ID mismatch, bad client version), 100// If the error is something the user should know about (build ID mismatch, bad client version),
@@ -205,8 +205,8 @@ private:
205 {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)}, 205 {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)},
206 }; 206 };
207 207
208 if (FileUtil::Exists(path)) { 208 if (Common::FS::Exists(path)) {
209 FileUtil::IOFile file{path, "rb"}; 209 Common::FS::IOFile file{path, "rb"};
210 if (file.IsOpen()) { 210 if (file.IsOpen()) {
211 std::vector<u8> bytes(file.GetSize()); 211 std::vector<u8> bytes(file.GetSize());
212 file.ReadBytes(bytes.data(), bytes.size()); 212 file.ReadBytes(bytes.data(), bytes.size());
@@ -236,8 +236,8 @@ private:
236 return DownloadResult::InvalidContentType; 236 return DownloadResult::InvalidContentType;
237 } 237 }
238 238
239 FileUtil::CreateFullPath(path); 239 Common::FS::CreateFullPath(path);
240 FileUtil::IOFile file{path, "wb"}; 240 Common::FS::IOFile file{path, "wb"};
241 if (!file.IsOpen()) 241 if (!file.IsOpen())
242 return DownloadResult::GeneralFSError; 242 return DownloadResult::GeneralFSError;
243 if (!file.Resize(response->body.size())) 243 if (!file.Resize(response->body.size()))
@@ -290,7 +290,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
290 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); 290 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
291 291
292 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { 292 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
293 FileUtil::Delete(zip_path); 293 Common::FS::Delete(zip_path);
294 } 294 }
295 295
296 HandleDownloadDisplayResult(applet_manager, res); 296 HandleDownloadDisplayResult(applet_manager, res);
@@ -300,7 +300,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
300 300
301 progress.StartProcessingDataList(); 301 progress.StartProcessingDataList();
302 302
303 FileUtil::IOFile zip{zip_path, "rb"}; 303 Common::FS::IOFile zip{zip_path, "rb"};
304 const auto size = zip.GetSize(); 304 const auto size = zip.GetSize();
305 std::vector<u8> bytes(size); 305 std::vector<u8> bytes(size);
306 if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { 306 if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
@@ -365,8 +365,7 @@ bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress)
365 365
366 std::thread([this, title, &progress] { 366 std::thread([this, title, &progress] {
367 SynchronizeInternal(applet_manager, dir_getter, title, progress); 367 SynchronizeInternal(applet_manager, dir_getter, title, progress);
368 }) 368 }).detach();
369 .detach();
370 369
371 return true; 370 return true;
372} 371}
@@ -377,8 +376,7 @@ bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name,
377 376
378 std::thread([this, title, name, &progress] { 377 std::thread([this, title, name, &progress] {
379 SynchronizeInternal(applet_manager, dir_getter, title, progress, name); 378 SynchronizeInternal(applet_manager, dir_getter, title, progress, name);
380 }) 379 }).detach();
381 .detach();
382 380
383 return true; 381 return true;
384} 382}
@@ -422,7 +420,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
422 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); 420 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
423 421
424 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { 422 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
425 FileUtil::Delete(path); 423 Common::FS::Delete(path);
426 } 424 }
427 425
428 HandleDownloadDisplayResult(applet_manager, res); 426 HandleDownloadDisplayResult(applet_manager, res);
@@ -430,7 +428,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
430 } 428 }
431 } 429 }
432 430
433 FileUtil::IOFile bin{path, "rb"}; 431 Common::FS::IOFile bin{path, "rb"};
434 const auto size = bin.GetSize(); 432 const auto size = bin.GetSize();
435 std::vector<u8> bytes(size); 433 std::vector<u8> bytes(size);
436 if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { 434 if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 4490f8e4c..2cee1193c 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -36,7 +36,7 @@ constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
36 36
37static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, 37static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
38 std::string_view dir_name_) { 38 std::string_view dir_name_) {
39 std::string dir_name(FileUtil::SanitizePath(dir_name_)); 39 std::string dir_name(Common::FS::SanitizePath(dir_name_));
40 if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\") 40 if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
41 return base; 41 return base;
42 42
@@ -53,13 +53,13 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
53} 53}
54 54
55ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const { 55ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
56 std::string path(FileUtil::SanitizePath(path_)); 56 std::string path(Common::FS::SanitizePath(path_));
57 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 57 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
58 // dir can be nullptr if path contains subdirectories, create those prior to creating the file. 58 // dir can be nullptr if path contains subdirectories, create those prior to creating the file.
59 if (dir == nullptr) { 59 if (dir == nullptr) {
60 dir = backing->CreateSubdirectory(FileUtil::GetParentPath(path)); 60 dir = backing->CreateSubdirectory(Common::FS::GetParentPath(path));
61 } 61 }
62 auto file = dir->CreateFile(FileUtil::GetFilename(path)); 62 auto file = dir->CreateFile(Common::FS::GetFilename(path));
63 if (file == nullptr) { 63 if (file == nullptr) {
64 // TODO(DarkLordZach): Find a better error code for this 64 // TODO(DarkLordZach): Find a better error code for this
65 return RESULT_UNKNOWN; 65 return RESULT_UNKNOWN;
@@ -72,17 +72,17 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
72} 72}
73 73
74ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { 74ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
75 std::string path(FileUtil::SanitizePath(path_)); 75 std::string path(Common::FS::SanitizePath(path_));
76 if (path.empty()) { 76 if (path.empty()) {
77 // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but... 77 // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
78 return RESULT_SUCCESS; 78 return RESULT_SUCCESS;
79 } 79 }
80 80
81 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 81 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
82 if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) { 82 if (dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
83 return FileSys::ERROR_PATH_NOT_FOUND; 83 return FileSys::ERROR_PATH_NOT_FOUND;
84 } 84 }
85 if (!dir->DeleteFile(FileUtil::GetFilename(path))) { 85 if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
86 // TODO(DarkLordZach): Find a better error code for this 86 // TODO(DarkLordZach): Find a better error code for this
87 return RESULT_UNKNOWN; 87 return RESULT_UNKNOWN;
88 } 88 }
@@ -91,11 +91,11 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
91} 91}
92 92
93ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const { 93ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
94 std::string path(FileUtil::SanitizePath(path_)); 94 std::string path(Common::FS::SanitizePath(path_));
95 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 95 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
96 if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty()) 96 if (dir == nullptr && Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty())
97 dir = backing; 97 dir = backing;
98 auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path)); 98 auto new_dir = dir->CreateSubdirectory(Common::FS::GetFilename(path));
99 if (new_dir == nullptr) { 99 if (new_dir == nullptr) {
100 // TODO(DarkLordZach): Find a better error code for this 100 // TODO(DarkLordZach): Find a better error code for this
101 return RESULT_UNKNOWN; 101 return RESULT_UNKNOWN;
@@ -104,9 +104,9 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_)
104} 104}
105 105
106ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const { 106ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
107 std::string path(FileUtil::SanitizePath(path_)); 107 std::string path(Common::FS::SanitizePath(path_));
108 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 108 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
109 if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) { 109 if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
110 // TODO(DarkLordZach): Find a better error code for this 110 // TODO(DarkLordZach): Find a better error code for this
111 return RESULT_UNKNOWN; 111 return RESULT_UNKNOWN;
112 } 112 }
@@ -114,9 +114,9 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_)
114} 114}
115 115
116ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const { 116ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
117 std::string path(FileUtil::SanitizePath(path_)); 117 std::string path(Common::FS::SanitizePath(path_));
118 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 118 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
119 if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) { 119 if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
120 // TODO(DarkLordZach): Find a better error code for this 120 // TODO(DarkLordZach): Find a better error code for this
121 return RESULT_UNKNOWN; 121 return RESULT_UNKNOWN;
122 } 122 }
@@ -124,10 +124,10 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
124} 124}
125 125
126ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const { 126ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
127 const std::string sanitized_path(FileUtil::SanitizePath(path)); 127 const std::string sanitized_path(Common::FS::SanitizePath(path));
128 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(sanitized_path)); 128 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));
129 129
130 if (!dir->CleanSubdirectoryRecursive(FileUtil::GetFilename(sanitized_path))) { 130 if (!dir->CleanSubdirectoryRecursive(Common::FS::GetFilename(sanitized_path))) {
131 // TODO(DarkLordZach): Find a better error code for this 131 // TODO(DarkLordZach): Find a better error code for this
132 return RESULT_UNKNOWN; 132 return RESULT_UNKNOWN;
133 } 133 }
@@ -137,14 +137,14 @@ ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::stri
137 137
138ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, 138ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
139 const std::string& dest_path_) const { 139 const std::string& dest_path_) const {
140 std::string src_path(FileUtil::SanitizePath(src_path_)); 140 std::string src_path(Common::FS::SanitizePath(src_path_));
141 std::string dest_path(FileUtil::SanitizePath(dest_path_)); 141 std::string dest_path(Common::FS::SanitizePath(dest_path_));
142 auto src = backing->GetFileRelative(src_path); 142 auto src = backing->GetFileRelative(src_path);
143 if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { 143 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
144 // Use more-optimized vfs implementation rename. 144 // Use more-optimized vfs implementation rename.
145 if (src == nullptr) 145 if (src == nullptr)
146 return FileSys::ERROR_PATH_NOT_FOUND; 146 return FileSys::ERROR_PATH_NOT_FOUND;
147 if (!src->Rename(FileUtil::GetFilename(dest_path))) { 147 if (!src->Rename(Common::FS::GetFilename(dest_path))) {
148 // TODO(DarkLordZach): Find a better error code for this 148 // TODO(DarkLordZach): Find a better error code for this
149 return RESULT_UNKNOWN; 149 return RESULT_UNKNOWN;
150 } 150 }
@@ -162,7 +162,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
162 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), 162 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
163 "Could not write all of the bytes but everything else has succeded."); 163 "Could not write all of the bytes but everything else has succeded.");
164 164
165 if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) { 165 if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
166 // TODO(DarkLordZach): Find a better error code for this 166 // TODO(DarkLordZach): Find a better error code for this
167 return RESULT_UNKNOWN; 167 return RESULT_UNKNOWN;
168 } 168 }
@@ -172,14 +172,14 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
172 172
173ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, 173ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
174 const std::string& dest_path_) const { 174 const std::string& dest_path_) const {
175 std::string src_path(FileUtil::SanitizePath(src_path_)); 175 std::string src_path(Common::FS::SanitizePath(src_path_));
176 std::string dest_path(FileUtil::SanitizePath(dest_path_)); 176 std::string dest_path(Common::FS::SanitizePath(dest_path_));
177 auto src = GetDirectoryRelativeWrapped(backing, src_path); 177 auto src = GetDirectoryRelativeWrapped(backing, src_path);
178 if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { 178 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
179 // Use more-optimized vfs implementation rename. 179 // Use more-optimized vfs implementation rename.
180 if (src == nullptr) 180 if (src == nullptr)
181 return FileSys::ERROR_PATH_NOT_FOUND; 181 return FileSys::ERROR_PATH_NOT_FOUND;
182 if (!src->Rename(FileUtil::GetFilename(dest_path))) { 182 if (!src->Rename(Common::FS::GetFilename(dest_path))) {
183 // TODO(DarkLordZach): Find a better error code for this 183 // TODO(DarkLordZach): Find a better error code for this
184 return RESULT_UNKNOWN; 184 return RESULT_UNKNOWN;
185 } 185 }
@@ -198,7 +198,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
198 198
199ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_, 199ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
200 FileSys::Mode mode) const { 200 FileSys::Mode mode) const {
201 const std::string path(FileUtil::SanitizePath(path_)); 201 const std::string path(Common::FS::SanitizePath(path_));
202 std::string_view npath = path; 202 std::string_view npath = path;
203 while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) { 203 while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
204 npath.remove_prefix(1); 204 npath.remove_prefix(1);
@@ -218,7 +218,7 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::
218} 218}
219 219
220ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) { 220ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) {
221 std::string path(FileUtil::SanitizePath(path_)); 221 std::string path(Common::FS::SanitizePath(path_));
222 auto dir = GetDirectoryRelativeWrapped(backing, path); 222 auto dir = GetDirectoryRelativeWrapped(backing, path);
223 if (dir == nullptr) { 223 if (dir == nullptr) {
224 // TODO(DarkLordZach): Find a better error code for this 224 // TODO(DarkLordZach): Find a better error code for this
@@ -229,11 +229,11 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s
229 229
230ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( 230ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
231 const std::string& path_) const { 231 const std::string& path_) const {
232 std::string path(FileUtil::SanitizePath(path_)); 232 std::string path(Common::FS::SanitizePath(path_));
233 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 233 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
234 if (dir == nullptr) 234 if (dir == nullptr)
235 return FileSys::ERROR_PATH_NOT_FOUND; 235 return FileSys::ERROR_PATH_NOT_FOUND;
236 auto filename = FileUtil::GetFilename(path); 236 auto filename = Common::FS::GetFilename(path);
237 // TODO(Subv): Some games use the '/' path, find out what this means. 237 // TODO(Subv): Some games use the '/' path, find out what this means.
238 if (filename.empty()) 238 if (filename.empty())
239 return MakeResult(FileSys::EntryType::Directory); 239 return MakeResult(FileSys::EntryType::Directory);
@@ -695,13 +695,13 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
695 sdmc_factory = nullptr; 695 sdmc_factory = nullptr;
696 } 696 }
697 697
698 auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), 698 auto nand_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir),
699 FileSys::Mode::ReadWrite); 699 FileSys::Mode::ReadWrite);
700 auto sd_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), 700 auto sd_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir),
701 FileSys::Mode::ReadWrite); 701 FileSys::Mode::ReadWrite);
702 auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), 702 auto load_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
703 FileSys::Mode::ReadWrite); 703 FileSys::Mode::ReadWrite);
704 auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), 704 auto dump_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir),
705 FileSys::Mode::ReadWrite); 705 FileSys::Mode::ReadWrite);
706 706
707 if (bis_factory == nullptr) { 707 if (bis_factory == nullptr) {
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index ef67ad690..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/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index caca80dde..637b310d7 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -24,13 +24,13 @@ BufferQueue::~BufferQueue() = default;
24void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { 24void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
25 LOG_WARNING(Service, "Adding graphics buffer {}", slot); 25 LOG_WARNING(Service, "Adding graphics buffer {}", slot);
26 26
27 Buffer buffer{};
28 buffer.slot = slot;
29 buffer.igbp_buffer = igbp_buffer;
30 buffer.status = Buffer::Status::Free;
31 free_buffers.push_back(slot); 27 free_buffers.push_back(slot);
28 queue.push_back({
29 .slot = slot,
30 .status = Buffer::Status::Free,
31 .igbp_buffer = igbp_buffer,
32 });
32 33
33 queue.emplace_back(buffer);
34 buffer_wait_event.writable->Signal(); 34 buffer_wait_event.writable->Signal();
35} 35}
36 36
@@ -38,7 +38,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue
38 u32 height) { 38 u32 height) {
39 39
40 if (free_buffers.empty()) { 40 if (free_buffers.empty()) {
41 return {}; 41 return std::nullopt;
42 } 42 }
43 43
44 auto f_itr = free_buffers.begin(); 44 auto f_itr = free_buffers.begin();
@@ -69,7 +69,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue
69 } 69 }
70 70
71 if (itr == queue.end()) { 71 if (itr == queue.end()) {
72 return {}; 72 return std::nullopt;
73 } 73 }
74 74
75 itr->status = Buffer::Status::Dequeued; 75 itr->status = Buffer::Status::Dequeued;
@@ -103,14 +103,15 @@ std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::Ac
103 auto itr = queue.end(); 103 auto itr = queue.end();
104 // Iterate to find a queued buffer matching the requested slot. 104 // Iterate to find a queued buffer matching the requested slot.
105 while (itr == queue.end() && !queue_sequence.empty()) { 105 while (itr == queue.end() && !queue_sequence.empty()) {
106 u32 slot = queue_sequence.front(); 106 const u32 slot = queue_sequence.front();
107 itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) { 107 itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) {
108 return buffer.status == Buffer::Status::Queued && buffer.slot == slot; 108 return buffer.status == Buffer::Status::Queued && buffer.slot == slot;
109 }); 109 });
110 queue_sequence.pop_front(); 110 queue_sequence.pop_front();
111 } 111 }
112 if (itr == queue.end()) 112 if (itr == queue.end()) {
113 return {}; 113 return std::nullopt;
114 }
114 itr->status = Buffer::Status::Acquired; 115 itr->status = Buffer::Status::Acquired;
115 return *itr; 116 return *itr;
116} 117}
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index ff85cbba6..1ebe949c0 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -86,11 +86,13 @@ public:
86 86
87 [[nodiscard]] s64 GetNextTicks() const; 87 [[nodiscard]] s64 GetNextTicks() const;
88 88
89 [[nodiscard]] std::unique_lock<std::mutex> Lock() const { return std::unique_lock{*guard}; } 89 [[nodiscard]] std::unique_lock<std::mutex> Lock() const {
90 return std::unique_lock{*guard};
91 }
90 92
91 private : 93private:
92 /// Finds the display identified by the specified ID. 94 /// Finds the display identified by the specified ID.
93 [[nodiscard]] VI::Display* FindDisplay(u64 display_id); 95 [[nodiscard]] VI::Display* FindDisplay(u64 display_id);
94 96
95 /// Finds the display identified by the specified ID. 97 /// Finds the display identified by the specified ID.
96 [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const; 98 [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const;
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index b526a94fe..aabf166b7 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -57,7 +57,7 @@ public:
57 ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name); 57 ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name);
58 ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name); 58 ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name);
59 59
60 template <Common::IsBaseOf<Kernel::SessionRequestHandler> T> 60 template <Common::DerivedFrom<Kernel::SessionRequestHandler> T>
61 std::shared_ptr<T> GetService(const std::string& service_name) const { 61 std::shared_ptr<T> GetService(const std::string& service_name) const {
62 auto service = registered_services.find(service_name); 62 auto service = registered_services.find(service_name);
63 if (service == registered_services.end()) { 63 if (service == registered_services.end()) {
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
index c070d6e97..320672add 100644
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -73,10 +73,8 @@ TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::
73 73
74 std::string location_name; 74 std::string location_name;
75 const auto timezone_setting = Settings::GetTimeZoneString(); 75 const auto timezone_setting = Settings::GetTimeZoneString();
76 if (timezone_setting == "auto") { 76 if (timezone_setting == "auto" || timezone_setting == "default") {
77 location_name = Common::TimeZone::GetDefaultTimeZone(); 77 location_name = Common::TimeZone::GetDefaultTimeZone();
78 } else if (timezone_setting == "default") {
79 location_name = location_name;
80 } else { 78 } else {
81 location_name = timezone_setting; 79 location_name = timezone_setting;
82 } 80 }
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 59ca7091a..9bc3a8840 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -3,8 +3,10 @@
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#include <optional>
6#include <ostream> 7#include <ostream>
7#include <string> 8#include <string>
9#include "common/concepts.h"
8#include "common/file_util.h" 10#include "common/file_util.h"
9#include "common/logging/log.h" 11#include "common/logging/log.h"
10#include "common/string_util.h" 12#include "common/string_util.h"
@@ -21,27 +23,41 @@
21 23
22namespace Loader { 24namespace Loader {
23 25
24FileType IdentifyFile(FileSys::VirtualFile file) { 26namespace {
25 FileType type;
26
27#define CHECK_TYPE(loader) \
28 type = AppLoader_##loader::IdentifyType(file); \
29 if (FileType::Error != type) \
30 return type;
31 27
32 CHECK_TYPE(DeconstructedRomDirectory) 28template <Common::DerivedFrom<AppLoader> T>
33 CHECK_TYPE(ELF) 29std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
34 CHECK_TYPE(NSO) 30 const auto file_type = T::IdentifyType(file);
35 CHECK_TYPE(NRO) 31 if (file_type != FileType::Error) {
36 CHECK_TYPE(NCA) 32 return file_type;
37 CHECK_TYPE(XCI) 33 }
38 CHECK_TYPE(NAX) 34 return std::nullopt;
39 CHECK_TYPE(NSP) 35}
40 CHECK_TYPE(KIP)
41 36
42#undef CHECK_TYPE 37} // namespace
43 38
44 return FileType::Unknown; 39FileType IdentifyFile(FileSys::VirtualFile file) {
40 if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) {
41 return *romdir_type;
42 } else if (const auto elf_type = IdentifyFileLoader<AppLoader_ELF>(file)) {
43 return *elf_type;
44 } else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) {
45 return *nso_type;
46 } else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) {
47 return *nro_type;
48 } else if (const auto nca_type = IdentifyFileLoader<AppLoader_NCA>(file)) {
49 return *nca_type;
50 } else if (const auto xci_type = IdentifyFileLoader<AppLoader_XCI>(file)) {
51 return *xci_type;
52 } else if (const auto nax_type = IdentifyFileLoader<AppLoader_NAX>(file)) {
53 return *nax_type;
54 } else if (const auto nsp_type = IdentifyFileLoader<AppLoader_NSP>(file)) {
55 return *nsp_type;
56 } else if (const auto kip_type = IdentifyFileLoader<AppLoader_KIP>(file)) {
57 return *kip_type;
58 } else {
59 return FileType::Unknown;
60 }
45} 61}
46 62
47FileType GuessFromFilename(const std::string& name) { 63FileType GuessFromFilename(const std::string& name) {
@@ -51,7 +67,7 @@ FileType GuessFromFilename(const std::string& name) {
51 return FileType::NCA; 67 return FileType::NCA;
52 68
53 const std::string extension = 69 const std::string extension =
54 Common::ToLower(std::string(FileUtil::GetExtensionFromFilename(name))); 70 Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
55 71
56 if (extension == "elf") 72 if (extension == "elf")
57 return FileType::ELF; 73 return FileType::ELF;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 2c5588933..86d17c6cb 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -704,7 +704,7 @@ struct Memory::Impl {
704 u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; 704 u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
705 if (page_pointer != nullptr) { 705 if (page_pointer != nullptr) {
706 // NOTE: Avoid adding any extra logic to this fast-path block 706 // NOTE: Avoid adding any extra logic to this fast-path block
707 T volatile* pointer = reinterpret_cast<T volatile*>(&page_pointer[vaddr]); 707 auto* pointer = reinterpret_cast<volatile T*>(&page_pointer[vaddr]);
708 return Common::AtomicCompareAndSwap(pointer, data, expected); 708 return Common::AtomicCompareAndSwap(pointer, data, expected);
709 } 709 }
710 710
@@ -720,9 +720,8 @@ struct Memory::Impl {
720 case Common::PageType::RasterizerCachedMemory: { 720 case Common::PageType::RasterizerCachedMemory: {
721 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; 721 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
722 system.GPU().InvalidateRegion(vaddr, sizeof(T)); 722 system.GPU().InvalidateRegion(vaddr, sizeof(T));
723 T volatile* pointer = reinterpret_cast<T volatile*>(&host_ptr); 723 auto* pointer = reinterpret_cast<volatile T*>(&host_ptr);
724 return Common::AtomicCompareAndSwap(pointer, data, expected); 724 return Common::AtomicCompareAndSwap(pointer, data, expected);
725 break;
726 } 725 }
727 default: 726 default:
728 UNREACHABLE(); 727 UNREACHABLE();
@@ -734,7 +733,7 @@ struct Memory::Impl {
734 u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; 733 u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
735 if (page_pointer != nullptr) { 734 if (page_pointer != nullptr) {
736 // NOTE: Avoid adding any extra logic to this fast-path block 735 // NOTE: Avoid adding any extra logic to this fast-path block
737 u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&page_pointer[vaddr]); 736 auto* pointer = reinterpret_cast<volatile u64*>(&page_pointer[vaddr]);
738 return Common::AtomicCompareAndSwap(pointer, data, expected); 737 return Common::AtomicCompareAndSwap(pointer, data, expected);
739 } 738 }
740 739
@@ -750,9 +749,8 @@ struct Memory::Impl {
750 case Common::PageType::RasterizerCachedMemory: { 749 case Common::PageType::RasterizerCachedMemory: {
751 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; 750 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
752 system.GPU().InvalidateRegion(vaddr, sizeof(u128)); 751 system.GPU().InvalidateRegion(vaddr, sizeof(u128));
753 u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&host_ptr); 752 auto* pointer = reinterpret_cast<volatile u64*>(&host_ptr);
754 return Common::AtomicCompareAndSwap(pointer, data, expected); 753 return Common::AtomicCompareAndSwap(pointer, data, expected);
755 break;
756 } 754 }
757 default: 755 default:
758 UNREACHABLE(); 756 UNREACHABLE();
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index eeebdf02e..e503118dd 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -42,7 +42,7 @@ u64 StandardVmCallbacks::HidKeysDown() {
42 if (applet_resource == nullptr) { 42 if (applet_resource == nullptr) {
43 LOG_WARNING(CheatEngine, 43 LOG_WARNING(CheatEngine,
44 "Attempted to read input state, but applet resource is not initialized!"); 44 "Attempted to read input state, but applet resource is not initialized!");
45 return false; 45 return 0;
46 } 46 }
47 47
48 const auto press_state = 48 const auto press_state =
@@ -199,17 +199,29 @@ void CheatEngine::Initialize() {
199 metadata.title_id = system.CurrentProcess()->GetTitleID(); 199 metadata.title_id = system.CurrentProcess()->GetTitleID();
200 200
201 const auto& page_table = system.CurrentProcess()->PageTable(); 201 const auto& page_table = system.CurrentProcess()->PageTable();
202 metadata.heap_extents = {page_table.GetHeapRegionStart(), page_table.GetHeapRegionSize()}; 202 metadata.heap_extents = {
203 metadata.address_space_extents = {page_table.GetAddressSpaceStart(), 203 .base = page_table.GetHeapRegionStart(),
204 page_table.GetAddressSpaceSize()}; 204 .size = page_table.GetHeapRegionSize(),
205 metadata.alias_extents = {page_table.GetAliasCodeRegionStart(), 205 };
206 page_table.GetAliasCodeRegionSize()}; 206
207 metadata.address_space_extents = {
208 .base = page_table.GetAddressSpaceStart(),
209 .size = page_table.GetAddressSpaceSize(),
210 };
211
212 metadata.alias_extents = {
213 .base = page_table.GetAliasCodeRegionStart(),
214 .size = page_table.GetAliasCodeRegionSize(),
215 };
207 216
208 is_pending_reload.exchange(true); 217 is_pending_reload.exchange(true);
209} 218}
210 219
211void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) { 220void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
212 metadata.main_nso_extents = {main_region_begin, main_region_size}; 221 metadata.main_nso_extents = {
222 .base = main_region_begin,
223 .size = main_region_size,
224 };
213} 225}
214 226
215void CheatEngine::Reload(std::vector<CheatEntry> cheats) { 227void CheatEngine::Reload(std::vector<CheatEntry> cheats) {
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index b899ac884..b93396a80 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -38,11 +38,11 @@ PerfStats::~PerfStats() {
38 std::ostringstream stream; 38 std::ostringstream stream;
39 std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index, 39 std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index,
40 std::ostream_iterator<double>(stream, "\n")); 40 std::ostream_iterator<double>(stream, "\n"));
41 const std::string& path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 41 const std::string& path = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
42 // %F Date format expanded is "%Y-%m-%d" 42 // %F Date format expanded is "%Y-%m-%d"
43 const std::string filename = 43 const std::string filename =
44 fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id); 44 fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id);
45 FileUtil::IOFile file(filename, "w"); 45 Common::FS::IOFile file(filename, "w");
46 file.WriteString(stream.str()); 46 file.WriteString(stream.str());
47} 47}
48 48
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index 76cfa5a17..0becdf642 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -28,8 +28,9 @@
28namespace { 28namespace {
29 29
30std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) { 30std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) {
31 return fmt::format("{}{}/{:016X}_{}.json", FileUtil::GetUserPath(FileUtil::UserPath::LogDir), 31 return fmt::format("{}{}/{:016X}_{}.json",
32 type, title_id, timestamp); 32 Common::FS::GetUserPath(Common::FS::UserPath::LogDir), type, title_id,
33 timestamp);
33} 34}
34 35
35std::string GetTimestamp() { 36std::string GetTimestamp() {
@@ -40,13 +41,13 @@ std::string GetTimestamp() {
40using namespace nlohmann; 41using namespace nlohmann;
41 42
42void SaveToFile(json json, const std::string& filename) { 43void SaveToFile(json json, const std::string& filename) {
43 if (!FileUtil::CreateFullPath(filename)) { 44 if (!Common::FS::CreateFullPath(filename)) {
44 LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename); 45 LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename);
45 return; 46 return;
46 } 47 }
47 48
48 std::ofstream file( 49 std::ofstream file(
49 FileUtil::SanitizePath(filename, FileUtil::DirectorySeparator::PlatformDefault)); 50 Common::FS::SanitizePath(filename, Common::FS::DirectorySeparator::PlatformDefault));
50 file << std::setw(4) << json << std::endl; 51 file << std::setw(4) << json << std::endl;
51} 52}
52 53
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 416b2d866..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/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 5a30c75da..7dae48bc6 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -70,12 +70,12 @@ static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) {
70 70
71u64 GetTelemetryId() { 71u64 GetTelemetryId() {
72 u64 telemetry_id{}; 72 u64 telemetry_id{};
73 const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + 73 const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) +
74 "telemetry_id"}; 74 "telemetry_id"};
75 75
76 bool generate_new_id = !FileUtil::Exists(filename); 76 bool generate_new_id = !Common::FS::Exists(filename);
77 if (!generate_new_id) { 77 if (!generate_new_id) {
78 FileUtil::IOFile file(filename, "rb"); 78 Common::FS::IOFile file(filename, "rb");
79 if (!file.IsOpen()) { 79 if (!file.IsOpen()) {
80 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 80 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
81 return {}; 81 return {};
@@ -88,7 +88,7 @@ u64 GetTelemetryId() {
88 } 88 }
89 89
90 if (generate_new_id) { 90 if (generate_new_id) {
91 FileUtil::IOFile file(filename, "wb"); 91 Common::FS::IOFile file(filename, "wb");
92 if (!file.IsOpen()) { 92 if (!file.IsOpen()) {
93 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 93 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
94 return {}; 94 return {};
@@ -102,10 +102,10 @@ u64 GetTelemetryId() {
102 102
103u64 RegenerateTelemetryId() { 103u64 RegenerateTelemetryId() {
104 const u64 new_telemetry_id{GenerateTelemetryId()}; 104 const u64 new_telemetry_id{GenerateTelemetryId()};
105 const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + 105 const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) +
106 "telemetry_id"}; 106 "telemetry_id"};
107 107
108 FileUtil::IOFile file(filename, "wb"); 108 Common::FS::IOFile file(filename, "wb");
109 if (!file.IsOpen()) { 109 if (!file.IsOpen()) {
110 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 110 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
111 return {}; 111 return {};
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 2003e096f..5c674a099 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -107,28 +107,21 @@ void Freezer::Unfreeze(VAddr address) {
107 107
108 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address); 108 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
109 109
110 entries.erase( 110 std::erase_if(entries, [address](const Entry& entry) { return entry.address == address; });
111 std::remove_if(entries.begin(), entries.end(),
112 [&address](const Entry& entry) { return entry.address == address; }),
113 entries.end());
114} 111}
115 112
116bool Freezer::IsFrozen(VAddr address) const { 113bool Freezer::IsFrozen(VAddr address) const {
117 std::lock_guard lock{entries_mutex}; 114 std::lock_guard lock{entries_mutex};
118 115
119 return std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { 116 return FindEntry(address) != entries.cend();
120 return entry.address == address;
121 }) != entries.end();
122} 117}
123 118
124void Freezer::SetFrozenValue(VAddr address, u64 value) { 119void Freezer::SetFrozenValue(VAddr address, u64 value) {
125 std::lock_guard lock{entries_mutex}; 120 std::lock_guard lock{entries_mutex};
126 121
127 const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { 122 const auto iter = FindEntry(address);
128 return entry.address == address;
129 });
130 123
131 if (iter == entries.end()) { 124 if (iter == entries.cend()) {
132 LOG_ERROR(Common_Memory, 125 LOG_ERROR(Common_Memory,
133 "Tried to set freeze value for address={:016X} that is not frozen!", address); 126 "Tried to set freeze value for address={:016X} that is not frozen!", address);
134 return; 127 return;
@@ -143,11 +136,9 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) {
143std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { 136std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
144 std::lock_guard lock{entries_mutex}; 137 std::lock_guard lock{entries_mutex};
145 138
146 const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { 139 const auto iter = FindEntry(address);
147 return entry.address == address;
148 });
149 140
150 if (iter == entries.end()) { 141 if (iter == entries.cend()) {
151 return std::nullopt; 142 return std::nullopt;
152 } 143 }
153 144
@@ -160,6 +151,16 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const {
160 return entries; 151 return entries;
161} 152}
162 153
154Freezer::Entries::iterator Freezer::FindEntry(VAddr address) {
155 return std::find_if(entries.begin(), entries.end(),
156 [address](const Entry& entry) { return entry.address == address; });
157}
158
159Freezer::Entries::const_iterator Freezer::FindEntry(VAddr address) const {
160 return std::find_if(entries.begin(), entries.end(),
161 [address](const Entry& entry) { return entry.address == address; });
162}
163
163void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { 164void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
164 if (!IsActive()) { 165 if (!IsActive()) {
165 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); 166 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h
index 2b2326bc4..0fdb701a7 100644
--- a/src/core/tools/freezer.h
+++ b/src/core/tools/freezer.h
@@ -73,13 +73,18 @@ public:
73 std::vector<Entry> GetEntries() const; 73 std::vector<Entry> GetEntries() const;
74 74
75private: 75private:
76 using Entries = std::vector<Entry>;
77
78 Entries::iterator FindEntry(VAddr address);
79 Entries::const_iterator FindEntry(VAddr address) const;
80
76 void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 81 void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
77 void FillEntryReads(); 82 void FillEntryReads();
78 83
79 std::atomic_bool active{false}; 84 std::atomic_bool active{false};
80 85
81 mutable std::mutex entries_mutex; 86 mutable std::mutex entries_mutex;
82 std::vector<Entry> entries; 87 Entries entries;
83 88
84 std::shared_ptr<Core::Timing::EventType> event; 89 std::shared_ptr<Core::Timing::EventType> event;
85 Core::Timing::CoreTiming& core_timing; 90 Core::Timing::CoreTiming& core_timing;
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index f45983f3f..b346fdf8e 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -148,19 +148,17 @@ void GCButtonFactory::EndConfiguration() {
148 148
149class GCAnalog final : public Input::AnalogDevice { 149class GCAnalog final : public Input::AnalogDevice {
150public: 150public:
151 GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter) 151 GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter,
152 float range_)
152 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), 153 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
153 origin_value_x(adapter->GetOriginValue(port_, axis_x_)), 154 origin_value_x(adapter->GetOriginValue(port_, axis_x_)),
154 origin_value_y(adapter->GetOriginValue(port_, axis_y_)) {} 155 origin_value_y(adapter->GetOriginValue(port_, axis_y_)), range(range_) {}
155 156
156 float GetAxis(int axis) const { 157 float GetAxis(int axis) const {
157 if (gcadapter->DeviceConnected(port)) { 158 if (gcadapter->DeviceConnected(port)) {
158 std::lock_guard lock{mutex}; 159 std::lock_guard lock{mutex};
159 const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y; 160 const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
160 // division is not by a perfect 128 to account for some variance in center location 161 return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / (100.0f * range);
161 // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
162 // [20-230]
163 return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / 95.0f;
164 } 162 }
165 return 0.0f; 163 return 0.0f;
166 } 164 }
@@ -215,6 +213,7 @@ private:
215 GCAdapter::Adapter* gcadapter; 213 GCAdapter::Adapter* gcadapter;
216 const float origin_value_x; 214 const float origin_value_x;
217 const float origin_value_y; 215 const float origin_value_y;
216 const float range;
218 mutable std::mutex mutex; 217 mutable std::mutex mutex;
219}; 218};
220 219
@@ -234,8 +233,9 @@ std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::Param
234 const int axis_x = params.Get("axis_x", 0); 233 const int axis_x = params.Get("axis_x", 0);
235 const int axis_y = params.Get("axis_y", 1); 234 const int axis_y = params.Get("axis_y", 1);
236 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); 235 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
236 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
237 237
238 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get()); 238 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range);
239} 239}
240 240
241void GCAnalogFactory::BeginConfiguration() { 241void GCAnalogFactory::BeginConfiguration() {
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 675b477fa..d76c279d3 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -66,14 +66,14 @@ public:
66 state.axes.insert_or_assign(axis, value); 66 state.axes.insert_or_assign(axis, value);
67 } 67 }
68 68
69 float GetAxis(int axis) const { 69 float GetAxis(int axis, float range) const {
70 std::lock_guard lock{mutex}; 70 std::lock_guard lock{mutex};
71 return state.axes.at(axis) / 32767.0f; 71 return state.axes.at(axis) / (32767.0f * range);
72 } 72 }
73 73
74 std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const { 74 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const {
75 float x = GetAxis(axis_x); 75 float x = GetAxis(axis_x, range);
76 float y = GetAxis(axis_y); 76 float y = GetAxis(axis_y, range);
77 y = -y; // 3DS uses an y-axis inverse from SDL 77 y = -y; // 3DS uses an y-axis inverse from SDL
78 78
79 // Make sure the coordinates are in the unit circle, 79 // Make sure the coordinates are in the unit circle,
@@ -313,7 +313,7 @@ public:
313 trigger_if_greater(trigger_if_greater_) {} 313 trigger_if_greater(trigger_if_greater_) {}
314 314
315 bool GetStatus() const override { 315 bool GetStatus() const override {
316 const float axis_value = joystick->GetAxis(axis); 316 const float axis_value = joystick->GetAxis(axis, 1.0f);
317 if (trigger_if_greater) { 317 if (trigger_if_greater) {
318 return axis_value > threshold; 318 return axis_value > threshold;
319 } 319 }
@@ -329,11 +329,13 @@ private:
329 329
330class SDLAnalog final : public Input::AnalogDevice { 330class SDLAnalog final : public Input::AnalogDevice {
331public: 331public:
332 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_) 332 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_,
333 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_) {} 333 float range_)
334 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_),
335 range(range_) {}
334 336
335 std::tuple<float, float> GetStatus() const override { 337 std::tuple<float, float> GetStatus() const override {
336 const auto [x, y] = joystick->GetAnalog(axis_x, axis_y); 338 const auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range);
337 const float r = std::sqrt((x * x) + (y * y)); 339 const float r = std::sqrt((x * x) + (y * y));
338 if (r > deadzone) { 340 if (r > deadzone) {
339 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone), 341 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
@@ -363,6 +365,7 @@ private:
363 const int axis_x; 365 const int axis_x;
364 const int axis_y; 366 const int axis_y;
365 const float deadzone; 367 const float deadzone;
368 const float range;
366}; 369};
367 370
368/// A button device factory that creates button devices from SDL joystick 371/// A button device factory that creates button devices from SDL joystick
@@ -458,13 +461,13 @@ public:
458 const int axis_x = params.Get("axis_x", 0); 461 const int axis_x = params.Get("axis_x", 0);
459 const int axis_y = params.Get("axis_y", 1); 462 const int axis_y = params.Get("axis_y", 1);
460 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); 463 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
461 464 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
462 auto joystick = state.GetSDLJoystickByGUID(guid, port); 465 auto joystick = state.GetSDLJoystickByGUID(guid, port);
463 466
464 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash 467 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
465 joystick->SetAxis(axis_x, 0); 468 joystick->SetAxis(axis_x, 0);
466 joystick->SetAxis(axis_y, 0); 469 joystick->SetAxis(axis_y, 0);
467 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone); 470 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone, range);
468 } 471 }
469 472
470private: 473private:
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index 6c95a8b42..3f4eaf448 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -224,8 +224,7 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie
224 } else { 224 } else {
225 failure_callback(); 225 failure_callback();
226 } 226 }
227 }) 227 }).detach();
228 .detach();
229} 228}
230 229
231CalibrationConfigurationJob::CalibrationConfigurationJob( 230CalibrationConfigurationJob::CalibrationConfigurationJob(
@@ -279,8 +278,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
279 complete_event.Wait(); 278 complete_event.Wait();
280 socket.Stop(); 279 socket.Stop();
281 worker_thread.join(); 280 worker_thread.join();
282 }) 281 }).detach();
283 .detach();
284} 282}
285 283
286CalibrationConfigurationJob::~CalibrationConfigurationJob() { 284CalibrationConfigurationJob::~CalibrationConfigurationJob() {
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index ef1618990..c97eeb792 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -647,7 +647,7 @@ public:
647 GetX() + GetWidth(), // right 647 GetX() + GetWidth(), // right
648 GetY() // bottom 648 GetY() // bottom
649 }; 649 };
650 }; 650 }
651 651
652 f32 GetX() const { 652 f32 GetX() const {
653 return std::max(0.0f, translate_x - std::fabs(scale_x)); 653 return std::max(0.0f, translate_x - std::fabs(scale_x));
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/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_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index be71e1733..eb49a36bf 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -403,7 +403,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
403 } 403 }
404 }; 404 };
405 405
406 const auto num_workers{static_cast<std::size_t>(std::thread::hardware_concurrency() + 1ULL)}; 406 const std::size_t num_workers{std::max(1U, std::thread::hardware_concurrency())};
407 const std::size_t bucket_size{transferable->size() / num_workers}; 407 const std::size_t bucket_size{transferable->size() / num_workers};
408 std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers); 408 std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers);
409 std::vector<std::thread> threads(num_workers); 409 std::vector<std::thread> threads(num_workers);
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..52fbab3c1 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) {
@@ -217,7 +217,7 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
217 return {}; 217 return {};
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;
@@ -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());
@@ -317,7 +317,7 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo
317} 317}
318 318
319void ShaderDiskCacheOpenGL::InvalidateTransferable() { 319void ShaderDiskCacheOpenGL::InvalidateTransferable() {
320 if (!FileUtil::Delete(GetTransferablePath())) { 320 if (!Common::FS::Delete(GetTransferablePath())) {
321 LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}", 321 LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}",
322 GetTransferablePath()); 322 GetTransferablePath());
323 } 323 }
@@ -328,7 +328,7 @@ void ShaderDiskCacheOpenGL::InvalidatePrecompiled() {
328 // Clear virtaul precompiled cache file 328 // Clear virtaul precompiled cache file
329 precompiled_cache_virtual_file.Resize(0); 329 precompiled_cache_virtual_file.Resize(0);
330 330
331 if (!FileUtil::Delete(GetPrecompiledPath())) { 331 if (!Common::FS::Delete(GetPrecompiledPath())) {
332 LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath()); 332 LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath());
333 } 333 }
334} 334}
@@ -344,7 +344,7 @@ void ShaderDiskCacheOpenGL::SaveEntry(const ShaderDiskCacheEntry& entry) {
344 return; 344 return;
345 } 345 }
346 346
347 FileUtil::IOFile file = AppendTransferableFile(); 347 Common::FS::IOFile file = AppendTransferableFile();
348 if (!file.IsOpen()) { 348 if (!file.IsOpen()) {
349 return; 349 return;
350 } 350 }
@@ -386,15 +386,15 @@ void ShaderDiskCacheOpenGL::SavePrecompiled(u64 unique_identifier, GLuint progra
386 } 386 }
387} 387}
388 388
389FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const { 389Common::FS::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const {
390 if (!EnsureDirectories()) { 390 if (!EnsureDirectories()) {
391 return {}; 391 return {};
392 } 392 }
393 393
394 const auto transferable_path{GetTransferablePath()}; 394 const auto transferable_path{GetTransferablePath()};
395 const bool existed = FileUtil::Exists(transferable_path); 395 const bool existed = Common::FS::Exists(transferable_path);
396 396
397 FileUtil::IOFile file(transferable_path, "ab"); 397 Common::FS::IOFile file(transferable_path, "ab");
398 if (!file.IsOpen()) { 398 if (!file.IsOpen()) {
399 LOG_ERROR(Render_OpenGL, "Failed to open transferable cache in path={}", transferable_path); 399 LOG_ERROR(Render_OpenGL, "Failed to open transferable cache in path={}", transferable_path);
400 return {}; 400 return {};
@@ -426,7 +426,7 @@ void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() {
426 Common::Compression::CompressDataZSTDDefault(uncompressed.data(), uncompressed.size()); 426 Common::Compression::CompressDataZSTDDefault(uncompressed.data(), uncompressed.size());
427 427
428 const auto precompiled_path{GetPrecompiledPath()}; 428 const auto precompiled_path{GetPrecompiledPath()};
429 FileUtil::IOFile file(precompiled_path, "wb"); 429 Common::FS::IOFile file(precompiled_path, "wb");
430 430
431 if (!file.IsOpen()) { 431 if (!file.IsOpen()) {
432 LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path); 432 LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path);
@@ -440,24 +440,24 @@ void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() {
440 440
441bool ShaderDiskCacheOpenGL::EnsureDirectories() const { 441bool ShaderDiskCacheOpenGL::EnsureDirectories() const {
442 const auto CreateDir = [](const std::string& dir) { 442 const auto CreateDir = [](const std::string& dir) {
443 if (!FileUtil::CreateDir(dir)) { 443 if (!Common::FS::CreateDir(dir)) {
444 LOG_ERROR(Render_OpenGL, "Failed to create directory={}", dir); 444 LOG_ERROR(Render_OpenGL, "Failed to create directory={}", dir);
445 return false; 445 return false;
446 } 446 }
447 return true; 447 return true;
448 }; 448 };
449 449
450 return CreateDir(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) && 450 return CreateDir(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)) &&
451 CreateDir(GetBaseDir()) && CreateDir(GetTransferableDir()) && 451 CreateDir(GetBaseDir()) && CreateDir(GetTransferableDir()) &&
452 CreateDir(GetPrecompiledDir()); 452 CreateDir(GetPrecompiledDir());
453} 453}
454 454
455std::string ShaderDiskCacheOpenGL::GetTransferablePath() const { 455std::string ShaderDiskCacheOpenGL::GetTransferablePath() const {
456 return FileUtil::SanitizePath(GetTransferableDir() + DIR_SEP_CHR + GetTitleID() + ".bin"); 456 return Common::FS::SanitizePath(GetTransferableDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
457} 457}
458 458
459std::string ShaderDiskCacheOpenGL::GetPrecompiledPath() const { 459std::string ShaderDiskCacheOpenGL::GetPrecompiledPath() const {
460 return FileUtil::SanitizePath(GetPrecompiledDir() + DIR_SEP_CHR + GetTitleID() + ".bin"); 460 return Common::FS::SanitizePath(GetPrecompiledDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
461} 461}
462 462
463std::string ShaderDiskCacheOpenGL::GetTransferableDir() const { 463std::string ShaderDiskCacheOpenGL::GetTransferableDir() const {
@@ -469,7 +469,7 @@ std::string ShaderDiskCacheOpenGL::GetPrecompiledDir() const {
469} 469}
470 470
471std::string ShaderDiskCacheOpenGL::GetBaseDir() const { 471std::string ShaderDiskCacheOpenGL::GetBaseDir() const {
472 return FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + DIR_SEP "opengl"; 472 return Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir) + DIR_SEP "opengl";
473} 473}
474 474
475std::string ShaderDiskCacheOpenGL::GetTitleID() const { 475std::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_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..0c62c8061 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
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..936f76195 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() {
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/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index 14cac38ea..c43d60adf 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,
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h
index 71daac9d7..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
@@ -756,8 +756,8 @@ public:
756 } 756 }
757 757
758 VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, 758 VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size,
759 void* data, VkDeviceSize stride, VkQueryResultFlags flags) const 759 void* data, VkDeviceSize stride,
760 noexcept { 760 VkQueryResultFlags flags) const noexcept {
761 return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride, 761 return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
762 flags); 762 flags);
763 } 763 }
@@ -849,8 +849,8 @@ public:
849 dld->vkCmdBindPipeline(handle, bind_point, pipeline); 849 dld->vkCmdBindPipeline(handle, bind_point, pipeline);
850 } 850 }
851 851
852 void BindIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType index_type) const 852 void BindIndexBuffer(VkBuffer buffer, VkDeviceSize offset,
853 noexcept { 853 VkIndexType index_type) const noexcept {
854 dld->vkCmdBindIndexBuffer(handle, buffer, offset, index_type); 854 dld->vkCmdBindIndexBuffer(handle, buffer, offset, index_type);
855 } 855 }
856 856
@@ -863,8 +863,8 @@ public:
863 BindVertexBuffers(binding, 1, &buffer, &offset); 863 BindVertexBuffers(binding, 1, &buffer, &offset);
864 } 864 }
865 865
866 void Draw(u32 vertex_count, u32 instance_count, u32 first_vertex, u32 first_instance) const 866 void Draw(u32 vertex_count, u32 instance_count, u32 first_vertex,
867 noexcept { 867 u32 first_instance) const noexcept {
868 dld->vkCmdDraw(handle, vertex_count, instance_count, first_vertex, first_instance); 868 dld->vkCmdDraw(handle, vertex_count, instance_count, first_vertex, first_instance);
869 } 869 }
870 870
@@ -874,15 +874,15 @@ public:
874 first_instance); 874 first_instance);
875 } 875 }
876 876
877 void ClearAttachments(Span<VkClearAttachment> attachments, Span<VkClearRect> rects) const 877 void ClearAttachments(Span<VkClearAttachment> attachments,
878 noexcept { 878 Span<VkClearRect> rects) const noexcept {
879 dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(), 879 dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(),
880 rects.data()); 880 rects.data());
881 } 881 }
882 882
883 void BlitImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image, 883 void BlitImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image,
884 VkImageLayout dst_layout, Span<VkImageBlit> regions, VkFilter filter) const 884 VkImageLayout dst_layout, Span<VkImageBlit> regions,
885 noexcept { 885 VkFilter filter) const noexcept {
886 dld->vkCmdBlitImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(), 886 dld->vkCmdBlitImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(),
887 regions.data(), filter); 887 regions.data(), filter);
888 } 888 }
@@ -907,8 +907,8 @@ public:
907 regions.data()); 907 regions.data());
908 } 908 }
909 909
910 void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, Span<VkBufferCopy> regions) const 910 void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer,
911 noexcept { 911 Span<VkBufferCopy> regions) const noexcept {
912 dld->vkCmdCopyBuffer(handle, src_buffer, dst_buffer, regions.size(), regions.data()); 912 dld->vkCmdCopyBuffer(handle, src_buffer, dst_buffer, regions.size(), regions.data());
913 } 913 }
914 914
@@ -924,8 +924,8 @@ public:
924 regions.data()); 924 regions.data());
925 } 925 }
926 926
927 void FillBuffer(VkBuffer dst_buffer, VkDeviceSize dst_offset, VkDeviceSize size, u32 data) const 927 void FillBuffer(VkBuffer dst_buffer, VkDeviceSize dst_offset, VkDeviceSize size,
928 noexcept { 928 u32 data) const noexcept {
929 dld->vkCmdFillBuffer(handle, dst_buffer, dst_offset, size, data); 929 dld->vkCmdFillBuffer(handle, dst_buffer, dst_offset, size, data);
930 } 930 }
931 931
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/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp
index 8d86020f6..336397cdb 100644
--- a/src/video_core/shader/control_flow.cpp
+++ b/src/video_core/shader/control_flow.cpp
@@ -187,24 +187,26 @@ std::optional<std::pair<BufferInfo, u64>> TrackLDC(const CFGRebuildState& state,
187 187
188std::optional<u64> TrackSHLRegister(const CFGRebuildState& state, u32& pos, 188std::optional<u64> TrackSHLRegister(const CFGRebuildState& state, u32& pos,
189 u64 ldc_tracked_register) { 189 u64 ldc_tracked_register) {
190 return TrackInstruction<u64>(state, pos, 190 return TrackInstruction<u64>(
191 [ldc_tracked_register](auto instr, const auto& opcode) { 191 state, pos,
192 return opcode.GetId() == OpCode::Id::SHL_IMM && 192 [ldc_tracked_register](auto instr, const auto& opcode) {
193 instr.gpr0.Value() == ldc_tracked_register; 193 return opcode.GetId() == OpCode::Id::SHL_IMM &&
194 }, 194 instr.gpr0.Value() == ldc_tracked_register;
195 [](auto instr, const auto&) { return instr.gpr8.Value(); }); 195 },
196 [](auto instr, const auto&) { return instr.gpr8.Value(); });
196} 197}
197 198
198std::optional<u32> TrackIMNMXValue(const CFGRebuildState& state, u32& pos, 199std::optional<u32> TrackIMNMXValue(const CFGRebuildState& state, u32& pos,
199 u64 shl_tracked_register) { 200 u64 shl_tracked_register) {
200 return TrackInstruction<u32>(state, pos, 201 return TrackInstruction<u32>(
201 [shl_tracked_register](auto instr, const auto& opcode) { 202 state, pos,
202 return opcode.GetId() == OpCode::Id::IMNMX_IMM && 203 [shl_tracked_register](auto instr, const auto& opcode) {
203 instr.gpr0.Value() == shl_tracked_register; 204 return opcode.GetId() == OpCode::Id::IMNMX_IMM &&
204 }, 205 instr.gpr0.Value() == shl_tracked_register;
205 [](auto instr, const auto&) { 206 },
206 return static_cast<u32>(instr.alu.GetSignedImm20_20() + 1); 207 [](auto instr, const auto&) {
207 }); 208 return static_cast<u32>(instr.alu.GetSignedImm20_20() + 1);
209 });
208} 210}
209 211
210std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& state, u32 pos) { 212std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& state, u32 pos) {
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index 63adbc4a3..e4739394d 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -471,9 +471,9 @@ std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackGlobalMemory(NodeBlock&
471 471
472 const auto [base_address, index, offset] = 472 const auto [base_address, index, offset] =
473 TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size())); 473 TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()));
474 ASSERT_OR_EXECUTE_MSG(base_address != nullptr, 474 ASSERT_OR_EXECUTE_MSG(
475 { return std::make_tuple(nullptr, nullptr, GlobalMemoryBase{}); }, 475 base_address != nullptr, { return std::make_tuple(nullptr, nullptr, GlobalMemoryBase{}); },
476 "Global memory tracking failed"); 476 "Global memory tracking failed");
477 477
478 bb.push_back(Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", index, offset))); 478 bb.push_back(Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", index, offset)));
479 479
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
index 9a98f0e98..e614a92df 100644
--- a/src/video_core/texture_cache/surface_params.cpp
+++ b/src/video_core/texture_cache/surface_params.cpp
@@ -96,7 +96,6 @@ SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_ta
96 } 96 }
97 params.type = GetFormatType(params.pixel_format); 97 params.type = GetFormatType(params.pixel_format);
98 } 98 }
99 params.type = GetFormatType(params.pixel_format);
100 // TODO: on 1DBuffer we should use the tic info. 99 // TODO: on 1DBuffer we should use the tic info.
101 if (tic.IsBuffer()) { 100 if (tic.IsBuffer()) {
102 params.target = SurfaceTarget::TextureBuffer; 101 params.target = SurfaceTarget::TextureBuffer;
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/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/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index cb71b8d11..a372190cc 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 =
@@ -677,11 +674,11 @@ void Config::ReadScreenshotValues() {
677 674
678 UISettings::values.enable_screenshot_save_as = 675 UISettings::values.enable_screenshot_save_as =
679 ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool(); 676 ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool();
680 FileUtil::GetUserPath( 677 FS::GetUserPath(
681 FileUtil::UserPath::ScreenshotsDir, 678 FS::UserPath::ScreenshotsDir,
682 qt_config 679 qt_config
683 ->value(QStringLiteral("screenshot_path"), QString::fromStdString(FileUtil::GetUserPath( 680 ->value(QStringLiteral("screenshot_path"),
684 FileUtil::UserPath::ScreenshotsDir))) 681 QString::fromStdString(FS::GetUserPath(FS::UserPath::ScreenshotsDir)))
685 .toString() 682 .toString()
686 .toStdString()); 683 .toStdString());
687 684
@@ -1016,20 +1013,20 @@ void Config::SaveDataStorageValues() {
1016 1013
1017 WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true); 1014 WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true);
1018 WriteSetting(QStringLiteral("nand_directory"), 1015 WriteSetting(QStringLiteral("nand_directory"),
1019 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)), 1016 QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir)),
1020 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); 1017 QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir)));
1021 WriteSetting(QStringLiteral("sdmc_directory"), 1018 WriteSetting(QStringLiteral("sdmc_directory"),
1022 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)), 1019 QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir)),
1023 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 1020 QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir)));
1024 WriteSetting(QStringLiteral("load_directory"), 1021 WriteSetting(QStringLiteral("load_directory"),
1025 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)), 1022 QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir)),
1026 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))); 1023 QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir)));
1027 WriteSetting(QStringLiteral("dump_directory"), 1024 WriteSetting(QStringLiteral("dump_directory"),
1028 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)), 1025 QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)),
1029 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))); 1026 QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)));
1030 WriteSetting(QStringLiteral("cache_directory"), 1027 WriteSetting(QStringLiteral("cache_directory"),
1031 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)), 1028 QString::fromStdString(FS::GetUserPath(FS::UserPath::CacheDir)),
1032 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))); 1029 QString::fromStdString(FS::GetUserPath(FS::UserPath::CacheDir)));
1033 WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false); 1030 WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false);
1034 WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, 1031 WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game,
1035 false); 1032 false);
@@ -1180,7 +1177,7 @@ void Config::SaveScreenshotValues() {
1180 WriteSetting(QStringLiteral("enable_screenshot_save_as"), 1177 WriteSetting(QStringLiteral("enable_screenshot_save_as"),
1181 UISettings::values.enable_screenshot_save_as); 1178 UISettings::values.enable_screenshot_save_as);
1182 WriteSetting(QStringLiteral("screenshot_path"), 1179 WriteSetting(QStringLiteral("screenshot_path"),
1183 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))); 1180 QString::fromStdString(FS::GetUserPath(FS::UserPath::ScreenshotsDir)));
1184 1181
1185 qt_config->endGroup(); 1182 qt_config->endGroup();
1186} 1183}
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_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_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index b1850bc95..597defe8c 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -281,24 +281,25 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
281 281
282 button->setContextMenuPolicy(Qt::CustomContextMenu); 282 button->setContextMenuPolicy(Qt::CustomContextMenu);
283 connect(button, &QPushButton::clicked, [=, this] { 283 connect(button, &QPushButton::clicked, [=, this] {
284 HandleClick(button_map[button_id], 284 HandleClick(
285 [=, this](Common::ParamPackage params) { 285 button_map[button_id],
286 // Workaround for ZL & ZR for analog triggers like on XBOX controllors. 286 [=, this](Common::ParamPackage params) {
287 // Analog triggers (from controllers like the XBOX controller) would not 287 // Workaround for ZL & ZR for analog triggers like on XBOX controllors.
288 // work due to a different range of their signals (from 0 to 255 on 288 // Analog triggers (from controllers like the XBOX controller) would not
289 // analog triggers instead of -32768 to 32768 on analog joysticks). The 289 // work due to a different range of their signals (from 0 to 255 on
290 // SDL driver misinterprets analog triggers as analog joysticks. 290 // analog triggers instead of -32768 to 32768 on analog joysticks). The
291 // TODO: reinterpret the signal range for analog triggers to map the 291 // SDL driver misinterprets analog triggers as analog joysticks.
292 // values correctly. This is required for the correct emulation of the 292 // TODO: reinterpret the signal range for analog triggers to map the
293 // analog triggers of the GameCube controller. 293 // values correctly. This is required for the correct emulation of the
294 if (button_id == Settings::NativeButton::ZL || 294 // analog triggers of the GameCube controller.
295 button_id == Settings::NativeButton::ZR) { 295 if (button_id == Settings::NativeButton::ZL ||
296 params.Set("direction", "+"); 296 button_id == Settings::NativeButton::ZR) {
297 params.Set("threshold", "0.5"); 297 params.Set("direction", "+");
298 } 298 params.Set("threshold", "0.5");
299 buttons_param[button_id] = std::move(params); 299 }
300 }, 300 buttons_param[button_id] = std::move(params);
301 InputCommon::Polling::DeviceType::Button); 301 },
302 InputCommon::Polling::DeviceType::Button);
302 }); 303 });
303 connect(button, &QPushButton::customContextMenuRequested, 304 connect(button, &QPushButton::customContextMenuRequested,
304 [=, this](const QPoint& menu_location) { 305 [=, this](const QPoint& menu_location) {
@@ -325,12 +326,13 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
325 326
326 analog_button->setContextMenuPolicy(Qt::CustomContextMenu); 327 analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
327 connect(analog_button, &QPushButton::clicked, [=, this] { 328 connect(analog_button, &QPushButton::clicked, [=, this] {
328 HandleClick(analog_map_buttons[analog_id][sub_button_id], 329 HandleClick(
329 [=, this](const Common::ParamPackage& params) { 330 analog_map_buttons[analog_id][sub_button_id],
330 SetAnalogButton(params, analogs_param[analog_id], 331 [=, this](const Common::ParamPackage& params) {
331 analog_sub_buttons[sub_button_id]); 332 SetAnalogButton(params, analogs_param[analog_id],
332 }, 333 analog_sub_buttons[sub_button_id]);
333 InputCommon::Polling::DeviceType::Button); 334 },
335 InputCommon::Polling::DeviceType::Button);
334 }); 336 });
335 connect(analog_button, &QPushButton::customContextMenuRequested, 337 connect(analog_button, &QPushButton::customContextMenuRequested,
336 [=, this](const QPoint& menu_location) { 338 [=, this](const QPoint& menu_location) {
@@ -357,11 +359,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
357 tr("After pressing OK, first move your joystick horizontally, " 359 tr("After pressing OK, first move your joystick horizontally, "
358 "and then vertically."), 360 "and then vertically."),
359 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { 361 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
360 HandleClick(analog_map_stick[analog_id], 362 HandleClick(
361 [=, this](const Common::ParamPackage& params) { 363 analog_map_stick[analog_id],
362 analogs_param[analog_id] = params; 364 [=, this](const Common::ParamPackage& params) {
363 }, 365 analogs_param[analog_id] = params;
364 InputCommon::Polling::DeviceType::Analog); 366 },
367 InputCommon::Polling::DeviceType::Analog);
365 } 368 }
366 }); 369 });
367 370
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp
index ea2549363..5bcf5ffa8 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.cpp
+++ b/src/yuzu/configuration/configure_mouse_advanced.cpp
@@ -84,11 +84,12 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
84 84
85 button->setContextMenuPolicy(Qt::CustomContextMenu); 85 button->setContextMenuPolicy(Qt::CustomContextMenu);
86 connect(button, &QPushButton::clicked, [=, this] { 86 connect(button, &QPushButton::clicked, [=, this] {
87 HandleClick(button_map[button_id], 87 HandleClick(
88 [=, this](const Common::ParamPackage& params) { 88 button_map[button_id],
89 buttons_param[button_id] = params; 89 [=, this](const Common::ParamPackage& params) {
90 }, 90 buttons_param[button_id] = params;
91 InputCommon::Polling::DeviceType::Button); 91 },
92 InputCommon::Polling::DeviceType::Button);
92 }); 93 });
93 connect(button, &QPushButton::customContextMenuRequested, 94 connect(button, &QPushButton::customContextMenuRequested,
94 [=, this](const QPoint& menu_location) { 95 [=, this](const QPoint& menu_location) {
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 9be6a5a2f..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}
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 239016b94..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,11 +345,12 @@ 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() {
352 stop_processing = false; 352 stop_processing = false;
353 provider->ClearAllEntries();
353 354
354 for (UISettings::GameDir& game_dir : game_dirs) { 355 for (UISettings::GameDir& game_dir : game_dirs) {
355 if (game_dir.path == QStringLiteral("SDMC")) { 356 if (game_dir.path == QStringLiteral("SDMC")) {
@@ -368,13 +369,12 @@ void GameListWorker::run() {
368 watch_list.append(game_dir.path); 369 watch_list.append(game_dir.path);
369 auto* const game_list_dir = new GameListDir(game_dir); 370 auto* const game_list_dir = new GameListDir(game_dir);
370 emit DirEntryReady(game_list_dir); 371 emit DirEntryReady(game_list_dir);
371 provider->ClearAllEntries();
372 ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), 372 ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(),
373 game_dir.deep_scan ? 256 : 0, game_list_dir); 373 game_dir.deep_scan ? 256 : 0, game_list_dir);
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 e2b6462bb..c6b7e2c00 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,
@@ -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);
@@ -1268,7 +1271,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
1268 switch (target) { 1271 switch (target) {
1269 case GameListOpenTarget::SaveData: { 1272 case GameListOpenTarget::SaveData: {
1270 open_target = tr("Save Data"); 1273 open_target = tr("Save Data");
1271 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); 1274 const std::string nand_dir = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
1272 1275
1273 if (has_user_save) { 1276 if (has_user_save) {
1274 // User save data 1277 // User save data
@@ -1303,16 +1306,16 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
1303 FileSys::SaveDataType::SaveData, program_id, {}, 0); 1306 FileSys::SaveDataType::SaveData, program_id, {}, 0);
1304 } 1307 }
1305 1308
1306 if (!FileUtil::Exists(path)) { 1309 if (!Common::FS::Exists(path)) {
1307 FileUtil::CreateFullPath(path); 1310 Common::FS::CreateFullPath(path);
1308 FileUtil::CreateDir(path); 1311 Common::FS::CreateDir(path);
1309 } 1312 }
1310 1313
1311 break; 1314 break;
1312 } 1315 }
1313 case GameListOpenTarget::ModData: { 1316 case GameListOpenTarget::ModData: {
1314 open_target = tr("Mod Data"); 1317 open_target = tr("Mod Data");
1315 const auto load_dir = FileUtil::GetUserPath(FileUtil::UserPath::LoadDir); 1318 const auto load_dir = Common::FS::GetUserPath(Common::FS::UserPath::LoadDir);
1316 path = fmt::format("{}{:016X}", load_dir, program_id); 1319 path = fmt::format("{}{:016X}", load_dir, program_id);
1317 break; 1320 break;
1318 } 1321 }
@@ -1334,7 +1337,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
1334 1337
1335void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { 1338void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
1336 const QString shader_dir = 1339 const QString shader_dir =
1337 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); 1340 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir));
1338 const QString transferable_shader_cache_folder_path = 1341 const QString transferable_shader_cache_folder_path =
1339 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); 1342 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
1340 const QString transferable_shader_cache_file_path = 1343 const QString transferable_shader_cache_file_path =
@@ -1437,8 +1440,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
1437 RemoveAddOnContent(program_id, entry_type); 1440 RemoveAddOnContent(program_id, entry_type);
1438 break; 1441 break;
1439 } 1442 }
1440 FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + 1443 Common::FS::DeleteDirRecursively(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
1441 "game_list"); 1444 DIR_SEP + "game_list");
1442 game_list->PopulateAsync(UISettings::values.game_dirs); 1445 game_list->PopulateAsync(UISettings::values.game_dirs);
1443} 1446}
1444 1447
@@ -1528,7 +1531,7 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ
1528 1531
1529void GMainWindow::RemoveTransferableShaderCache(u64 program_id) { 1532void GMainWindow::RemoveTransferableShaderCache(u64 program_id) {
1530 const QString shader_dir = 1533 const QString shader_dir =
1531 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); 1534 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir));
1532 const QString transferable_shader_cache_folder_path = 1535 const QString transferable_shader_cache_folder_path =
1533 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); 1536 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
1534 const QString transferable_shader_cache_file_path = 1537 const QString transferable_shader_cache_file_path =
@@ -1552,7 +1555,7 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id) {
1552 1555
1553void GMainWindow::RemoveCustomConfiguration(u64 program_id) { 1556void GMainWindow::RemoveCustomConfiguration(u64 program_id) {
1554 const QString config_dir = 1557 const QString config_dir =
1555 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir)); 1558 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir));
1556 const QString custom_config_file_path = 1559 const QString custom_config_file_path =
1557 config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id)); 1560 config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id));
1558 1561
@@ -1599,7 +1602,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1599 } 1602 }
1600 1603
1601 const auto path = fmt::format( 1604 const auto path = fmt::format(
1602 "{}{:016X}/romfs", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), *romfs_title_id); 1605 "{}{:016X}/romfs", Common::FS::GetUserPath(Common::FS::UserPath::DumpDir), *romfs_title_id);
1603 1606
1604 FileSys::VirtualFile romfs; 1607 FileSys::VirtualFile romfs;
1605 1608
@@ -1679,13 +1682,13 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
1679void GMainWindow::OnGameListOpenDirectory(const QString& directory) { 1682void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
1680 QString path; 1683 QString path;
1681 if (directory == QStringLiteral("SDMC")) { 1684 if (directory == QStringLiteral("SDMC")) {
1682 path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + 1685 path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
1683 "Nintendo/Contents/registered"); 1686 "Nintendo/Contents/registered");
1684 } else if (directory == QStringLiteral("UserNAND")) { 1687 } else if (directory == QStringLiteral("UserNAND")) {
1685 path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1688 path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1686 "user/Contents/registered"); 1689 "user/Contents/registered");
1687 } else if (directory == QStringLiteral("SysNAND")) { 1690 } else if (directory == QStringLiteral("SysNAND")) {
1688 path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1691 path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1689 "system/Contents/registered"); 1692 "system/Contents/registered");
1690 } else { 1693 } else {
1691 path = directory; 1694 path = directory;
@@ -1699,8 +1702,10 @@ void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
1699 1702
1700void GMainWindow::OnGameListAddDirectory() { 1703void GMainWindow::OnGameListAddDirectory() {
1701 const QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); 1704 const QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
1702 if (dir_path.isEmpty()) 1705 if (dir_path.isEmpty()) {
1703 return; 1706 return;
1707 }
1708
1704 UISettings::GameDir game_dir{dir_path, false, true}; 1709 UISettings::GameDir game_dir{dir_path, false, true};
1705 if (!UISettings::values.game_dirs.contains(game_dir)) { 1710 if (!UISettings::values.game_dirs.contains(game_dir)) {
1706 UISettings::values.game_dirs.append(game_dir); 1711 UISettings::values.game_dirs.append(game_dir);
@@ -1727,26 +1732,7 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
1727 return; 1732 return;
1728 } 1733 }
1729 1734
1730 ConfigurePerGame dialog(this, title_id); 1735 OpenPerGameConfiguration(title_id, file);
1731 dialog.LoadFromFile(v_file);
1732 auto result = dialog.exec();
1733 if (result == QDialog::Accepted) {
1734 dialog.ApplyConfiguration();
1735
1736 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
1737 if (reload) {
1738 game_list->PopulateAsync(UISettings::values.game_dirs);
1739 }
1740
1741 // Do not cause the global config to write local settings into the config file
1742 Settings::RestoreGlobalState();
1743
1744 if (!Core::System::GetInstance().IsPoweredOn()) {
1745 config->Save();
1746 }
1747 } else {
1748 Settings::RestoreGlobalState();
1749 }
1750} 1736}
1751 1737
1752void GMainWindow::OnMenuLoadFile() { 1738void GMainWindow::OnMenuLoadFile() {
@@ -1891,8 +1877,8 @@ void GMainWindow::OnMenuInstallToNAND() {
1891 : tr("%n file(s) failed to install\n", "", failed_files.size())); 1877 : tr("%n file(s) failed to install\n", "", failed_files.size()));
1892 1878
1893 QMessageBox::information(this, tr("Install Results"), install_results); 1879 QMessageBox::information(this, tr("Install Results"), install_results);
1894 FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + 1880 Common::FS::DeleteDirRecursively(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
1895 "game_list"); 1881 DIR_SEP + "game_list");
1896 game_list->PopulateAsync(UISettings::values.game_dirs); 1882 game_list->PopulateAsync(UISettings::values.game_dirs);
1897 ui.action_Install_File_NAND->setEnabled(true); 1883 ui.action_Install_File_NAND->setEnabled(true);
1898} 1884}
@@ -2075,6 +2061,7 @@ void GMainWindow::OnStartGame() {
2075 ui.action_Pause->setEnabled(true); 2061 ui.action_Pause->setEnabled(true);
2076 ui.action_Stop->setEnabled(true); 2062 ui.action_Stop->setEnabled(true);
2077 ui.action_Restart->setEnabled(true); 2063 ui.action_Restart->setEnabled(true);
2064 ui.action_Configure_Current_Game->setEnabled(true);
2078 ui.action_Report_Compatibility->setEnabled(true); 2065 ui.action_Report_Compatibility->setEnabled(true);
2079 2066
2080 discord_rpc->Update(); 2067 discord_rpc->Update();
@@ -2264,6 +2251,36 @@ void GMainWindow::OnConfigure() {
2264 UpdateStatusButtons(); 2251 UpdateStatusButtons();
2265} 2252}
2266 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
2267void GMainWindow::OnLoadAmiibo() { 2284void GMainWindow::OnLoadAmiibo() {
2268 const QString extensions{QStringLiteral("*.bin")}; 2285 const QString extensions{QStringLiteral("*.bin")};
2269 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);
@@ -2311,7 +2328,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
2311 2328
2312void GMainWindow::OnOpenYuzuFolder() { 2329void GMainWindow::OnOpenYuzuFolder() {
2313 QDesktopServices::openUrl(QUrl::fromLocalFile( 2330 QDesktopServices::openUrl(QUrl::fromLocalFile(
2314 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir)))); 2331 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::UserDir))));
2315} 2332}
2316 2333
2317void GMainWindow::OnAbout() { 2334void GMainWindow::OnAbout() {
@@ -2333,7 +2350,7 @@ void GMainWindow::OnCaptureScreenshot() {
2333 2350
2334 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 2351 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
2335 const auto screenshot_path = 2352 const auto screenshot_path =
2336 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir)); 2353 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir));
2337 const auto date = 2354 const auto date =
2338 QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz")); 2355 QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz"));
2339 QString filename = QStringLiteral("%1%2_%3.png") 2356 QString filename = QStringLiteral("%1%2_%3.png")
@@ -2536,18 +2553,18 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
2536 if (res == QMessageBox::Cancel) 2553 if (res == QMessageBox::Cancel)
2537 return; 2554 return;
2538 2555
2539 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) + 2556 Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) +
2540 "prod.keys_autogenerated"); 2557 "prod.keys_autogenerated");
2541 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) + 2558 Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) +
2542 "console.keys_autogenerated"); 2559 "console.keys_autogenerated");
2543 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) + 2560 Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) +
2544 "title.keys_autogenerated"); 2561 "title.keys_autogenerated");
2545 } 2562 }
2546 2563
2547 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); 2564 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
2548 if (keys.BaseDeriveNecessary()) { 2565 if (keys.BaseDeriveNecessary()) {
2549 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory( 2566 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory(
2550 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)}; 2567 Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), FileSys::Mode::Read)};
2551 2568
2552 const auto function = [this, &keys, &pdm] { 2569 const auto function = [this, &keys, &pdm] {
2553 keys.PopulateFromPartitionData(pdm); 2570 keys.PopulateFromPartitionData(pdm);
@@ -2879,7 +2896,7 @@ int main(int argc, char* argv[]) {
2879 // 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 "/".
2880 // 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
2881 // 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
2882 const std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + ".."; 2899 const std::string bin_path = Common::FS::GetBundleDirectory() + DIR_SEP + "..";
2883 chdir(bin_path.c_str()); 2900 chdir(bin_path.c_str());
2884#endif 2901#endif
2885 2902
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 42a8e583c..01f9131e5 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -217,6 +217,7 @@ private slots:
217 void OnMenuInstallToNAND(); 217 void OnMenuInstallToNAND();
218 void OnMenuRecentFile(); 218 void OnMenuRecentFile();
219 void OnConfigure(); 219 void OnConfigure();
220 void OnConfigurePerGame();
220 void OnLoadAmiibo(); 221 void OnLoadAmiibo();
221 void OnOpenYuzuFolder(); 222 void OnOpenYuzuFolder();
222 void OnAbout(); 223 void OnAbout();
@@ -250,6 +251,7 @@ private:
250 void ShowMouseCursor(); 251 void ShowMouseCursor();
251 void OpenURL(const QUrl& url); 252 void OpenURL(const QUrl& url);
252 void LoadTranslation(); 253 void LoadTranslation();
254 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
253 255
254 Ui::MainWindow ui; 256 Ui::MainWindow ui;
255 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..3f2f61ca0 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>());
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..7c5ac5681 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>());