summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/cubeb_sink.cpp2
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/algorithm.h3
-rw-r--r--src/common/alignment.h14
-rw-r--r--src/common/assert.h7
-rw-r--r--src/common/atomic_ops.h10
-rw-r--r--src/common/bit_field.h19
-rw-r--r--src/common/bit_util.h34
-rw-r--r--src/common/cityhash.h23
-rw-r--r--src/common/color.h38
-rw-r--r--src/common/common_funcs.h14
-rw-r--r--src/common/concepts.h14
-rw-r--r--src/common/detached_tasks.cpp3
-rw-r--r--src/common/dynamic_library.cpp2
-rw-r--r--src/common/dynamic_library.h13
-rw-r--r--src/common/fiber.h2
-rw-r--r--src/common/file_util.cpp68
-rw-r--r--src/common/file_util.h85
-rw-r--r--src/common/hash.h25
-rw-r--r--src/common/hex_util.cpp34
-rw-r--r--src/common/hex_util.h33
-rw-r--r--src/common/logging/backend.h2
-rw-r--r--src/common/lz4_compression.cpp16
-rw-r--r--src/common/lz4_compression.h24
-rw-r--r--src/common/math_util.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.h12
-rw-r--r--src/common/quaternion.h15
-rw-r--r--src/common/ring_buffer.h4
-rw-r--r--src/common/spin_lock.h2
-rw-r--r--src/common/string_util.h34
-rw-r--r--src/common/telemetry.cpp4
-rw-r--r--src/common/telemetry.h16
-rw-r--r--src/common/thread_queue_list.h10
-rw-r--r--src/common/threadsafe_queue.h12
-rw-r--r--src/common/time_zone.h4
-rw-r--r--src/common/timer.h16
-rw-r--r--src/common/uint128.h6
-rw-r--r--src/common/uuid.h14
-rw-r--r--src/common/vector_math.h204
-rw-r--r--src/common/virtual_buffer.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/arm/cpu_interrupt_handler.cpp6
-rw-r--r--src/core/arm/cpu_interrupt_handler.h3
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp13
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp13
-rw-r--r--src/core/core.cpp14
-rw-r--r--src/core/core_timing.cpp12
-rw-r--r--src/core/core_timing_util.cpp1
-rw-r--r--src/core/core_timing_util.h1
-rw-r--r--src/core/cpu_manager.cpp29
-rw-r--r--src/core/crypto/aes_util.cpp2
-rw-r--r--src/core/crypto/key_manager.cpp449
-rw-r--r--src/core/crypto/key_manager.h8
-rw-r--r--src/core/crypto/partition_data_manager.cpp210
-rw-r--r--src/core/file_sys/bis_factory.cpp2
-rw-r--r--src/core/file_sys/registered_cache.cpp35
-rw-r--r--src/core/file_sys/registered_cache.h6
-rw-r--r--src/core/file_sys/system_archive/mii_model.cpp18
-rw-r--r--src/core/file_sys/system_archive/ng_word.cpp42
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.cpp9
-rw-r--r--src/core/file_sys/vfs.cpp56
-rw-r--r--src/core/file_sys/vfs_libzip.cpp2
-rw-r--r--src/core/file_sys/vfs_real.cpp241
-rw-r--r--src/core/file_sys/vfs_real.h8
-rw-r--r--src/core/file_sys/vfs_vector.h13
-rw-r--r--src/core/file_sys/xts_archive.cpp2
-rw-r--r--src/core/frontend/emu_window.h4
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp2
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp46
-rw-r--r--src/core/hle/kernel/memory/page_table.cpp23
-rw-r--r--src/core/hle/kernel/memory/system_control.cpp21
-rw-r--r--src/core/hle/kernel/memory/system_control.h5
-rw-r--r--src/core/hle/kernel/scheduler.cpp31
-rw-r--r--src/core/hle/kernel/scheduler.h2
-rw-r--r--src/core/hle/result.h12
-rw-r--r--src/core/hle/service/acc/acc.cpp8
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp22
-rw-r--r--src/core/hle/service/am/am.cpp6
-rw-r--r--src/core/hle/service/am/am.h7
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp3
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp19
-rw-r--r--src/core/hle/service/audio/audout_u.cpp2
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp26
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp80
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp239
-rw-r--r--src/core/hle/service/hid/controllers/npad.h11
-rw-r--r--src/core/hle/service/hid/hid.cpp14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h3
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h10
-rw-r--r--src/core/hle/service/sm/sm.h2
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp4
-rw-r--r--src/core/loader/loader.cpp4
-rw-r--r--src/core/memory/cheat_engine.cpp26
-rw-r--r--src/core/perf_stats.cpp4
-rw-r--r--src/core/reporter.cpp9
-rw-r--r--src/core/settings.cpp54
-rw-r--r--src/core/settings.h345
-rw-r--r--src/core/telemetry_session.cpp14
-rw-r--r--src/core/telemetry_session.h5
-rw-r--r--src/core/tools/freezer.cpp31
-rw-r--r--src/core/tools/freezer.h7
-rw-r--r--src/input_common/CMakeLists.txt2
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp18
-rw-r--r--src/input_common/main.cpp201
-rw-r--r--src/input_common/main.h129
-rw-r--r--src/input_common/sdl/sdl.h19
-rw-r--r--src/input_common/sdl/sdl_impl.cpp438
-rw-r--r--src/input_common/sdl/sdl_impl.h8
-rw-r--r--src/input_common/settings.cpp33
-rw-r--r--src/input_common/settings.h335
-rw-r--r--src/input_common/udp/client.cpp6
-rw-r--r--src/input_common/udp/udp.cpp14
-rw-r--r--src/input_common/udp/udp.h7
-rw-r--r--src/video_core/CMakeLists.txt5
-rw-r--r--src/video_core/engines/fermi_2d.cpp10
-rw-r--r--src/video_core/engines/fermi_2d.h9
-rw-r--r--src/video_core/engines/kepler_compute.cpp17
-rw-r--r--src/video_core/engines/kepler_compute.h16
-rw-r--r--src/video_core/engines/maxwell_3d.cpp35
-rw-r--r--src/video_core/engines/maxwell_3d.h23
-rw-r--r--src/video_core/engines/maxwell_dma.cpp21
-rw-r--r--src/video_core/fence_manager.h5
-rw-r--r--src/video_core/gpu.cpp31
-rw-r--r--src/video_core/gpu.h13
-rw-r--r--src/video_core/gpu_asynch.cpp10
-rw-r--r--src/video_core/gpu_asynch.h4
-rw-r--r--src/video_core/gpu_synch.cpp8
-rw-r--r--src/video_core/gpu_synch.h6
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt43
-rw-r--r--src/video_core/host_shaders/StringShaderHeader.cmake11
-rw-r--r--src/video_core/host_shaders/opengl_present.frag10
-rw-r--r--src/video_core/host_shaders/opengl_present.vert24
-rw-r--r--src/video_core/host_shaders/source_shader.h.in9
-rw-r--r--src/video_core/macro/macro_hle.cpp6
-rw-r--r--src/video_core/macro/macro_interpreter.cpp1
-rw-r--r--src/video_core/memory_manager.cpp16
-rw-r--r--src/video_core/memory_manager.h41
-rw-r--r--src/video_core/renderer_base.cpp4
-rw-r--r--src/video_core/renderer_base.h17
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.h1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp9
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp57
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.h10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.h2
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp64
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h11
-rw-r--r--src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp14
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp10
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp10
-rw-r--r--src/video_core/renderer_vulkan/vk_device.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h28
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp37
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h31
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp30
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h16
-rw-r--r--src/video_core/renderer_vulkan/wrapper.cpp4
-rw-r--r--src/video_core/renderer_vulkan/wrapper.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.cpp9
-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/video_core/video_core.cpp32
-rw-r--r--src/web_service/CMakeLists.txt1
-rw-r--r--src/web_service/telemetry_json.cpp6
-rw-r--r--src/web_service/telemetry_json.h30
-rw-r--r--src/web_service/verify_login.cpp2
-rw-r--r--src/web_service/web_backend.cpp56
-rw-r--r--src/web_service/web_backend.h20
-rw-r--r--src/web_service/web_result.h (renamed from src/common/web_result.h)4
-rw-r--r--src/yuzu/CMakeLists.txt9
-rw-r--r--src/yuzu/applets/profile_select.cpp2
-rw-r--r--src/yuzu/bootmanager.cpp25
-rw-r--r--src/yuzu/bootmanager.h9
-rw-r--r--src/yuzu/compatdb.cpp3
-rw-r--r--src/yuzu/configuration/config.cpp179
-rw-r--r--src/yuzu/configuration/config.h4
-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.ui59
-rw-r--r--src/yuzu/configuration/configure_audio.cpp13
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp17
-rw-r--r--src/yuzu/configuration/configure_cpu.h1
-rw-r--r--src/yuzu/configuration/configure_cpu.ui52
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug_controller.cpp40
-rw-r--r--src/yuzu/configuration/configure_debug_controller.h38
-rw-r--r--src/yuzu/configuration/configure_debug_controller.ui97
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp13
-rw-r--r--src/yuzu/configuration/configure_dialog.h7
-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.cpp272
-rw-r--r--src/yuzu/configuration/configure_input.h31
-rw-r--r--src/yuzu/configuration/configure_input.ui1039
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp169
-rw-r--r--src/yuzu/configuration/configure_input_advanced.h45
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui2688
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp801
-rw-r--r--src/yuzu/configuration/configure_input_player.h114
-rw-r--r--src/yuzu/configuration/configure_input_player.ui4019
-rw-r--r--src/yuzu/configuration/configure_input_simple.cpp152
-rw-r--r--src/yuzu/configuration/configure_input_simple.h43
-rw-r--r--src/yuzu/configuration/configure_input_simple.ui97
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.cpp67
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.h15
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.ui252
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp4
-rw-r--r--src/yuzu/configuration/configure_profile_manager.cpp4
-rw-r--r--src/yuzu/configuration/configure_system.cpp18
-rw-r--r--src/yuzu/configuration/configure_ui.cpp12
-rw-r--r--src/yuzu/debugger/profiler.cpp3
-rw-r--r--src/yuzu/game_list.cpp6
-rw-r--r--src/yuzu/game_list.h3
-rw-r--r--src/yuzu/game_list_worker.cpp30
-rw-r--r--src/yuzu/main.cpp157
-rw-r--r--src/yuzu/main.h22
-rw-r--r--src/yuzu/main.ui9
-rw-r--r--src/yuzu/uisettings.cpp1
-rw-r--r--src/yuzu/uisettings.h12
-rw-r--r--src/yuzu_cmd/config.cpp40
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp18
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h10
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp5
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h7
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp5
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h7
-rw-r--r--src/yuzu_cmd/yuzu.cpp15
-rw-r--r--src/yuzu_tester/config.cpp22
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp5
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.h6
-rw-r--r--src/yuzu_tester/yuzu.cpp7
256 files changed, 11258 insertions, 5387 deletions
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:
const s16 surround_left{samples[i + 4]};
const s16 surround_right{samples[i + 5]};
// Not used in the ATSC reference implementation
- [[maybe_unused]] const s16 low_frequency_effects { samples[i + 3] };
+ [[maybe_unused]] const s16 low_frequency_effects{samples[i + 3]};
constexpr s32 clev{707}; // center mixing level coefficient
constexpr s32 slev{707}; // surround mixing level coefficient
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 78c3bfb3b..5d54516eb 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -172,7 +172,6 @@ add_library(common STATIC
virtual_buffer.h
wall_clock.cpp
wall_clock.h
- web_result.h
zstd_compression.cpp
zstd_compression.h
)
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 @@
namespace Common {
template <class ForwardIt, class T, class Compare = std::less<>>
-ForwardIt BinaryFind(ForwardIt first, ForwardIt last, const T& value, Compare comp = {}) {
+[[nodiscard]] ForwardIt BinaryFind(ForwardIt first, ForwardIt last, const T& value,
+ Compare comp = {}) {
// Note: BOTH type T and the type after ForwardIt is dereferenced
// must be implicitly convertible to BOTH Type1 and Type2, used in Compare.
// 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 @@
namespace Common {
template <typename T>
-constexpr T AlignUp(T value, std::size_t size) {
+[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
auto mod{static_cast<T>(value % size)};
value -= mod;
@@ -17,31 +17,31 @@ constexpr T AlignUp(T value, std::size_t size) {
}
template <typename T>
-constexpr T AlignDown(T value, std::size_t size) {
+[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return static_cast<T>(value - value % size);
}
template <typename T>
-constexpr T AlignBits(T value, std::size_t align) {
+[[nodiscard]] constexpr T AlignBits(T value, std::size_t align) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align);
}
template <typename T>
-constexpr bool Is4KBAligned(T value) {
+[[nodiscard]] constexpr bool Is4KBAligned(T value) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return (value & 0xFFF) == 0;
}
template <typename T>
-constexpr bool IsWordAligned(T value) {
+[[nodiscard]] constexpr bool IsWordAligned(T value) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return (value & 0b11) == 0;
}
template <typename T>
-constexpr bool IsAligned(T value, std::size_t alignment) {
+[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) {
using U = typename std::make_unsigned<T>::type;
const U mask = static_cast<U>(alignment - 1);
return (value & mask) == 0;
@@ -64,7 +64,7 @@ public:
template <typename T2>
constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {}
- T* allocate(size_type n) {
+ [[nodiscard]] T* allocate(size_type n) {
return static_cast<T*>(::operator new (n * sizeof(T), std::align_val_t{Align}));
}
diff --git a/src/common/assert.h b/src/common/assert.h
index 5b67c5c52..06d7b5612 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -17,11 +17,12 @@
// enough for our purposes.
template <typename Fn>
#if defined(_MSC_VER)
-__declspec(noinline, noreturn)
+[[msvc::noinline, noreturn]]
#elif defined(__GNUC__)
- __attribute__((noinline, noreturn, cold))
+[[gnu::cold, gnu::noinline, noreturn]]
#endif
- static void assert_noinline_call(const Fn& fn) {
+static void
+assert_noinline_call(const Fn& fn) {
fn();
Crash();
exit(1); // Keeps GCC's mouth shut about this actually returning
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h
index 8d6b73c00..b46888589 100644
--- a/src/common/atomic_ops.h
+++ b/src/common/atomic_ops.h
@@ -8,10 +8,10 @@
namespace Common {
-bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected);
-bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected);
-bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected);
-bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected);
-bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected);
+[[nodiscard]] bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected);
+[[nodiscard]] bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected);
+[[nodiscard]] bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected);
+[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected);
+[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected);
} // 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 @@
#include "common/common_funcs.h"
#include "common/swap.h"
-// Inlining
-#ifdef _WIN32
-#define FORCE_INLINE __forceinline
-#else
-#define FORCE_INLINE inline __attribute__((always_inline))
-#endif
-
/*
* Abstract bitfield class
*
@@ -142,8 +135,8 @@ public:
* containing several bitfields can be assembled by formatting each of their values and ORing
* the results together.
*/
- static constexpr FORCE_INLINE StorageType FormatValue(const T& value) {
- return ((StorageType)value << position) & mask;
+ [[nodiscard]] static constexpr StorageType FormatValue(const T& value) {
+ return (static_cast<StorageType>(value) << position) & mask;
}
/**
@@ -151,7 +144,7 @@ public:
* (such as Value() or operator T), but this can be used to extract a value from a bitfield
* union in a constexpr context.
*/
- static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
+ [[nodiscard]] static constexpr T ExtractValue(const StorageType& storage) {
if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
std::size_t shift = 8 * sizeof(T) - bits;
return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
@@ -175,7 +168,7 @@ public:
constexpr BitField(BitField&&) noexcept = default;
constexpr BitField& operator=(BitField&&) noexcept = default;
- constexpr operator T() const {
+ [[nodiscard]] constexpr operator T() const {
return Value();
}
@@ -183,11 +176,11 @@ public:
storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
}
- constexpr T Value() const {
+ [[nodiscard]] constexpr T Value() const {
return ExtractValue(storage);
}
- constexpr explicit operator bool() const {
+ [[nodiscard]] constexpr explicit operator bool() const {
return Value() != 0;
}
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 {
/// Gets the size of a specified type T in bits.
template <typename T>
-constexpr std::size_t BitSize() {
+[[nodiscard]] constexpr std::size_t BitSize() {
return sizeof(T) * CHAR_BIT;
}
#ifdef _MSC_VER
-inline u32 CountLeadingZeroes32(u32 value) {
+[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
unsigned long leading_zero = 0;
if (_BitScanReverse(&leading_zero, value) != 0) {
@@ -32,7 +32,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
return 32;
}
-inline u32 CountLeadingZeroes64(u64 value) {
+[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
unsigned long leading_zero = 0;
if (_BitScanReverse64(&leading_zero, value) != 0) {
@@ -42,7 +42,7 @@ inline u32 CountLeadingZeroes64(u64 value) {
return 64;
}
#else
-inline u32 CountLeadingZeroes32(u32 value) {
+[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
if (value == 0) {
return 32;
}
@@ -50,7 +50,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
return static_cast<u32>(__builtin_clz(value));
}
-inline u32 CountLeadingZeroes64(u64 value) {
+[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
@@ -60,7 +60,7 @@ inline u32 CountLeadingZeroes64(u64 value) {
#endif
#ifdef _MSC_VER
-inline u32 CountTrailingZeroes32(u32 value) {
+[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
unsigned long trailing_zero = 0;
if (_BitScanForward(&trailing_zero, value) != 0) {
@@ -70,7 +70,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
return 32;
}
-inline u32 CountTrailingZeroes64(u64 value) {
+[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
unsigned long trailing_zero = 0;
if (_BitScanForward64(&trailing_zero, value) != 0) {
@@ -80,7 +80,7 @@ inline u32 CountTrailingZeroes64(u64 value) {
return 64;
}
#else
-inline u32 CountTrailingZeroes32(u32 value) {
+[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
if (value == 0) {
return 32;
}
@@ -88,7 +88,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
return static_cast<u32>(__builtin_ctz(value));
}
-inline u32 CountTrailingZeroes64(u64 value) {
+[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
@@ -99,13 +99,13 @@ inline u32 CountTrailingZeroes64(u64 value) {
#ifdef _MSC_VER
-inline u32 MostSignificantBit32(const u32 value) {
+[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
unsigned long result;
_BitScanReverse(&result, value);
return static_cast<u32>(result);
}
-inline u32 MostSignificantBit64(const u64 value) {
+[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
unsigned long result;
_BitScanReverse64(&result, value);
return static_cast<u32>(result);
@@ -113,30 +113,30 @@ inline u32 MostSignificantBit64(const u64 value) {
#else
-inline u32 MostSignificantBit32(const u32 value) {
+[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
return 31U - static_cast<u32>(__builtin_clz(value));
}
-inline u32 MostSignificantBit64(const u64 value) {
+[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
return 63U - static_cast<u32>(__builtin_clzll(value));
}
#endif
-inline u32 Log2Floor32(const u32 value) {
+[[nodiscard]] inline u32 Log2Floor32(const u32 value) {
return MostSignificantBit32(value);
}
-inline u32 Log2Ceil32(const u32 value) {
+[[nodiscard]] inline u32 Log2Ceil32(const u32 value) {
const u32 log2_f = Log2Floor32(value);
return log2_f + ((value ^ (1U << log2_f)) != 0U);
}
-inline u32 Log2Floor64(const u64 value) {
+[[nodiscard]] inline u32 Log2Floor64(const u64 value) {
return MostSignificantBit64(value);
}
-inline u32 Log2Ceil64(const u64 value) {
+[[nodiscard]] inline u32 Log2Ceil64(const u64 value) {
const u64 log2_f = static_cast<u64>(Log2Floor64(value));
return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL));
}
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 @@
#pragma once
+#include <cstddef>
+#include <cstdint>
#include <utility>
-#include <stdint.h>
-#include <stdlib.h> // for std::size_t.
namespace Common {
-typedef std::pair<uint64_t, uint64_t> uint128;
+using uint128 = std::pair<uint64_t, uint64_t>;
-inline uint64_t Uint128Low64(const uint128& x) {
+[[nodiscard]] inline uint64_t Uint128Low64(const uint128& x) {
return x.first;
}
-inline uint64_t Uint128High64(const uint128& x) {
+[[nodiscard]] inline uint64_t Uint128High64(const uint128& x) {
return x.second;
}
// Hash function for a byte array.
-uint64_t CityHash64(const char* buf, std::size_t len);
+[[nodiscard]] uint64_t CityHash64(const char* buf, std::size_t len);
// Hash function for a byte array. For convenience, a 64-bit seed is also
// hashed into the result.
-uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed);
+[[nodiscard]] uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed);
// Hash function for a byte array. For convenience, two seeds are also
// hashed into the result.
-uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0, uint64_t seed1);
+[[nodiscard]] uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0,
+ uint64_t seed1);
// Hash function for a byte array.
-uint128 CityHash128(const char* s, std::size_t len);
+[[nodiscard]] uint128 CityHash128(const char* s, std::size_t len);
// Hash function for a byte array. For convenience, a 128-bit seed is also
// hashed into the result.
-uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed);
+[[nodiscard]] uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed);
// Hash 128 input bits down to 64 bits of output.
// This is intended to be a reasonably good hash function.
-inline uint64_t Hash128to64(const uint128& x) {
+[[nodiscard]] inline uint64_t Hash128to64(const uint128& x) {
// Murmur-inspired hashing.
const uint64_t kMul = 0x9ddfea08eb382d69ULL;
uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
diff --git a/src/common/color.h b/src/common/color.h
index 3a2222077..bbcac858e 100644
--- a/src/common/color.h
+++ b/src/common/color.h
@@ -10,45 +10,45 @@
#include "common/swap.h"
#include "common/vector_math.h"
-namespace Color {
+namespace Common::Color {
/// Convert a 1-bit color component to 8 bit
-constexpr u8 Convert1To8(u8 value) {
+[[nodiscard]] constexpr u8 Convert1To8(u8 value) {
return value * 255;
}
/// Convert a 4-bit color component to 8 bit
-constexpr u8 Convert4To8(u8 value) {
+[[nodiscard]] constexpr u8 Convert4To8(u8 value) {
return (value << 4) | value;
}
/// Convert a 5-bit color component to 8 bit
-constexpr u8 Convert5To8(u8 value) {
+[[nodiscard]] constexpr u8 Convert5To8(u8 value) {
return (value << 3) | (value >> 2);
}
/// Convert a 6-bit color component to 8 bit
-constexpr u8 Convert6To8(u8 value) {
+[[nodiscard]] constexpr u8 Convert6To8(u8 value) {
return (value << 2) | (value >> 4);
}
/// Convert a 8-bit color component to 1 bit
-constexpr u8 Convert8To1(u8 value) {
+[[nodiscard]] constexpr u8 Convert8To1(u8 value) {
return value >> 7;
}
/// Convert a 8-bit color component to 4 bit
-constexpr u8 Convert8To4(u8 value) {
+[[nodiscard]] constexpr u8 Convert8To4(u8 value) {
return value >> 4;
}
/// Convert a 8-bit color component to 5 bit
-constexpr u8 Convert8To5(u8 value) {
+[[nodiscard]] constexpr u8 Convert8To5(u8 value) {
return value >> 3;
}
/// Convert a 8-bit color component to 6 bit
-constexpr u8 Convert8To6(u8 value) {
+[[nodiscard]] constexpr u8 Convert8To6(u8 value) {
return value >> 2;
}
@@ -57,7 +57,7 @@ constexpr u8 Convert8To6(u8 value) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
-inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
+[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
return {bytes[3], bytes[2], bytes[1], bytes[0]};
}
@@ -66,7 +66,7 @@ inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
-inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
+[[nodiscard]] inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
return {bytes[2], bytes[1], bytes[0], 255};
}
@@ -75,7 +75,7 @@ inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
-inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
+[[nodiscard]] inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
return {bytes[1], bytes[0], 0, 255};
}
@@ -84,7 +84,7 @@ inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
-inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
+[[nodiscard]] inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
@@ -96,7 +96,7 @@ inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
-inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
+[[nodiscard]] inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
@@ -108,7 +108,7 @@ inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
-inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
+[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
@@ -120,7 +120,7 @@ inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
* @param bytes Pointer to encoded source value
* @return Depth value as an u32
*/
-inline u32 DecodeD16(const u8* bytes) {
+[[nodiscard]] inline u32 DecodeD16(const u8* bytes) {
u16_le data;
std::memcpy(&data, bytes, sizeof(data));
return data;
@@ -131,7 +131,7 @@ inline u32 DecodeD16(const u8* bytes) {
* @param bytes Pointer to encoded source value
* @return Depth value as an u32
*/
-inline u32 DecodeD24(const u8* bytes) {
+[[nodiscard]] inline u32 DecodeD24(const u8* bytes) {
return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
}
@@ -140,7 +140,7 @@ inline u32 DecodeD24(const u8* bytes) {
* @param bytes Pointer to encoded source values
* @return Resulting values stored as a Common::Vec2
*/
-inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) {
+[[nodiscard]] inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) {
return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
}
@@ -268,4 +268,4 @@ inline void EncodeX24S8(u8 stencil, u8* bytes) {
bytes[3] = stencil;
}
-} // namespace Color
+} // namespace Common::Color
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 88cf5250a..98421bced 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -53,14 +53,14 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
// Call directly after the command or use the error num.
// This function might change the error code.
// Defined in Misc.cpp.
-std::string GetLastErrorMsg();
+[[nodiscard]] std::string GetLastErrorMsg();
#define DECLARE_ENUM_FLAG_OPERATORS(type) \
- constexpr type operator|(type a, type b) noexcept { \
+ [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
using T = std::underlying_type_t<type>; \
return static_cast<type>(static_cast<T>(a) | static_cast<T>(b)); \
} \
- constexpr type operator&(type a, type b) noexcept { \
+ [[nodiscard]] constexpr type operator&(type a, type b) noexcept { \
using T = std::underlying_type_t<type>; \
return static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \
} \
@@ -74,22 +74,22 @@ std::string GetLastErrorMsg();
a = static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \
return a; \
} \
- constexpr type operator~(type key) noexcept { \
+ [[nodiscard]] constexpr type operator~(type key) noexcept { \
using T = std::underlying_type_t<type>; \
return static_cast<type>(~static_cast<T>(key)); \
} \
- constexpr bool True(type key) noexcept { \
+ [[nodiscard]] constexpr bool True(type key) noexcept { \
using T = std::underlying_type_t<type>; \
return static_cast<T>(key) != 0; \
} \
- constexpr bool False(type key) noexcept { \
+ [[nodiscard]] constexpr bool False(type key) noexcept { \
using T = std::underlying_type_t<type>; \
return static_cast<T>(key) == 0; \
}
namespace Common {
-constexpr u32 MakeMagic(char a, char b, char c, char d) {
+[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) {
return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24;
}
diff --git a/src/common/concepts.h b/src/common/concepts.h
index db5fb373d..5bef3ad67 100644
--- a/src/common/concepts.h
+++ b/src/common/concepts.h
@@ -4,10 +4,10 @@
#pragma once
-namespace Common {
-
#include <type_traits>
+namespace Common {
+
// Check if type is like an STL container
template <typename T>
concept IsSTLContainer = requires(T t) {
@@ -23,10 +23,12 @@ concept IsSTLContainer = requires(T t) {
t.size();
};
-// Check if type T is derived from T2
-template <typename T, typename T2>
-concept IsBaseOf = requires {
- std::is_base_of_v<T, T2>;
+// TODO: Replace with std::derived_from when the <concepts> header
+// is available on all supported platforms.
+template <typename Derived, typename Base>
+concept DerivedFrom = requires {
+ std::is_base_of_v<Base, Derived>;
+ std::is_convertible_v<const volatile Derived*, const volatile Base*>;
};
} // 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) {
std::unique_lock lock{instance->mutex};
--instance->count;
std::notify_all_at_thread_exit(instance->cv, std::move(lock));
- })
- .detach();
+ }).detach();
}
} // namespace Common
diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp
index 7ab54e9e4..7f0a10521 100644
--- a/src/common/dynamic_library.cpp
+++ b/src/common/dynamic_library.cpp
@@ -21,7 +21,7 @@ namespace Common {
DynamicLibrary::DynamicLibrary() = default;
DynamicLibrary::DynamicLibrary(const char* filename) {
- Open(filename);
+ void(Open(filename));
}
DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept
diff --git a/src/common/dynamic_library.h b/src/common/dynamic_library.h
index 2a06372fd..3512da940 100644
--- a/src/common/dynamic_library.h
+++ b/src/common/dynamic_library.h
@@ -33,7 +33,7 @@ public:
~DynamicLibrary();
/// Returns the specified library name with the platform-specific suffix added.
- static std::string GetUnprefixedFilename(const char* filename);
+ [[nodiscard]] static std::string GetUnprefixedFilename(const char* filename);
/// Returns the specified library name in platform-specific format.
/// Major/minor versions will not be included if set to -1.
@@ -41,28 +41,29 @@ public:
/// Windows: LIBNAME-MAJOR-MINOR.dll
/// Linux: libLIBNAME.so.MAJOR.MINOR
/// Mac: libLIBNAME.MAJOR.MINOR.dylib
- static std::string GetVersionedFilename(const char* libname, int major = -1, int minor = -1);
+ [[nodiscard]] static std::string GetVersionedFilename(const char* libname, int major = -1,
+ int minor = -1);
/// Returns true if a module is loaded, otherwise false.
- bool IsOpen() const {
+ [[nodiscard]] bool IsOpen() const {
return handle != nullptr;
}
/// Loads (or replaces) the handle with the specified library file name.
/// Returns true if the library was loaded and can be used.
- bool Open(const char* filename);
+ [[nodiscard]] bool Open(const char* filename);
/// Unloads the library, any function pointers from this library are no longer valid.
void Close();
/// Returns the address of the specified symbol (function or variable) as an untyped pointer.
/// If the specified symbol does not exist in this library, nullptr is returned.
- void* GetSymbolAddress(const char* name) const;
+ [[nodiscard]] void* GetSymbolAddress(const char* name) const;
/// Obtains the address of the specified symbol, automatically casting to the correct type.
/// Returns true if the symbol was found and assigned, otherwise false.
template <typename T>
- bool GetSymbol(const char* name, T* ptr) const {
+ [[nodiscard]] bool GetSymbol(const char* name, T* ptr) const {
*ptr = reinterpret_cast<T>(GetSymbolAddress(name));
return *ptr != nullptr;
}
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:
/// Yields control from Fiber 'from' to Fiber 'to'
/// Fiber 'from' must be the currently running fiber.
static void YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to);
- static std::shared_ptr<Fiber> ThreadToFiber();
+ [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter);
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 4ede9f72c..16c3713e0 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -74,7 +74,7 @@
// This namespace has various generic functions related to files and paths.
// The code still needs a ton of cleanup.
// REMEMBER: strdup considered harmful!
-namespace FileUtil {
+namespace Common::FS {
// Remove any ending forward slashes from directory paths
// Modifies argument.
@@ -196,7 +196,7 @@ bool CreateFullPath(const std::string& fullPath) {
int panicCounter = 100;
LOG_TRACE(Common_Filesystem, "path {}", fullPath);
- if (FileUtil::Exists(fullPath)) {
+ if (Exists(fullPath)) {
LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath);
return true;
}
@@ -212,7 +212,7 @@ bool CreateFullPath(const std::string& fullPath) {
// Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
std::string const subPath(fullPath.substr(0, position + 1));
- if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) {
+ if (!IsDirectory(subPath) && !CreateDir(subPath)) {
LOG_ERROR(Common, "CreateFullPath: directory creation failed");
return false;
}
@@ -231,7 +231,7 @@ bool DeleteDir(const std::string& filename) {
LOG_TRACE(Common_Filesystem, "directory {}", filename);
// check if a directory
- if (!FileUtil::IsDirectory(filename)) {
+ if (!IsDirectory(filename)) {
LOG_ERROR(Common_Filesystem, "Not a directory {}", filename);
return false;
}
@@ -371,7 +371,7 @@ u64 GetSize(FILE* f) {
bool CreateEmptyFile(const std::string& filename) {
LOG_TRACE(Common_Filesystem, "{}", filename);
- if (!FileUtil::IOFile(filename, "wb").IsOpen()) {
+ if (!IOFile(filename, "wb").IsOpen()) {
LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg());
return false;
}
@@ -488,29 +488,34 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
return false;
// Delete the outermost directory
- FileUtil::DeleteDir(directory);
+ DeleteDir(directory);
return true;
}
void CopyDir(const std::string& source_path, const std::string& dest_path) {
#ifndef _WIN32
- if (source_path == dest_path)
+ if (source_path == dest_path) {
return;
- if (!FileUtil::Exists(source_path))
+ }
+ if (!Exists(source_path)) {
return;
- if (!FileUtil::Exists(dest_path))
- FileUtil::CreateFullPath(dest_path);
+ }
+ if (!Exists(dest_path)) {
+ CreateFullPath(dest_path);
+ }
DIR* dirp = opendir(source_path.c_str());
- if (!dirp)
+ if (!dirp) {
return;
+ }
while (struct dirent* result = readdir(dirp)) {
const std::string virtualName(result->d_name);
// check for "." and ".."
if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
- ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0')))
+ ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0'))) {
continue;
+ }
std::string source, dest;
source = source_path + virtualName;
@@ -518,11 +523,13 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) {
if (IsDirectory(source)) {
source += '/';
dest += '/';
- if (!FileUtil::Exists(dest))
- FileUtil::CreateFullPath(dest);
+ if (!Exists(dest)) {
+ CreateFullPath(dest);
+ }
CopyDir(source, dest);
- } else if (!FileUtil::Exists(dest))
- FileUtil::Copy(source, dest);
+ } else if (!Exists(dest)) {
+ Copy(source, dest);
+ }
}
closedir(dirp);
#endif
@@ -538,7 +545,7 @@ std::optional<std::string> GetCurrentDir() {
if (!dir) {
#endif
LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg());
- return {};
+ return std::nullopt;
}
#ifdef _WIN32
std::string strDir = Common::UTF16ToUTF8(dir);
@@ -546,7 +553,7 @@ std::optional<std::string> GetCurrentDir() {
std::string strDir = dir;
#endif
free(dir);
- return strDir;
+ return std::move(strDir);
}
bool SetCurrentDir(const std::string& directory) {
@@ -668,7 +675,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
if (user_path.empty()) {
#ifdef _WIN32
user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
- if (!FileUtil::IsDirectory(user_path)) {
+ if (!IsDirectory(user_path)) {
user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
} else {
LOG_INFO(Common_Filesystem, "Using the local user directory");
@@ -677,7 +684,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
#else
- if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
+ if (Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
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) {
}
if (!new_path.empty()) {
- if (!FileUtil::IsDirectory(new_path)) {
+ if (!IsDirectory(new_path)) {
LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path);
return paths[path];
} else {
@@ -902,10 +909,10 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
return std::string(RemoveTrailingSlash(path));
}
-IOFile::IOFile() {}
+IOFile::IOFile() = default;
IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
- Open(filename, openmode, flags);
+ void(Open(filename, openmode, flags));
}
IOFile::~IOFile() {
@@ -946,17 +953,18 @@ bool IOFile::Open(const std::string& filename, const char openmode[], int flags)
}
bool IOFile::Close() {
- if (!IsOpen() || 0 != std::fclose(m_file))
+ if (!IsOpen() || 0 != std::fclose(m_file)) {
return false;
+ }
m_file = nullptr;
return true;
}
u64 IOFile::GetSize() const {
- if (IsOpen())
- return FileUtil::GetSize(m_file);
-
+ if (IsOpen()) {
+ return FS::GetSize(m_file);
+ }
return 0;
}
@@ -965,9 +973,9 @@ bool IOFile::Seek(s64 off, int origin) const {
}
u64 IOFile::Tell() const {
- if (IsOpen())
+ if (IsOpen()) {
return ftello(m_file);
-
+ }
return std::numeric_limits<u64>::max();
}
@@ -1016,4 +1024,4 @@ bool IOFile::Resize(u64 size) {
;
}
-} // namespace FileUtil
+} // 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 @@
#include "common/string_util.h"
#endif
-namespace FileUtil {
+namespace Common::FS {
// User paths for GetUserPath
enum class UserPath {
@@ -48,19 +48,19 @@ struct FSTEntry {
};
// Returns true if file filename exists
-bool Exists(const std::string& filename);
+[[nodiscard]] bool Exists(const std::string& filename);
// Returns true if filename is a directory
-bool IsDirectory(const std::string& filename);
+[[nodiscard]] bool IsDirectory(const std::string& filename);
// Returns the size of filename (64bit)
-u64 GetSize(const std::string& filename);
+[[nodiscard]] u64 GetSize(const std::string& filename);
// Overloaded GetSize, accepts file descriptor
-u64 GetSize(const int fd);
+[[nodiscard]] u64 GetSize(int fd);
// Overloaded GetSize, accepts FILE*
-u64 GetSize(FILE* f);
+[[nodiscard]] u64 GetSize(FILE* f);
// Returns true if successful, or path already exists.
bool CreateDir(const std::string& filename);
@@ -120,7 +120,7 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256);
// Returns the current directory
-std::optional<std::string> GetCurrentDir();
+[[nodiscard]] std::optional<std::string> GetCurrentDir();
// Create directory and copy contents (does not overwrite existing files)
void CopyDir(const std::string& source_path, const std::string& dest_path);
@@ -132,20 +132,20 @@ bool SetCurrentDir(const std::string& directory);
// directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
-std::string GetHactoolConfigurationPath();
+[[nodiscard]] std::string GetHactoolConfigurationPath();
-std::string GetNANDRegistrationDir(bool system = false);
+[[nodiscard]] std::string GetNANDRegistrationDir(bool system = false);
// Returns the path to where the sys file are
-std::string GetSysDirectory();
+[[nodiscard]] std::string GetSysDirectory();
#ifdef __APPLE__
-std::string GetBundleDirectory();
+[[nodiscard]] std::string GetBundleDirectory();
#endif
#ifdef _WIN32
-const std::string& GetExeDirectory();
-std::string AppDataRoamingDirectory();
+[[nodiscard]] const std::string& GetExeDirectory();
+[[nodiscard]] std::string AppDataRoamingDirectory();
#endif
std::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
// Splits the path on '/' or '\' and put the components into a vector
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
-std::vector<std::string> SplitPathComponents(std::string_view filename);
+[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename);
// Gets all of the text up to the last '/' or '\' in the path.
-std::string_view GetParentPath(std::string_view path);
+[[nodiscard]] std::string_view GetParentPath(std::string_view path);
// Gets all of the text after the first '/' or '\' in the path.
-std::string_view GetPathWithoutTop(std::string_view path);
+[[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path);
// Gets the filename of the path
-std::string_view GetFilename(std::string_view path);
+[[nodiscard]] std::string_view GetFilename(std::string_view path);
// Gets the extension of the filename
-std::string_view GetExtensionFromFilename(std::string_view name);
+[[nodiscard]] std::string_view GetExtensionFromFilename(std::string_view name);
// Removes the final '/' or '\' if one exists
-std::string_view RemoveTrailingSlash(std::string_view path);
+[[nodiscard]] std::string_view RemoveTrailingSlash(std::string_view path);
// Creates a new vector containing indices [first, last) from the original.
template <typename T>
-std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, std::size_t last) {
- if (first >= last)
+[[nodiscard]] std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first,
+ std::size_t last) {
+ if (first >= last) {
return {};
+ }
last = std::min<std::size_t>(last, vector.size());
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
}
-enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault };
+enum class DirectorySeparator {
+ ForwardSlash,
+ BackwardSlash,
+ PlatformDefault,
+};
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
-std::string SanitizePath(std::string_view path,
- DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
+[[nodiscard]] std::string SanitizePath(
+ std::string_view path,
+ DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
+
+// To deal with Windows being dumb at Unicode
+template <typename T>
+void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
+#ifdef _MSC_VER
+ fstream.open(Common::UTF8ToUTF16W(filename), openmode);
+#else
+ fstream.open(filename, openmode);
+#endif
+}
// simple wrapper for cstdlib file functions to
// hopefully will make error checking easier
@@ -215,7 +232,7 @@ public:
void Swap(IOFile& other) noexcept;
- bool Open(const std::string& filename, const char openmode[], int flags = 0);
+ [[nodiscard]] bool Open(const std::string& filename, const char openmode[], int flags = 0);
bool Close();
template <typename T>
@@ -256,13 +273,13 @@ public:
return WriteArray(str.data(), str.length());
}
- bool IsOpen() const {
+ [[nodiscard]] bool IsOpen() const {
return nullptr != m_file;
}
bool Seek(s64 off, int origin) const;
- u64 Tell() const;
- u64 GetSize() const;
+ [[nodiscard]] u64 Tell() const;
+ [[nodiscard]] u64 GetSize() const;
bool Resize(u64 size);
bool Flush();
@@ -278,14 +295,4 @@ private:
std::FILE* m_file = nullptr;
};
-} // namespace FileUtil
-
-// To deal with Windows being dumb at unicode:
-template <typename T>
-void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
-#ifdef _MSC_VER
- fstream.open(Common::UTF8ToUTF16W(filename), openmode);
-#else
- fstream.open(filename, openmode);
-#endif
-}
+} // namespace Common::FS
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 @@
#pragma once
#include <cstddef>
-#include <cstring>
#include <utility>
#include <boost/functional/hash.hpp>
-#include "common/cityhash.h"
-#include "common/common_types.h"
namespace Common {
-/**
- * Computes a 64-bit hash over the specified block of data
- * @param data Block of data to compute hash over
- * @param len Length of data (in bytes) to compute hash over
- * @returns 64-bit hash value that was computed over the data block
- */
-static inline u64 ComputeHash64(const void* data, std::size_t len) {
- return CityHash64(static_cast<const char*>(data), len);
-}
-
-/**
- * Computes a 64-bit hash of a struct. In addition to being trivially copyable, it is also critical
- * that either the struct includes no padding, or that any padding is initialized to a known value
- * by memsetting the struct to 0 before filling it in.
- */
-template <typename T>
-static inline u64 ComputeStructHash64(const T& data) {
- static_assert(std::is_trivially_copyable_v<T>,
- "Type passed to ComputeStructHash64 must be trivially copyable");
- return ComputeHash64(&data, sizeof(data));
-}
-
struct PairHash {
template <class T1, class T2>
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 @@
// Refer to the license.txt file included.
#include "common/hex_util.h"
-#include "common/logging/log.h"
namespace Common {
-u8 ToHexNibble(char c1) {
- if (c1 >= 65 && c1 <= 70)
- return c1 - 55;
- if (c1 >= 97 && c1 <= 102)
- return c1 - 87;
- if (c1 >= 48 && c1 <= 57)
- return c1 - 48;
- LOG_ERROR(Common, "Invalid hex digit: 0x{:02X}", c1);
- return 0;
-}
-
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
std::vector<u8> out(str.size() / 2);
if (little_endian) {
@@ -30,26 +18,4 @@ std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
return out;
}
-std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
- if (len != 32) {
- LOG_ERROR(Common,
- "Attempting to parse string to array that is not of correct size (expected=32, "
- "actual={}).",
- len);
- return {};
- }
- return HexStringToArray<16>(str);
-}
-
-std::array<u8, 32> operator""_array32(const char* str, std::size_t len) {
- if (len != 64) {
- LOG_ERROR(Common,
- "Attempting to parse string to array that is not of correct size (expected=64, "
- "actual={}).",
- len);
- return {};
- }
- return HexStringToArray<32>(str);
-}
-
} // 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 @@
namespace Common {
-u8 ToHexNibble(char c1);
+[[nodiscard]] constexpr u8 ToHexNibble(char c) {
+ if (c >= 65 && c <= 70) {
+ return c - 55;
+ }
+
+ if (c >= 97 && c <= 102) {
+ return c - 87;
+ }
+
+ return c - 48;
+}
-std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
+[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
template <std::size_t Size, bool le = false>
-std::array<u8, Size> HexStringToArray(std::string_view str) {
+[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
std::array<u8, Size> out{};
if constexpr (le) {
- for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2)
+ for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
+ }
} else {
- for (std::size_t i = 0; i < 2 * Size; i += 2)
+ for (std::size_t i = 0; i < 2 * Size; i += 2) {
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
+ }
}
return out;
}
template <typename ContiguousContainer>
-std::string HexToString(const ContiguousContainer& data, bool upper = true) {
+[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
"Underlying type within the contiguous container must be u8.");
@@ -48,7 +60,12 @@ std::string HexToString(const ContiguousContainer& data, bool upper = true) {
return out;
}
-std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len);
-std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len);
+[[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[17]) {
+ return HexStringToArray<16>(data);
+}
+
+[[nodiscard]] constexpr std::array<u8, 32> AsArray(const char (&data)[65]) {
+ return HexStringToArray<32>(data);
+}
} // namespace Common
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index e5d702568..da1c2f185 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -94,7 +94,7 @@ public:
void Write(const Entry& entry) override;
private:
- FileUtil::IOFile file;
+ Common::FS::IOFile file;
std::size_t bytes_written;
};
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) {
ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size");
const auto source_size_int = static_cast<int>(source_size);
- const int max_compressed_size = LZ4_compressBound(source_size_int);
+ const auto max_compressed_size = static_cast<std::size_t>(LZ4_compressBound(source_size_int));
std::vector<u8> compressed(max_compressed_size);
- const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source),
- reinterpret_cast<char*>(compressed.data()),
- source_size_int, max_compressed_size);
+ const int compressed_size = LZ4_compress_default(
+ reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()),
+ source_size_int, static_cast<int>(max_compressed_size));
if (compressed_size <= 0) {
// Compression failed
return {};
}
- compressed.resize(compressed_size);
+ compressed.resize(static_cast<std::size_t>(compressed_size));
return compressed;
}
@@ -38,19 +38,19 @@ std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX);
const auto source_size_int = static_cast<int>(source_size);
- const int max_compressed_size = LZ4_compressBound(source_size_int);
+ const auto max_compressed_size = static_cast<std::size_t>(LZ4_compressBound(source_size_int));
std::vector<u8> compressed(max_compressed_size);
const int compressed_size = LZ4_compress_HC(
reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()),
- source_size_int, max_compressed_size, compression_level);
+ source_size_int, static_cast<int>(max_compressed_size), compression_level);
if (compressed_size <= 0) {
// Compression failed
return {};
}
- compressed.resize(compressed_size);
+ compressed.resize(static_cast<std::size_t>(compressed_size));
return compressed;
}
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 {
/**
* Compresses a source memory region with LZ4 and returns the compressed data in a vector.
*
- * @param source the uncompressed source memory region.
- * @param source_size the size in bytes of the uncompressed source memory region.
+ * @param source The uncompressed source memory region.
+ * @param source_size The size of the uncompressed source memory region.
*
* @return the compressed data.
*/
-std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
+[[nodiscard]] std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
/**
* 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);
* compression level has almost no impact on decompression speed. Data compressed with LZ4HC can
* also be decompressed with the default LZ4 decompression.
*
- * @param source the uncompressed source memory region.
- * @param source_size the size in bytes of the uncompressed source memory region.
- * @param compression_level the used compression level. Should be between 3 and 12.
+ * @param source The uncompressed source memory region.
+ * @param source_size The size of the uncompressed source memory region.
+ * @param compression_level The used compression level. Should be between 3 and 12.
*
* @return the compressed data.
*/
-std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32 compression_level);
+[[nodiscard]] std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
+ s32 compression_level);
/**
* Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level.
*
- * @param source the uncompressed source memory region.
- * @param source_size the size in bytes of the uncompressed source memory region.
+ * @param source The uncompressed source memory region.
+ * @param source_size The size of the uncompressed source memory region
*
* @return the compressed data.
*/
-std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
+[[nodiscard]] std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
/**
* 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);
*
* @return the decompressed data.
*/
-std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, std::size_t uncompressed_size);
+[[nodiscard]] std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed,
+ std::size_t uncompressed_size);
} // 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 {
constexpr Rectangle(T left, T top, T right, T bottom)
: left(left), top(top), right(right), bottom(bottom) {}
- T GetWidth() const {
+ [[nodiscard]] T GetWidth() const {
if constexpr (std::is_floating_point_v<T>) {
return std::abs(right - left);
} else {
@@ -31,7 +31,7 @@ struct Rectangle {
}
}
- T GetHeight() const {
+ [[nodiscard]] T GetHeight() const {
if constexpr (std::is_floating_point_v<T>) {
return std::abs(bottom - top);
} else {
@@ -39,21 +39,21 @@ struct Rectangle {
}
}
- Rectangle<T> TranslateX(const T x) const {
+ [[nodiscard]] Rectangle<T> TranslateX(const T x) const {
return Rectangle{left + x, top, right + x, bottom};
}
- Rectangle<T> TranslateY(const T y) const {
+ [[nodiscard]] Rectangle<T> TranslateY(const T y) const {
return Rectangle{left, top + y, right, bottom + y};
}
- Rectangle<T> Scale(const float s) const {
+ [[nodiscard]] Rectangle<T> Scale(const float s) const {
return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
static_cast<T>(top + GetHeight() * s)};
}
};
template <typename T>
-Rectangle(T, T, T, T)->Rectangle<T>;
+Rectangle(T, T, T, T) -> Rectangle<T>;
} // 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 {
* Gets the memory info of the host system
* @return Reference to a MemoryInfo struct with the physical and swap memory sizes in bytes
*/
-const MemoryInfo& GetMemInfo();
+[[nodiscard]] const MemoryInfo& GetMemInfo();
} // 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:
ListShiftForward(levels[priority], n);
}
- std::size_t depth() const {
+ [[nodiscard]] std::size_t depth() const {
return Depth;
}
- std::size_t size(u32 priority) const {
+ [[nodiscard]] std::size_t size(u32 priority) const {
return levels[priority].size();
}
- std::size_t size() const {
+ [[nodiscard]] std::size_t size() const {
u64 priorities = used_priorities;
std::size_t size = 0;
while (priorities != 0) {
@@ -242,64 +242,64 @@ public:
return size;
}
- bool empty() const {
+ [[nodiscard]] bool empty() const {
return used_priorities == 0;
}
- bool empty(u32 priority) const {
+ [[nodiscard]] bool empty(u32 priority) const {
return (used_priorities & (1ULL << priority)) == 0;
}
- u32 highest_priority_set(u32 max_priority = 0) const {
+ [[nodiscard]] u32 highest_priority_set(u32 max_priority = 0) const {
const u64 priorities =
max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
}
- u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
+ [[nodiscard]] u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
const u64 priorities = min_priority >= Depth - 1
? used_priorities
: (used_priorities & ((1ULL << (min_priority + 1)) - 1));
return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
}
- const_iterator cbegin(u32 max_prio = 0) const {
+ [[nodiscard]] const_iterator cbegin(u32 max_prio = 0) const {
const u32 priority = highest_priority_set(max_prio);
return priority == Depth ? cend()
: const_iterator{*this, levels[priority].cbegin(), priority};
}
- const_iterator begin(u32 max_prio = 0) const {
+ [[nodiscard]] const_iterator begin(u32 max_prio = 0) const {
return cbegin(max_prio);
}
- iterator begin(u32 max_prio = 0) {
+ [[nodiscard]] iterator begin(u32 max_prio = 0) {
const u32 priority = highest_priority_set(max_prio);
return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
}
- const_iterator cend(u32 min_prio = Depth - 1) const {
+ [[nodiscard]] const_iterator cend(u32 min_prio = Depth - 1) const {
return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
}
- const_iterator end(u32 min_prio = Depth - 1) const {
+ [[nodiscard]] const_iterator end(u32 min_prio = Depth - 1) const {
return cend(min_prio);
}
- iterator end(u32 min_prio = Depth - 1) {
+ [[nodiscard]] iterator end(u32 min_prio = Depth - 1) {
return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
}
- T& front(u32 max_priority = 0) {
+ [[nodiscard]] T& front(u32 max_priority = 0) {
const u32 priority = highest_priority_set(max_priority);
return levels[priority == Depth ? 0 : priority].front();
}
- const T& front(u32 max_priority = 0) const {
+ [[nodiscard]] const T& front(u32 max_priority = 0) const {
const u32 priority = highest_priority_set(max_priority);
return levels[priority == Depth ? 0 : priority].front();
}
- T back(u32 min_priority = Depth - 1) {
+ [[nodiscard]] T& back(u32 min_priority = Depth - 1) {
const u32 priority = lowest_priority_set(min_priority); // intended
return levels[priority == Depth ? 63 : priority].back();
}
- const T& back(u32 min_priority = Depth - 1) const {
+ [[nodiscard]] const T& back(u32 min_priority = Depth - 1) const {
const u32 priority = lowest_priority_set(min_priority); // intended
return levels[priority == Depth ? 63 : priority].back();
}
@@ -329,7 +329,8 @@ private:
in_list.splice(position, out_list, element);
}
- static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) {
+ [[nodiscard]] static const_list_iterator ListIterateTo(const std::list<T>& list,
+ const T& element) {
auto it = list.cbegin();
while (it != list.cend() && *it != element) {
++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 {
MemoryHookPointer handler;
- bool operator<(const SpecialRegion& other) const {
+ [[nodiscard]] bool operator<(const SpecialRegion& other) const {
return std::tie(type, handler) < std::tie(other.type, other.handler);
}
- bool operator==(const SpecialRegion& other) const {
+ [[nodiscard]] bool operator==(const SpecialRegion& other) const {
return std::tie(type, handler) == std::tie(other.type, other.handler);
}
};
diff --git a/src/common/param_package.h b/src/common/param_package.h
index 6a0a9b656..c13e45479 100644
--- a/src/common/param_package.h
+++ b/src/common/param_package.h
@@ -19,19 +19,19 @@ public:
explicit ParamPackage(const std::string& serialized);
ParamPackage(std::initializer_list<DataType::value_type> list);
ParamPackage(const ParamPackage& other) = default;
- ParamPackage(ParamPackage&& other) = default;
+ ParamPackage(ParamPackage&& other) noexcept = default;
ParamPackage& operator=(const ParamPackage& other) = default;
ParamPackage& operator=(ParamPackage&& other) = default;
- std::string Serialize() const;
- std::string Get(const std::string& key, const std::string& default_value) const;
- int Get(const std::string& key, int default_value) const;
- float Get(const std::string& key, float default_value) const;
+ [[nodiscard]] std::string Serialize() const;
+ [[nodiscard]] std::string Get(const std::string& key, const std::string& default_value) const;
+ [[nodiscard]] int Get(const std::string& key, int default_value) const;
+ [[nodiscard]] float Get(const std::string& key, float default_value) const;
void Set(const std::string& key, std::string value);
void Set(const std::string& key, int value);
void Set(const std::string& key, float value);
- bool Has(const std::string& key) const;
+ [[nodiscard]] bool Has(const std::string& key) const;
void Erase(const std::string& key);
void Clear();
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:
Vec3<T> xyz;
T w{};
- Quaternion<decltype(-T{})> Inverse() const {
+ [[nodiscard]] Quaternion<decltype(-T{})> Inverse() const {
return {-xyz, w};
}
- Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const {
+ [[nodiscard]] Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const {
return {xyz + other.xyz, w + other.w};
}
- Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const {
+ [[nodiscard]] Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const {
return {xyz - other.xyz, w - other.w};
}
- Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(const Quaternion& other) const {
+ [[nodiscard]] Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(
+ const Quaternion& other) const {
return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),
w * other.w - Dot(xyz, other.xyz)};
}
- Quaternion<T> Normalized() const {
+ [[nodiscard]] Quaternion<T> Normalized() const {
T length = std::sqrt(xyz.Length2() + w * w);
return {xyz / length, w / length};
}
};
template <typename T>
-auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {
+[[nodiscard]] auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {
return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w);
}
-inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {
+[[nodiscard]] inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {
return {axis * std::sin(angle / 2), std::cos(angle / 2)};
}
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:
}
/// @returns Number of slots used
- std::size_t Size() const {
+ [[nodiscard]] std::size_t Size() const {
return m_write_index.load() - m_read_index.load();
}
/// @returns Maximum size of ring buffer
- constexpr std::size_t Capacity() const {
+ [[nodiscard]] constexpr std::size_t Capacity() const {
return capacity;
}
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 {
public:
void lock();
void unlock();
- bool try_lock();
+ [[nodiscard]] bool try_lock();
private:
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 @@
namespace Common {
/// Make a string lowercase
-std::string ToLower(std::string str);
+[[nodiscard]] std::string ToLower(std::string str);
/// Make a string uppercase
-std::string ToUpper(std::string str);
+[[nodiscard]] std::string ToUpper(std::string str);
-std::string StringFromBuffer(const std::vector<u8>& data);
+[[nodiscard]] std::string StringFromBuffer(const std::vector<u8>& data);
-std::string StripSpaces(const std::string& s);
-std::string StripQuotes(const std::string& s);
+[[nodiscard]] std::string StripSpaces(const std::string& s);
+[[nodiscard]] std::string StripQuotes(const std::string& s);
-std::string StringFromBool(bool value);
+[[nodiscard]] std::string StringFromBool(bool value);
-std::string TabsToSpaces(int tab_size, std::string in);
+[[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in);
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
@@ -34,14 +34,15 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path,
const std::string& _Filename);
-std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest);
+[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
+ const std::string& dest);
-std::string UTF16ToUTF8(const std::u16string& input);
-std::u16string UTF8ToUTF16(const std::string& input);
+[[nodiscard]] std::string UTF16ToUTF8(const std::u16string& input);
+[[nodiscard]] std::u16string UTF8ToUTF16(const std::string& input);
#ifdef _WIN32
-std::string UTF16ToUTF8(const std::wstring& input);
-std::wstring UTF8ToUTF16W(const std::string& str);
+[[nodiscard]] std::string UTF16ToUTF8(const std::wstring& input);
+[[nodiscard]] std::wstring UTF8ToUTF16W(const std::string& str);
#endif
@@ -50,7 +51,7 @@ std::wstring UTF8ToUTF16W(const std::string& str);
* `other` for equality.
*/
template <typename InIt>
-bool ComparePartialString(InIt begin, InIt end, const char* other) {
+[[nodiscard]] bool ComparePartialString(InIt begin, InIt end, const char* other) {
for (; begin != end && *other != '\0'; ++begin, ++other) {
if (*begin != *other) {
return false;
@@ -64,14 +65,15 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
* Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
* NUL-terminated then the string ends at max_len characters.
*/
-std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len);
+[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(const char* buffer,
+ std::size_t max_len);
/**
* Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
* null-terminated, then the string ends at the greatest multiple of two less then or equal to
* max_len_bytes.
*/
-std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
- std::size_t max_len);
+[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
+ std::size_t max_len);
} // namespace Common
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp
index 16d42facd..6241d08b3 100644
--- a/src/common/telemetry.cpp
+++ b/src/common/telemetry.cpp
@@ -12,7 +12,7 @@
#include "common/x64/cpu_detect.h"
#endif
-namespace Telemetry {
+namespace Common::Telemetry {
void FieldCollection::Accept(VisitorInterface& visitor) const {
for (const auto& field : fields) {
@@ -88,4 +88,4 @@ void AppendOSInfo(FieldCollection& fc) {
#endif
}
-} // namespace Telemetry
+} // namespace Common::Telemetry
diff --git a/src/common/telemetry.h b/src/common/telemetry.h
index 854a73fae..a50c5d1de 100644
--- a/src/common/telemetry.h
+++ b/src/common/telemetry.h
@@ -10,7 +10,7 @@
#include <string>
#include "common/common_types.h"
-namespace Telemetry {
+namespace Common::Telemetry {
/// Field type, used for grouping fields together in the final submitted telemetry log
enum class FieldType : u8 {
@@ -63,30 +63,30 @@ public:
void Accept(VisitorInterface& visitor) const override;
- const std::string& GetName() const override {
+ [[nodiscard]] const std::string& GetName() const override {
return name;
}
/**
* Returns the type of the field.
*/
- FieldType GetType() const {
+ [[nodiscard]] FieldType GetType() const {
return type;
}
/**
* Returns the value of the field.
*/
- const T& GetValue() const {
+ [[nodiscard]] const T& GetValue() const {
return value;
}
- bool operator==(const Field& other) const {
+ [[nodiscard]] bool operator==(const Field& other) const {
return (type == other.type) && (name == other.name) && (value == other.value);
}
- bool operator!=(const Field& other) const {
- return !(*this == other);
+ [[nodiscard]] bool operator!=(const Field& other) const {
+ return !operator==(other);
}
private:
@@ -196,4 +196,4 @@ void AppendCPUInfo(FieldCollection& fc);
/// such as platform name, etc.
void AppendOSInfo(FieldCollection& fc);
-} // namespace Telemetry
+} // namespace Common::Telemetry
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index 791f99a8c..def9e5d8d 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -18,14 +18,14 @@ struct ThreadQueueList {
using Priority = unsigned int;
// Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
- static const Priority NUM_QUEUES = N;
+ static constexpr Priority NUM_QUEUES = N;
ThreadQueueList() {
first = nullptr;
}
// Only for debugging, returns priority level.
- Priority contains(const T& uid) const {
+ [[nodiscard]] Priority contains(const T& uid) const {
for (Priority i = 0; i < NUM_QUEUES; ++i) {
const Queue& cur = queues[i];
if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
@@ -36,7 +36,7 @@ struct ThreadQueueList {
return -1;
}
- T get_first() const {
+ [[nodiscard]] T get_first() const {
const Queue* cur = first;
while (cur != nullptr) {
if (!cur->data.empty()) {
@@ -49,7 +49,7 @@ struct ThreadQueueList {
}
template <typename UnaryPredicate>
- T get_first_filter(UnaryPredicate filter) const {
+ [[nodiscard]] T get_first_filter(UnaryPredicate filter) const {
const Queue* cur = first;
while (cur != nullptr) {
if (!cur->data.empty()) {
@@ -129,7 +129,7 @@ struct ThreadQueueList {
first = nullptr;
}
- bool empty(Priority priority) const {
+ [[nodiscard]] bool empty(Priority priority) const {
const Queue* cur = &queues[priority];
return cur->data.empty();
}
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:
delete read_ptr;
}
- std::size_t Size() const {
+ [[nodiscard]] std::size_t Size() const {
return size.load();
}
- bool Empty() const {
+ [[nodiscard]] bool Empty() const {
return Size() == 0;
}
- T& Front() const {
+ [[nodiscard]] T& Front() const {
return read_ptr->current;
}
@@ -130,15 +130,15 @@ private:
template <typename T>
class MPSCQueue {
public:
- std::size_t Size() const {
+ [[nodiscard]] std::size_t Size() const {
return spsc_queue.Size();
}
- bool Empty() const {
+ [[nodiscard]] bool Empty() const {
return spsc_queue.Empty();
}
- T& Front() const {
+ [[nodiscard]] T& Front() const {
return spsc_queue.Front();
}
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 @@
namespace Common::TimeZone {
/// Gets the default timezone, i.e. "GMT"
-std::string GetDefaultTimeZone();
+[[nodiscard]] std::string GetDefaultTimeZone();
/// Gets the offset of the current timezone (from the default), in seconds
-std::chrono::seconds GetCurrentOffsetSeconds();
+[[nodiscard]] std::chrono::seconds GetCurrentOffsetSeconds();
} // 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:
// The time difference is always returned in milliseconds, regardless of alternative internal
// representation
- std::chrono::milliseconds GetTimeDifference();
+ [[nodiscard]] std::chrono::milliseconds GetTimeDifference();
void AddTimeDifference();
- static std::chrono::seconds GetTimeSinceJan1970();
- static std::chrono::seconds GetLocalTimeSinceJan1970();
- static double GetDoubleTime();
+ [[nodiscard]] static std::chrono::seconds GetTimeSinceJan1970();
+ [[nodiscard]] static std::chrono::seconds GetLocalTimeSinceJan1970();
+ [[nodiscard]] static double GetDoubleTime();
- static std::string GetTimeFormatted();
- std::string GetTimeElapsedFormatted() const;
- std::chrono::milliseconds GetTimeElapsed();
+ [[nodiscard]] static std::string GetTimeFormatted();
+ [[nodiscard]] std::string GetTimeElapsedFormatted() const;
+ [[nodiscard]] std::chrono::milliseconds GetTimeElapsed();
- static std::chrono::milliseconds GetTimeMs();
+ [[nodiscard]] static std::chrono::milliseconds GetTimeMs();
private:
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 @@
namespace Common {
// This function multiplies 2 u64 values and divides it by a u64 value.
-u64 MultiplyAndDivide64(u64 a, u64 b, u64 d);
+[[nodiscard]] u64 MultiplyAndDivide64(u64 a, u64 b, u64 d);
// This function multiplies 2 u64 values and produces a u128 value;
-u128 Multiply64Into128(u64 a, u64 b);
+[[nodiscard]] u128 Multiply64Into128(u64 a, u64 b);
// This function divides a u128 by a u32 value and produces two u64 values:
// the result of division and the remainder
-std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
+[[nodiscard]] std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
} // 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 {
constexpr explicit UUID(const u128& id) : uuid{id} {}
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
- constexpr explicit operator bool() const {
+ [[nodiscard]] constexpr explicit operator bool() const {
return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1];
}
- constexpr bool operator==(const UUID& rhs) const {
+ [[nodiscard]] constexpr bool operator==(const UUID& rhs) const {
// TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20
return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1];
}
- constexpr bool operator!=(const UUID& rhs) const {
+ [[nodiscard]] constexpr bool operator!=(const UUID& rhs) const {
return !operator==(rhs);
}
// TODO(ogniK): Properly generate uuids based on RFC-4122
- static UUID Generate();
+ [[nodiscard]] static UUID Generate();
// Set the UUID to {0,0} to be considered an invalid user
constexpr void Invalidate() {
@@ -41,12 +41,12 @@ struct UUID {
}
// TODO(ogniK): Properly generate a Nintendo ID
- constexpr u64 GetNintendoID() const {
+ [[nodiscard]] constexpr u64 GetNintendoID() const {
return uuid[0];
}
- std::string Format() const;
- std::string FormatSwitch() const;
+ [[nodiscard]] std::string Format() const;
+ [[nodiscard]] std::string FormatSwitch() const;
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
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:
constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
template <typename T2>
- constexpr Vec2<T2> Cast() const {
+ [[nodiscard]] constexpr Vec2<T2> Cast() const {
return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
}
- static constexpr Vec2 AssignToAll(const T& f) {
+ [[nodiscard]] static constexpr Vec2 AssignToAll(const T& f) {
return Vec2{f, f};
}
- constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
+ [[nodiscard]] constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
return {x + other.x, y + other.y};
}
constexpr Vec2& operator+=(const Vec2& other) {
@@ -68,7 +68,7 @@ public:
y += other.y;
return *this;
}
- constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
+ [[nodiscard]] constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
return {x - other.x, y - other.y};
}
constexpr Vec2& operator-=(const Vec2& other) {
@@ -78,15 +78,15 @@ public:
}
template <typename U = T>
- constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
+ [[nodiscard]] constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y};
}
- constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
+ [[nodiscard]] constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
return {x * other.x, y * other.y};
}
template <typename V>
- constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
+ [[nodiscard]] constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
return {x * f, y * f};
}
@@ -97,7 +97,7 @@ public:
}
template <typename V>
- constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
+ [[nodiscard]] constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
return {x / f, y / f};
}
@@ -107,18 +107,18 @@ public:
return *this;
}
- constexpr T Length2() const {
+ [[nodiscard]] constexpr T Length2() const {
return x * x + y * y;
}
// Only implemented for T=float
- float Length() const;
- float Normalize(); // returns the previous length, which is often useful
+ [[nodiscard]] float Length() const;
+ [[nodiscard]] float Normalize(); // returns the previous length, which is often useful
- constexpr T& operator[](std::size_t i) {
+ [[nodiscard]] constexpr T& operator[](std::size_t i) {
return *((&x) + i);
}
- constexpr const T& operator[](std::size_t i) const {
+ [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
@@ -128,46 +128,46 @@ public:
}
// Common aliases: UV (texel coordinates), ST (texture coordinates)
- constexpr T& u() {
+ [[nodiscard]] constexpr T& u() {
return x;
}
- constexpr T& v() {
+ [[nodiscard]] constexpr T& v() {
return y;
}
- constexpr T& s() {
+ [[nodiscard]] constexpr T& s() {
return x;
}
- constexpr T& t() {
+ [[nodiscard]] constexpr T& t() {
return y;
}
- constexpr const T& u() const {
+ [[nodiscard]] constexpr const T& u() const {
return x;
}
- constexpr const T& v() const {
+ [[nodiscard]] constexpr const T& v() const {
return y;
}
- constexpr const T& s() const {
+ [[nodiscard]] constexpr const T& s() const {
return x;
}
- constexpr const T& t() const {
+ [[nodiscard]] constexpr const T& t() const {
return y;
}
// swizzlers - create a subvector of specific components
- constexpr Vec2 yx() const {
+ [[nodiscard]] constexpr Vec2 yx() const {
return Vec2(y, x);
}
- constexpr Vec2 vu() const {
+ [[nodiscard]] constexpr Vec2 vu() const {
return Vec2(y, x);
}
- constexpr Vec2 ts() const {
+ [[nodiscard]] constexpr Vec2 ts() const {
return Vec2(y, x);
}
};
template <typename T, typename V>
-constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
+[[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
return Vec2<T>(f * vec.x, f * vec.y);
}
@@ -196,15 +196,15 @@ public:
constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
template <typename T2>
- constexpr Vec3<T2> Cast() const {
+ [[nodiscard]] constexpr Vec3<T2> Cast() const {
return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
}
- static constexpr Vec3 AssignToAll(const T& f) {
+ [[nodiscard]] static constexpr Vec3 AssignToAll(const T& f) {
return Vec3(f, f, f);
}
- constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
+ [[nodiscard]] constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
return {x + other.x, y + other.y, z + other.z};
}
@@ -215,7 +215,7 @@ public:
return *this;
}
- constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
+ [[nodiscard]] constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
return {x - other.x, y - other.y, z - other.z};
}
@@ -227,16 +227,16 @@ public:
}
template <typename U = T>
- constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
+ [[nodiscard]] constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y, -z};
}
- constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
+ [[nodiscard]] constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
return {x * other.x, y * other.y, z * other.z};
}
template <typename V>
- constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
+ [[nodiscard]] constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
return {x * f, y * f, z * f};
}
@@ -246,7 +246,7 @@ public:
return *this;
}
template <typename V>
- constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
+ [[nodiscard]] constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
return {x / f, y / f, z / f};
}
@@ -256,20 +256,20 @@ public:
return *this;
}
- constexpr T Length2() const {
+ [[nodiscard]] constexpr T Length2() const {
return x * x + y * y + z * z;
}
// Only implemented for T=float
- float Length() const;
- Vec3 Normalized() const;
- float Normalize(); // returns the previous length, which is often useful
+ [[nodiscard]] float Length() const;
+ [[nodiscard]] Vec3 Normalized() const;
+ [[nodiscard]] float Normalize(); // returns the previous length, which is often useful
- constexpr T& operator[](std::size_t i) {
+ [[nodiscard]] constexpr T& operator[](std::size_t i) {
return *((&x) + i);
}
- constexpr const T& operator[](std::size_t i) const {
+ [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
@@ -280,63 +280,63 @@ public:
}
// Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
- constexpr T& u() {
+ [[nodiscard]] constexpr T& u() {
return x;
}
- constexpr T& v() {
+ [[nodiscard]] constexpr T& v() {
return y;
}
- constexpr T& w() {
+ [[nodiscard]] constexpr T& w() {
return z;
}
- constexpr T& r() {
+ [[nodiscard]] constexpr T& r() {
return x;
}
- constexpr T& g() {
+ [[nodiscard]] constexpr T& g() {
return y;
}
- constexpr T& b() {
+ [[nodiscard]] constexpr T& b() {
return z;
}
- constexpr T& s() {
+ [[nodiscard]] constexpr T& s() {
return x;
}
- constexpr T& t() {
+ [[nodiscard]] constexpr T& t() {
return y;
}
- constexpr T& q() {
+ [[nodiscard]] constexpr T& q() {
return z;
}
- constexpr const T& u() const {
+ [[nodiscard]] constexpr const T& u() const {
return x;
}
- constexpr const T& v() const {
+ [[nodiscard]] constexpr const T& v() const {
return y;
}
- constexpr const T& w() const {
+ [[nodiscard]] constexpr const T& w() const {
return z;
}
- constexpr const T& r() const {
+ [[nodiscard]] constexpr const T& r() const {
return x;
}
- constexpr const T& g() const {
+ [[nodiscard]] constexpr const T& g() const {
return y;
}
- constexpr const T& b() const {
+ [[nodiscard]] constexpr const T& b() const {
return z;
}
- constexpr const T& s() const {
+ [[nodiscard]] constexpr const T& s() const {
return x;
}
- constexpr const T& t() const {
+ [[nodiscard]] constexpr const T& t() const {
return y;
}
- constexpr const T& q() const {
+ [[nodiscard]] constexpr const T& q() const {
return z;
}
@@ -345,7 +345,7 @@ public:
// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
// component names (x<->r) and permutations (xy<->yx)
#define _DEFINE_SWIZZLER2(a, b, name) \
- constexpr Vec2<T> name() const { \
+ [[nodiscard]] constexpr Vec2<T> name() const { \
return Vec2<T>(a, b); \
}
#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
@@ -366,7 +366,7 @@ public:
};
template <typename T, typename V>
-constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
+[[nodiscard]] constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
return Vec3<T>(f * vec.x, f * vec.y, f * vec.z);
}
@@ -402,16 +402,16 @@ public:
: x(x_), y(y_), z(z_), w(w_) {}
template <typename T2>
- constexpr Vec4<T2> Cast() const {
+ [[nodiscard]] constexpr Vec4<T2> Cast() const {
return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
static_cast<T2>(w));
}
- static constexpr Vec4 AssignToAll(const T& f) {
+ [[nodiscard]] static constexpr Vec4 AssignToAll(const T& f) {
return Vec4(f, f, f, f);
}
- constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
+ [[nodiscard]] constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
return {x + other.x, y + other.y, z + other.z, w + other.w};
}
@@ -423,7 +423,7 @@ public:
return *this;
}
- constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
+ [[nodiscard]] constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
return {x - other.x, y - other.y, z - other.z, w - other.w};
}
@@ -436,16 +436,16 @@ public:
}
template <typename U = T>
- constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
+ [[nodiscard]] constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y, -z, -w};
}
- constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
+ [[nodiscard]] constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
return {x * other.x, y * other.y, z * other.z, w * other.w};
}
template <typename V>
- constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
+ [[nodiscard]] constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
return {x * f, y * f, z * f, w * f};
}
@@ -456,7 +456,7 @@ public:
}
template <typename V>
- constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
+ [[nodiscard]] constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
return {x / f, y / f, z / f, w / f};
}
@@ -466,15 +466,15 @@ public:
return *this;
}
- constexpr T Length2() const {
+ [[nodiscard]] constexpr T Length2() const {
return x * x + y * y + z * z + w * w;
}
- constexpr T& operator[](std::size_t i) {
+ [[nodiscard]] constexpr T& operator[](std::size_t i) {
return *((&x) + i);
}
- constexpr const T& operator[](std::size_t i) const {
+ [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
@@ -486,29 +486,29 @@ public:
}
// Common alias: RGBA (colors)
- constexpr T& r() {
+ [[nodiscard]] constexpr T& r() {
return x;
}
- constexpr T& g() {
+ [[nodiscard]] constexpr T& g() {
return y;
}
- constexpr T& b() {
+ [[nodiscard]] constexpr T& b() {
return z;
}
- constexpr T& a() {
+ [[nodiscard]] constexpr T& a() {
return w;
}
- constexpr const T& r() const {
+ [[nodiscard]] constexpr const T& r() const {
return x;
}
- constexpr const T& g() const {
+ [[nodiscard]] constexpr const T& g() const {
return y;
}
- constexpr const T& b() const {
+ [[nodiscard]] constexpr const T& b() const {
return z;
}
- constexpr const T& a() const {
+ [[nodiscard]] constexpr const T& a() const {
return w;
}
@@ -520,7 +520,7 @@ public:
// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
// permutations (xy<->yx)
#define _DEFINE_SWIZZLER2(a, b, name) \
- constexpr Vec2<T> name() const { \
+ [[nodiscard]] constexpr Vec2<T> name() const { \
return Vec2<T>(a, b); \
}
#define DEFINE_SWIZZLER2_COMP1(a, a2) \
@@ -547,7 +547,7 @@ public:
#undef _DEFINE_SWIZZLER2
#define _DEFINE_SWIZZLER3(a, b, c, name) \
- constexpr Vec3<T> name() const { \
+ [[nodiscard]] constexpr Vec3<T> name() const { \
return Vec3<T>(a, b, c); \
}
#define DEFINE_SWIZZLER3_COMP1(a, a2) \
@@ -581,7 +581,7 @@ public:
};
template <typename T, typename V>
-constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
+[[nodiscard]] constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
return {f * vec.x, f * vec.y, f * vec.z, f * vec.w};
}
@@ -593,39 +593,41 @@ constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b
}
template <typename T>
-constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
+[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
template <typename T>
-constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
+[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
}
template <typename T>
-constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) {
+[[nodiscard]] constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a,
+ const Vec3<T>& b) {
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};
}
// linear interpolation via float: 0.0=begin, 1.0=end
template <typename X>
-constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
- const float t) {
+[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
+ const float t) {
return begin * (1.f - t) + end * t;
}
// linear interpolation via int: 0=begin, base=end
template <typename X, int base>
-constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end,
- const int t) {
+[[nodiscard]] constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin,
+ const X& end,
+ const int t) {
return (begin * (base - t) + end * t) / base;
}
// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
// interpolation.
template <typename X>
-constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s,
- const float t) {
+[[nodiscard]] constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11,
+ const float s, const float t) {
auto y0 = Lerp(x00, x01, s);
auto y1 = Lerp(x10, x11, s);
return Lerp(y0, y1, t);
@@ -633,42 +635,42 @@ constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X&
// Utility vector factories
template <typename T>
-constexpr Vec2<T> MakeVec(const T& x, const T& y) {
+[[nodiscard]] constexpr Vec2<T> MakeVec(const T& x, const T& y) {
return Vec2<T>{x, y};
}
template <typename T>
-constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
+[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
return Vec3<T>{x, y, z};
}
template <typename T>
-constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
+[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
return MakeVec(x, y, zw[0], zw[1]);
}
template <typename T>
-constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
+[[nodiscard]] constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
return MakeVec(xy[0], xy[1], z);
}
template <typename T>
-constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
+[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
return MakeVec(x, yz[0], yz[1]);
}
template <typename T>
-constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
+[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
return Vec4<T>{x, y, z, w};
}
template <typename T>
-constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
+[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
return MakeVec(xy[0], xy[1], z, w);
}
template <typename T>
-constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
+[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
return MakeVec(x, yz[0], yz[1], w);
}
@@ -676,17 +678,17 @@ constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
// out soon enough due to misuse of the returned structure.
template <typename T>
-constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
+[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
return MakeVec(xy[0], xy[1], zw[0], zw[1]);
}
template <typename T>
-constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
+[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
return MakeVec(xyz[0], xyz[1], xyz[2], w);
}
template <typename T>
-constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
+[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
return MakeVec(x, yzw[0], yzw[1], yzw[2]);
}
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 @@
#ifdef _WIN32
#include <windows.h>
#else
-#include <stdio.h>
#include <sys/mman.h>
-#include <sys/types.h>
-#if defined __APPLE__ || defined __FreeBSD__ || defined __OpenBSD__
-#include <sys/sysctl.h>
-#elif defined __HAIKU__
-#include <OS.h>
-#else
-#include <sys/sysinfo.h>
-#endif
#endif
#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:
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
}
- constexpr const T& operator[](std::size_t index) const {
+ [[nodiscard]] constexpr const T& operator[](std::size_t index) const {
return base_ptr[index];
}
- constexpr T& operator[](std::size_t index) {
+ [[nodiscard]] constexpr T& operator[](std::size_t index) {
return base_ptr[index];
}
- constexpr T* data() {
+ [[nodiscard]] constexpr T* data() {
return base_ptr;
}
- constexpr const T* data() const {
+ [[nodiscard]] constexpr const T* data() const {
return base_ptr;
}
- constexpr std::size_t size() const {
+ [[nodiscard]] constexpr std::size_t size() const {
return alloc_size / sizeof(T);
}
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 {
class WallClock {
public:
/// Returns current wall time in nanoseconds
- virtual std::chrono::nanoseconds GetTimeNS() = 0;
+ [[nodiscard]] virtual std::chrono::nanoseconds GetTimeNS() = 0;
/// Returns current wall time in microseconds
- virtual std::chrono::microseconds GetTimeUS() = 0;
+ [[nodiscard]] virtual std::chrono::microseconds GetTimeUS() = 0;
/// Returns current wall time in milliseconds
- virtual std::chrono::milliseconds GetTimeMS() = 0;
+ [[nodiscard]] virtual std::chrono::milliseconds GetTimeMS() = 0;
/// Returns current wall time in emulated clock cycles
- virtual u64 GetClockCycles() = 0;
+ [[nodiscard]] virtual u64 GetClockCycles() = 0;
/// Returns current wall time in emulated cpu cycles
- virtual u64 GetCPUCycles() = 0;
+ [[nodiscard]] virtual u64 GetCPUCycles() = 0;
virtual void Pause(bool is_paused) = 0;
/// Tells if the wall clock, uses the host CPU's hardware clock
- bool IsNative() const {
+ [[nodiscard]] bool IsNative() const {
return is_native;
}
@@ -47,7 +47,7 @@ private:
bool is_native;
};
-std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
- u32 emulated_clock_frequency);
+[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
+ u32 emulated_clock_frequency);
} // 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 @@
#include <algorithm>
#include <zstd.h>
-#include "common/assert.h"
#include "common/zstd_compression.h"
namespace 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 {
/**
* Compresses a source memory region with Zstandard and returns the compressed data in a vector.
*
- * @param source the uncompressed source memory region.
- * @param source_size the size in bytes of the uncompressed source memory region.
- * @param compression_level the used compression level. Should be between 1 and 22.
+ * @param source The uncompressed source memory region.
+ * @param source_size The size of the uncompressed source memory region.
+ * @param compression_level The used compression level. Should be between 1 and 22.
*
* @return the compressed data.
*/
-std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level);
+[[nodiscard]] std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size,
+ s32 compression_level);
/**
* Compresses a source memory region with Zstandard with the default compression level and returns
* the compressed data in a vector.
*
- * @param source the uncompressed source memory region.
- * @param source_size the size in bytes of the uncompressed source memory region.
+ * @param source The uncompressed source memory region.
+ * @param source_size The size of the uncompressed source memory region.
*
* @return the compressed data.
*/
-std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size);
+[[nodiscard]] std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size);
/**
* 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
*
* @return the decompressed data.
*/
-std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed);
+[[nodiscard]] std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed);
} // namespace Common::Compression \ No newline at end of file
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
index df0350881..9c8898700 100644
--- a/src/core/arm/cpu_interrupt_handler.cpp
+++ b/src/core/arm/cpu_interrupt_handler.cpp
@@ -7,9 +7,7 @@
namespace Core {
-CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} {
- interrupt_event = std::make_unique<Common::Event>();
-}
+CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {}
CPUInterruptHandler::~CPUInterruptHandler() = default;
@@ -17,7 +15,7 @@ void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
if (is_interrupted_) {
interrupt_event->Set();
}
- this->is_interrupted = is_interrupted_;
+ is_interrupted = is_interrupted_;
}
void CPUInterruptHandler::AwaitInterrupt() {
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
index 3d062d326..71e582f79 100644
--- a/src/core/arm/cpu_interrupt_handler.h
+++ b/src/core/arm/cpu_interrupt_handler.h
@@ -4,6 +4,7 @@
#pragma once
+#include <atomic>
#include <memory>
namespace Common {
@@ -32,8 +33,8 @@ public:
void AwaitInterrupt();
private:
- bool is_interrupted{};
std::unique_ptr<Common::Event> interrupt_event;
+ std::atomic_bool is_interrupted{false};
};
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 443ca72eb..b5f28a86e 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -143,7 +143,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
config.wall_clock_cntpct = uses_wall_clock;
// Safe optimizations
- if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
+ if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
if (!Settings::values.cpuopt_page_tables) {
config.page_table = nullptr;
}
@@ -170,6 +170,17 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
}
}
+ // Unsafe optimizations
+ if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) {
+ config.unsafe_optimizations = true;
+ if (Settings::values.cpuopt_unsafe_unfuse_fma) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ }
+ if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
+ }
+ }
+
return std::make_unique<Dynarmic::A32::Jit>(config);
}
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index a63a04a25..ce9968724 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -195,7 +195,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
config.wall_clock_cntpct = uses_wall_clock;
// Safe optimizations
- if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
+ if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
if (!Settings::values.cpuopt_page_tables) {
config.page_table = nullptr;
}
@@ -222,6 +222,17 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
}
}
+ // Unsafe optimizations
+ if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) {
+ config.unsafe_optimizations = true;
+ if (Settings::values.cpuopt_unsafe_unfuse_fma) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ }
+ if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
+ }
+ }
+
return std::make_shared<Dynarmic::A64::Jit>(config);
}
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 42277e2cd..c2c0eec0b 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -113,7 +113,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
}
- if (FileUtil::IsDirectory(path))
+ if (Common::FS::IsDirectory(path))
return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read);
return vfs->OpenFile(path, FileSys::Mode::Read);
@@ -269,14 +269,14 @@ struct System::Impl {
// Log last frame performance stats if game was loded
if (perf_stats) {
const auto perf_results = GetAndResetPerfStats();
- telemetry_session->AddField(Telemetry::FieldType::Performance,
- "Shutdown_EmulationSpeed",
+ constexpr auto performance = Common::Telemetry::FieldType::Performance;
+
+ telemetry_session->AddField(performance, "Shutdown_EmulationSpeed",
perf_results.emulation_speed * 100.0);
- telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
- perf_results.game_fps);
- telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
+ telemetry_session->AddField(performance, "Shutdown_Framerate", perf_results.game_fps);
+ telemetry_session->AddField(performance, "Shutdown_Frametime",
perf_results.frametime * 1000.0);
- telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS",
+ telemetry_session->AddField(performance, "Mean_Frametime_MS",
perf_stats->GetMeanFrametime());
}
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 71af26ec5..e6c8461a5 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -7,14 +7,14 @@
#include <string>
#include <tuple>
-#include "common/assert.h"
#include "common/microprofile.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
+#include "core/hardware_properties.h"
namespace Core::Timing {
-constexpr u64 MAX_SLICE_LENGTH = 4000;
+constexpr s64 MAX_SLICE_LENGTH = 4000;
std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
return std::make_shared<EventType>(std::move(callback), std::move(name));
@@ -37,10 +37,8 @@ struct CoreTiming::Event {
}
};
-CoreTiming::CoreTiming() {
- clock =
- Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ);
-}
+CoreTiming::CoreTiming()
+ : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {}
CoreTiming::~CoreTiming() = default;
@@ -136,7 +134,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
void CoreTiming::AddTicks(u64 ticks) {
this->ticks += ticks;
- downcount -= ticks;
+ downcount -= static_cast<s64>(ticks);
}
void CoreTiming::Idle() {
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp
index aefc63663..8ce8e602e 100644
--- a/src/core/core_timing_util.cpp
+++ b/src/core/core_timing_util.cpp
@@ -8,6 +8,7 @@
#include <limits>
#include "common/logging/log.h"
#include "common/uint128.h"
+#include "core/hardware_properties.h"
namespace Core::Timing {
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
index 2ed979e14..e4a046bf9 100644
--- a/src/core/core_timing_util.h
+++ b/src/core/core_timing_util.h
@@ -6,7 +6,6 @@
#include <chrono>
#include "common/common_types.h"
-#include "core/hardware_properties.h"
namespace Core::Timing {
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 03651e285..688b99eba 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -41,9 +41,9 @@ void CpuManager::Shutdown() {
running_mode = false;
Pause(false);
if (is_multicore) {
- for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- core_data[core].host_thread->join();
- core_data[core].host_thread.reset();
+ for (auto& data : core_data) {
+ data.host_thread->join();
+ data.host_thread.reset();
}
} else {
core_data[0].host_thread->join();
@@ -166,25 +166,23 @@ void CpuManager::MultiCorePause(bool paused) {
bool all_not_barrier = false;
while (!all_not_barrier) {
all_not_barrier = true;
- for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- all_not_barrier &=
- !core_data[core].is_running.load() && core_data[core].initialized.load();
+ for (const auto& data : core_data) {
+ all_not_barrier &= !data.is_running.load() && data.initialized.load();
}
}
- for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- core_data[core].enter_barrier->Set();
+ for (auto& data : core_data) {
+ data.enter_barrier->Set();
}
if (paused_state.load()) {
bool all_barrier = false;
while (!all_barrier) {
all_barrier = true;
- for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- all_barrier &=
- core_data[core].is_paused.load() && core_data[core].initialized.load();
+ for (const auto& data : core_data) {
+ all_barrier &= data.is_paused.load() && data.initialized.load();
}
}
- for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- core_data[core].exit_barrier->Set();
+ for (auto& data : core_data) {
+ data.exit_barrier->Set();
}
}
} else {
@@ -192,9 +190,8 @@ void CpuManager::MultiCorePause(bool paused) {
bool all_barrier = false;
while (!all_barrier) {
all_barrier = true;
- for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- all_barrier &=
- core_data[core].is_paused.load() && core_data[core].initialized.load();
+ for (const auto& data : core_data) {
+ all_barrier &= data.is_paused.load() && data.initialized.load();
}
}
/// Don't release the barrier
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp
index 330996b24..6a9734812 100644
--- a/src/core/crypto/aes_util.cpp
+++ b/src/core/crypto/aes_util.cpp
@@ -116,7 +116,7 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8*
for (std::size_t i = 0; i < size; i += sector_size) {
SetIV(CalculateNintendoTweak(sector_id++));
- Transcode<u8, u8>(src + i, sector_size, dest + i, op);
+ Transcode(src + i, sector_size, dest + i, op);
}
}
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index f87fe0abc..dc591c730 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -36,18 +36,86 @@
#include "core/settings.h"
namespace Core::Crypto {
+namespace {
constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
constexpr u64 FULL_TICKET_SIZE = 0x400;
-using namespace Common;
+using Common::AsArray;
-const std::array<SHA256Hash, 2> eticket_source_hashes{
- "B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source
- "E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source
+// clang-format off
+constexpr std::array eticket_source_hashes{
+ AsArray("B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"), // eticket_rsa_kek_source
+ AsArray("E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"), // eticket_rsa_kekek_source
};
+// clang-format on
-const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
+constexpr std::array<std::pair<std::string_view, KeyIndex<S128KeyType>>, 30> s128_file_id{{
+ {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
+ {"eticket_rsa_kek_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
+ {"eticket_rsa_kekek_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
+ {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
+ {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
+ {"rsa_oaep_kek_generation_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
+ {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
+ {"aes_kek_generation_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
+ {"aes_key_generation_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
+ {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
+ {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
+ {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
+ {"key_area_key_application_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
+ static_cast<u64>(KeyAreaKeyType::Application)}},
+ {"key_area_key_ocean_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
+ static_cast<u64>(KeyAreaKeyType::Ocean)}},
+ {"key_area_key_system_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
+ static_cast<u64>(KeyAreaKeyType::System)}},
+ {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
+ {"keyblob_mac_key_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},
+ {"tsec_key", {S128KeyType::TSEC, 0, 0}},
+ {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
+ {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
+ {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
+ {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
+ {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
+ {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
+ {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
+ {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
+ {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
+ {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
+ {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
+ {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
+}};
+
+auto Find128ByName(std::string_view name) {
+ return std::find_if(s128_file_id.begin(), s128_file_id.end(),
+ [&name](const auto& pair) { return pair.first == name; });
+}
+
+constexpr std::array<std::pair<std::string_view, KeyIndex<S256KeyType>>, 6> s256_file_id{{
+ {"header_key", {S256KeyType::Header, 0, 0}},
+ {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
+ {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
+ {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
+ {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
+ {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
+}};
+
+auto Find256ByName(std::string_view name) {
+ return std::find_if(s256_file_id.begin(), s256_file_id.end(),
+ [&name](const auto& pair) { return pair.first == name; });
+}
+
+using KeyArray = std::array<std::pair<std::pair<S128KeyType, u64>, std::string_view>, 7>;
+constexpr KeyArray KEYS_VARIABLE_LENGTH{{
{{S128KeyType::Master, 0}, "master_key_"},
{{S128KeyType::Package1, 0}, "package1_key_"},
{{S128KeyType::Package2, 0}, "package2_key_"},
@@ -55,14 +123,13 @@ const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
{{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"},
{{S128KeyType::Keyblob, 0}, "keyblob_key_"},
{{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
-};
+}};
-namespace {
template <std::size_t Size>
bool IsAllZeroArray(const std::array<u8, Size>& array) {
return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; });
}
-} // namespace
+} // Anonymous namespace
u64 GetSignatureTypeDataSize(SignatureType type) {
switch (type) {
@@ -94,13 +161,13 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
}
SignatureType Ticket::GetSignatureType() const {
- if (auto ticket = std::get_if<RSA4096Ticket>(&data)) {
+ if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
return ticket->sig_type;
}
- if (auto ticket = std::get_if<RSA2048Ticket>(&data)) {
+ if (const auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
return ticket->sig_type;
}
- if (auto ticket = std::get_if<ECDSATicket>(&data)) {
+ if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
return ticket->sig_type;
}
@@ -108,13 +175,13 @@ SignatureType Ticket::GetSignatureType() const {
}
TicketData& Ticket::GetData() {
- if (auto ticket = std::get_if<RSA4096Ticket>(&data)) {
+ if (auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
return ticket->data;
}
- if (auto ticket = std::get_if<RSA2048Ticket>(&data)) {
+ if (auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
return ticket->data;
}
- if (auto ticket = std::get_if<ECDSATicket>(&data)) {
+ if (auto* ticket = std::get_if<ECDSATicket>(&data)) {
return ticket->data;
}
@@ -122,13 +189,13 @@ TicketData& Ticket::GetData() {
}
const TicketData& Ticket::GetData() const {
- if (auto ticket = std::get_if<RSA4096Ticket>(&data)) {
+ if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
return ticket->data;
}
- if (auto ticket = std::get_if<RSA2048Ticket>(&data)) {
+ if (const auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
return ticket->data;
}
- if (auto ticket = std::get_if<ECDSATicket>(&data)) {
+ if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
return ticket->data;
}
@@ -231,8 +298,9 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
}
RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const {
- if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek))
+ if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) {
return {};
+ }
const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek);
@@ -259,27 +327,30 @@ Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source)
}
std::optional<Key128> DeriveSDSeed() {
- const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
- "/system/save/8000000000000043",
- "rb+");
- if (!save_43.IsOpen())
- return {};
+ const Common::FS::IOFile save_43(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
+ "/system/save/8000000000000043",
+ "rb+");
+ if (!save_43.IsOpen()) {
+ return std::nullopt;
+ }
- const FileUtil::IOFile sd_private(
- FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
- if (!sd_private.IsOpen())
- return {};
+ const Common::FS::IOFile sd_private(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
+ "/Nintendo/Contents/private",
+ "rb+");
+ if (!sd_private.IsOpen()) {
+ return std::nullopt;
+ }
std::array<u8, 0x10> private_seed{};
if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
- return {};
+ return std::nullopt;
}
std::array<u8, 0x10> buffer{};
std::size_t offset = 0;
for (; offset + 0x10 < save_43.GetSize(); ++offset) {
if (!save_43.Seek(offset, SEEK_SET)) {
- return {};
+ return std::nullopt;
}
save_43.ReadBytes(buffer.data(), buffer.size());
@@ -289,23 +360,26 @@ std::optional<Key128> DeriveSDSeed() {
}
if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
- return {};
+ return std::nullopt;
}
Key128 seed{};
if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
- return {};
+ return std::nullopt;
}
return seed;
}
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) {
- if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek)))
+ if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek))) {
return Loader::ResultStatus::ErrorMissingSDKEKSource;
- if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)))
+ }
+ if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration))) {
return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource;
- if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)))
+ }
+ if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) {
return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource;
+ }
const auto sd_kek_source =
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek));
@@ -318,14 +392,17 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen);
keys.SetKey(S128KeyType::SDKek, sd_kek);
- if (!keys.HasKey(S128KeyType::SDSeed))
+ if (!keys.HasKey(S128KeyType::SDSeed)) {
return Loader::ResultStatus::ErrorMissingSDSeed;
+ }
const auto sd_seed = keys.GetKey(S128KeyType::SDSeed);
- if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)))
+ if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save))) {
return Loader::ResultStatus::ErrorMissingSDSaveKeySource;
- if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA)))
+ }
+ if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA))) {
return Loader::ResultStatus::ErrorMissingSDNCAKeySource;
+ }
std::array<Key256, 2> sd_key_sources{
keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)),
@@ -334,8 +411,9 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
// Combine sources and seed
for (auto& source : sd_key_sources) {
- for (std::size_t i = 0; i < source.size(); ++i)
+ for (std::size_t i = 0; i < source.size(); ++i) {
source[i] ^= sd_seed[i & 0xF];
+ }
}
AESCipher<Key128> cipher(sd_kek, Mode::ECB);
@@ -353,9 +431,10 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
return Loader::ResultStatus::Success;
}
-std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save) {
- if (!ticket_save.IsOpen())
+std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save) {
+ if (!ticket_save.IsOpen()) {
return {};
+ }
std::vector<u8> buffer(ticket_save.GetSize());
if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
@@ -415,7 +494,7 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
offset = i + 1;
break;
} else if (data[i] != 0x0) {
- return {};
+ return std::nullopt;
}
}
@@ -425,16 +504,18 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
const RSAKeyPair<2048>& key) {
const auto issuer = ticket.GetData().issuer;
- if (IsAllZeroArray(issuer))
- return {};
+ if (IsAllZeroArray(issuer)) {
+ return std::nullopt;
+ }
if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') {
LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority.");
}
Key128 rights_id = ticket.GetData().rights_id;
- if (rights_id == Key128{})
- return {};
+ if (rights_id == Key128{}) {
+ return std::nullopt;
+ }
if (!std::any_of(ticket.GetData().title_key_common_pad.begin(),
ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) {
@@ -466,15 +547,17 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
std::array<u8, 0xDF> m_2;
std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
- if (m_0 != 0)
- return {};
+ if (m_0 != 0) {
+ return std::nullopt;
+ }
m_1 = m_1 ^ MGF1<0x20>(m_2);
m_2 = m_2 ^ MGF1<0xDF>(m_1);
const auto offset = FindTicketOffset(m_2);
- if (!offset)
- return {};
+ if (!offset) {
+ return std::nullopt;
+ }
ASSERT(*offset > 0);
Key128 key_temp{};
@@ -485,8 +568,8 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
KeyManager::KeyManager() {
// Initialize keys
- const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
- const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
+ const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
+ const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
if (Settings::values.use_dev_keys) {
dev_mode = true;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);
@@ -504,34 +587,39 @@ KeyManager::KeyManager() {
}
static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
- if (base.size() < begin + length)
+ if (base.size() < begin + length) {
return false;
+ }
return std::all_of(base.begin() + begin, base.begin() + begin + length,
[](u8 c) { return std::isxdigit(c); });
}
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
std::ifstream file;
- OpenFStream(file, filename, std::ios_base::in);
- if (!file.is_open())
+ Common::FS::OpenFStream(file, filename, std::ios_base::in);
+ if (!file.is_open()) {
return;
+ }
std::string line;
while (std::getline(file, line)) {
std::vector<std::string> out;
std::stringstream stream(line);
std::string item;
- while (std::getline(stream, item, '='))
+ while (std::getline(stream, item, '=')) {
out.push_back(std::move(item));
+ }
- if (out.size() != 2)
+ if (out.size() != 2) {
continue;
+ }
out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
- if (out[0].compare(0, 1, "#") == 0)
+ if (out[0].compare(0, 1, "#") == 0) {
continue;
+ }
if (is_title_keys) {
auto rights_id_raw = Common::HexStringToArray<16>(out[0]);
@@ -541,24 +629,26 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
} else {
out[0] = Common::ToLower(out[0]);
- if (s128_file_id.find(out[0]) != s128_file_id.end()) {
- const auto index = s128_file_id.at(out[0]);
- Key128 key = Common::HexStringToArray<16>(out[1]);
+ if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
+ const auto& index = iter128->second;
+ const Key128 key = Common::HexStringToArray<16>(out[1]);
s128_keys[{index.type, index.field1, index.field2}] = key;
- } else if (s256_file_id.find(out[0]) != s256_file_id.end()) {
- const auto index = s256_file_id.at(out[0]);
- Key256 key = Common::HexStringToArray<32>(out[1]);
+ } else if (const auto iter256 = Find256ByName(out[0]); iter256 != s256_file_id.end()) {
+ const auto& index = iter256->second;
+ const Key256 key = Common::HexStringToArray<32>(out[1]);
s256_keys[{index.type, index.field1, index.field2}] = key;
} else if (out[0].compare(0, 8, "keyblob_") == 0 &&
out[0].compare(0, 9, "keyblob_k") != 0) {
- if (!ValidCryptoRevisionString(out[0], 8, 2))
+ if (!ValidCryptoRevisionString(out[0], 8, 2)) {
continue;
+ }
const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16);
keyblobs[index] = Common::HexStringToArray<0x90>(out[1]);
} else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) {
- if (!ValidCryptoRevisionString(out[0], 18, 2))
+ if (!ValidCryptoRevisionString(out[0], 18, 2)) {
continue;
+ }
const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16);
encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
@@ -566,8 +656,9 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
eticket_extended_kek = Common::HexStringToArray<576>(out[1]);
} else {
for (const auto& kv : KEYS_VARIABLE_LENGTH) {
- if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2))
+ if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) {
continue;
+ }
if (out[0].compare(0, kv.second.size(), kv.second) == 0) {
const auto index =
std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16);
@@ -602,10 +693,11 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title) {
- if (FileUtil::Exists(dir1 + DIR_SEP + filename))
+ if (Common::FS::Exists(dir1 + DIR_SEP + filename)) {
LoadFromFile(dir1 + DIR_SEP + filename, title);
- else if (FileUtil::Exists(dir2 + DIR_SEP + filename))
+ } else if (Common::FS::Exists(dir2 + DIR_SEP + filename)) {
LoadFromFile(dir2 + DIR_SEP + filename, title);
+ }
}
bool KeyManager::BaseDeriveNecessary() const {
@@ -613,8 +705,9 @@ bool KeyManager::BaseDeriveNecessary() const {
return !HasKey(key_type, index1, index2);
};
- if (check_key_existence(S256KeyType::Header))
+ if (check_key_existence(S256KeyType::Header)) {
return true;
+ }
for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) {
if (check_key_existence(S128KeyType::Master, i) ||
@@ -639,14 +732,16 @@ bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) const {
}
Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const {
- if (!HasKey(id, field1, field2))
+ if (!HasKey(id, field1, field2)) {
return {};
+ }
return s128_keys.at({id, field1, field2});
}
Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
- if (!HasKey(id, field1, field2))
+ if (!HasKey(id, field1, field2)) {
return {};
+ }
return s256_keys.at({id, field1, field2});
}
@@ -668,7 +763,7 @@ Key256 KeyManager::GetBISKey(u8 partition_id) const {
template <size_t Size>
void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
const std::array<u8, Size>& key) {
- const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
+ const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
std::string filename = "title.keys_autogenerated";
if (category == KeyCategory::Standard) {
filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
@@ -677,9 +772,9 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
}
const auto path = yuzu_keys_dir + DIR_SEP + filename;
- const auto add_info_text = !FileUtil::Exists(path);
- FileUtil::CreateFullPath(path);
- FileUtil::IOFile file{path, "a"};
+ const auto add_info_text = !Common::FS::Exists(path);
+ Common::FS::CreateFullPath(path);
+ Common::FS::IOFile file{path, "a"};
if (!file.IsOpen()) {
return;
}
@@ -712,8 +807,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
}
const auto iter2 = std::find_if(
- s128_file_id.begin(), s128_file_id.end(),
- [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
+ s128_file_id.begin(), s128_file_id.end(), [&id, &field1, &field2](const auto& elem) {
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
std::tie(id, field1, field2);
});
@@ -723,9 +817,11 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
// Variable cases
if (id == S128KeyType::KeyArea) {
- static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}",
- "key_area_key_ocean_{:02X}",
- "key_area_key_system_{:02X}"};
+ static constexpr std::array<const char*, 3> kak_names = {
+ "key_area_key_application_{:02X}",
+ "key_area_key_ocean_{:02X}",
+ "key_area_key_system_{:02X}",
+ };
WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key);
} else if (id == S128KeyType::Master) {
WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
@@ -751,8 +847,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
return;
}
const auto iter = std::find_if(
- s256_file_id.begin(), s256_file_id.end(),
- [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
+ s256_file_id.begin(), s256_file_id.end(), [&id, &field1, &field2](const auto& elem) {
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
std::tie(id, field1, field2);
});
@@ -763,29 +858,31 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
}
bool KeyManager::KeyFileExists(bool title) {
- const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
- const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
+ const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
+ const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
if (title) {
- return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "title.keys") ||
- FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "title.keys");
+ return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "title.keys") ||
+ Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "title.keys");
}
if (Settings::values.use_dev_keys) {
- return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") ||
- FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys");
+ return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") ||
+ Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys");
}
- return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") ||
- FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys");
+ return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") ||
+ Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys");
}
void KeyManager::DeriveSDSeedLazy() {
- if (HasKey(S128KeyType::SDSeed))
+ if (HasKey(S128KeyType::SDSeed)) {
return;
+ }
const auto res = DeriveSDSeed();
- if (res)
+ if (res) {
SetKey(S128KeyType::SDSeed, *res);
+ }
}
static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
@@ -797,11 +894,13 @@ static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
}
void KeyManager::DeriveBase() {
- if (!BaseDeriveNecessary())
+ if (!BaseDeriveNecessary()) {
return;
+ }
- if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC))
+ if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC)) {
return;
+ }
const auto has_bis = [this](u64 id) {
return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) &&
@@ -818,10 +917,11 @@ void KeyManager::DeriveBase() {
static_cast<u64>(BISKeyType::Tweak));
};
- if (has_bis(2) && !has_bis(3))
+ if (has_bis(2) && !has_bis(3)) {
copy_bis(2, 3);
- else if (has_bis(3) && !has_bis(2))
+ } else if (has_bis(3) && !has_bis(2)) {
copy_bis(3, 2);
+ }
std::bitset<32> revisions(0xFFFFFFFF);
for (size_t i = 0; i < revisions.size(); ++i) {
@@ -831,15 +931,17 @@ void KeyManager::DeriveBase() {
}
}
- if (!revisions.any())
+ if (!revisions.any()) {
return;
+ }
const auto sbk = GetKey(S128KeyType::SecureBoot);
const auto tsec = GetKey(S128KeyType::TSEC);
for (size_t i = 0; i < revisions.size(); ++i) {
- if (!revisions[i])
+ if (!revisions[i]) {
continue;
+ }
// Derive keyblob key
const auto key = DeriveKeyblobKey(
@@ -848,16 +950,18 @@ void KeyManager::DeriveBase() {
SetKey(S128KeyType::Keyblob, key, i);
// Derive keyblob MAC key
- if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)))
+ if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))) {
continue;
+ }
const auto mac_key = DeriveKeyblobMACKey(
key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)));
SetKey(S128KeyType::KeyblobMAC, mac_key, i);
Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key);
- if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0)
+ if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0) {
continue;
+ }
// Decrypt keyblob
if (keyblobs[i] == std::array<u8, 0x90>{}) {
@@ -881,16 +985,19 @@ void KeyManager::DeriveBase() {
revisions.set();
for (size_t i = 0; i < revisions.size(); ++i) {
- if (!HasKey(S128KeyType::Master, i))
+ if (!HasKey(S128KeyType::Master, i)) {
revisions.reset(i);
+ }
}
- if (!revisions.any())
+ if (!revisions.any()) {
return;
+ }
for (size_t i = 0; i < revisions.size(); ++i) {
- if (!revisions[i])
+ if (!revisions[i]) {
continue;
+ }
// Derive general purpose keys
DeriveGeneralPurposeKeys(i);
@@ -920,16 +1027,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
const auto es = Core::System::GetInstance().GetContentProvider().GetEntry(
0x0100000000000033, FileSys::ContentRecordType::Program);
- if (es == nullptr)
+ if (es == nullptr) {
return;
+ }
const auto exefs = es->GetExeFS();
- if (exefs == nullptr)
+ if (exefs == nullptr) {
return;
+ }
const auto main = exefs->GetFile("main");
- if (main == nullptr)
+ if (main == nullptr) {
return;
+ }
const auto bytes = main->ReadAllBytes();
@@ -939,16 +1049,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
const auto seed3 = data.GetRSAKekSeed3();
const auto mask0 = data.GetRSAKekMask0();
- if (eticket_kek != Key128{})
+ if (eticket_kek != Key128{}) {
SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek));
+ }
if (eticket_kekek != Key128{}) {
SetKey(S128KeyType::Source, eticket_kekek,
static_cast<size_t>(SourceKeyType::ETicketKekek));
}
- if (seed3 != Key128{})
+ if (seed3 != Key128{}) {
SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3));
- if (mask0 != Key128{})
+ }
+ if (mask0 != Key128{}) {
SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0));
+ }
if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} ||
mask0 == Key128{}) {
return;
@@ -974,8 +1087,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
AESCipher<Key128> es_kek(temp_kekek, Mode::ECB);
es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt);
- if (eticket_final == Key128{})
+ if (eticket_final == Key128{}) {
return;
+ }
SetKey(S128KeyType::ETicketRSAKek, eticket_final);
@@ -990,18 +1104,20 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
void KeyManager::PopulateTickets() {
const auto rsa_key = GetETicketRSAKey();
- if (rsa_key == RSAKeyPair<2048>{})
+ if (rsa_key == RSAKeyPair<2048>{}) {
return;
+ }
- if (!common_tickets.empty() && !personal_tickets.empty())
+ if (!common_tickets.empty() && !personal_tickets.empty()) {
return;
+ }
- const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
- "/system/save/80000000000000e1",
- "rb+");
- const FileUtil::IOFile save2(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
- "/system/save/80000000000000e2",
- "rb+");
+ const Common::FS::IOFile save1(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
+ "/system/save/80000000000000e1",
+ "rb+");
+ const Common::FS::IOFile save2(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
+ "/system/save/80000000000000e2",
+ "rb+");
const auto blob2 = GetTicketblob(save2);
auto res = GetTicketblob(save1);
@@ -1011,8 +1127,10 @@ void KeyManager::PopulateTickets() {
for (std::size_t i = 0; i < res.size(); ++i) {
const auto common = i < idx;
const auto pair = ParseTicket(res[i], rsa_key);
- if (!pair)
+ if (!pair) {
continue;
+ }
+
const auto& [rid, key] = *pair;
u128 rights_id;
std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1041,27 +1159,33 @@ void KeyManager::SynthesizeTickets() {
}
void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
- if (key == Key128{})
+ if (key == Key128{}) {
return;
+ }
SetKey(id, key, field1, field2);
}
void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) {
- if (key == Key256{})
+ if (key == Key256{}) {
return;
+ }
+
SetKey(id, key, field1, field2);
}
void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
- if (!BaseDeriveNecessary())
+ if (!BaseDeriveNecessary()) {
return;
+ }
- if (!data.HasBoot0())
+ if (!data.HasBoot0()) {
return;
+ }
for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) {
- if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{})
+ if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{}) {
continue;
+ }
encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i);
WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i),
encrypted_keyblobs[i]);
@@ -1083,8 +1207,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
static_cast<u64>(SourceKeyType::Keyblob), i);
}
- if (data.HasFuses())
+ if (data.HasFuses()) {
SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey());
+ }
DeriveBase();
@@ -1098,8 +1223,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
const auto masters = data.GetTZMasterKeys(latest_master);
for (size_t i = 0; i < masters.size(); ++i) {
- if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i))
+ if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i)) {
SetKey(S128KeyType::Master, masters[i], i);
+ }
}
DeriveBase();
@@ -1109,8 +1235,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
std::array<Key128, 0x20> package2_keys{};
for (size_t i = 0; i < package2_keys.size(); ++i) {
- if (HasKey(S128KeyType::Package2, i))
+ if (HasKey(S128KeyType::Package2, i)) {
package2_keys[i] = GetKey(S128KeyType::Package2, i);
+ }
}
data.DecryptPackage2(package2_keys, Package2Type::NormalMain);
@@ -1148,12 +1275,15 @@ const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const {
bool KeyManager::AddTicketCommon(Ticket raw) {
const auto rsa_key = GetETicketRSAKey();
- if (rsa_key == RSAKeyPair<2048>{})
+ if (rsa_key == RSAKeyPair<2048>{}) {
return false;
+ }
const auto pair = ParseTicket(raw, rsa_key);
- if (!pair)
+ if (!pair) {
return false;
+ }
+
const auto& [rid, key] = *pair;
u128 rights_id;
std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1164,12 +1294,15 @@ bool KeyManager::AddTicketCommon(Ticket raw) {
bool KeyManager::AddTicketPersonalized(Ticket raw) {
const auto rsa_key = GetETicketRSAKey();
- if (rsa_key == RSAKeyPair<2048>{})
+ if (rsa_key == RSAKeyPair<2048>{}) {
return false;
+ }
const auto pair = ParseTicket(raw, rsa_key);
- if (!pair)
+ if (!pair) {
return false;
+ }
+
const auto& [rid, key] = *pair;
u128 rights_id;
std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1177,58 +1310,4 @@ bool KeyManager::AddTicketPersonalized(Ticket raw) {
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
return true;
}
-
-const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
- {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
- {"eticket_rsa_kek_source",
- {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
- {"eticket_rsa_kekek_source",
- {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
- {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
- {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
- {"rsa_oaep_kek_generation_source",
- {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
- {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
- {"aes_kek_generation_source",
- {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
- {"aes_key_generation_source",
- {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
- {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
- {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
- {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
- {"key_area_key_application_source",
- {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
- static_cast<u64>(KeyAreaKeyType::Application)}},
- {"key_area_key_ocean_source",
- {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
- static_cast<u64>(KeyAreaKeyType::Ocean)}},
- {"key_area_key_system_source",
- {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
- static_cast<u64>(KeyAreaKeyType::System)}},
- {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
- {"keyblob_mac_key_source",
- {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},
- {"tsec_key", {S128KeyType::TSEC, 0, 0}},
- {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
- {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
- {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
- {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
- {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
- {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
- {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
- {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
- {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
- {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
- {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
- {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
-};
-
-const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
- {"header_key", {S256KeyType::Header, 0, 0}},
- {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
- {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
- {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
- {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
- {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
-};
} // namespace Core::Crypto
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 9269a73f2..321b75323 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -10,14 +10,13 @@
#include <string>
#include <variant>
-#include <boost/container/flat_map.hpp>
#include <fmt/format.h>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/crypto/partition_data_manager.h"
#include "core/file_sys/vfs_types.h"
-namespace FileUtil {
+namespace Common::FS {
class IOFile;
}
@@ -293,9 +292,6 @@ private:
void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
-
- static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
- static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
};
Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
@@ -308,7 +304,7 @@ std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblo
std::optional<Key128> DeriveSDSeed();
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
-std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save);
+std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save);
// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority
// (offset 0x140-0x144 is zero)
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index b31a81560..46136d04a 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -27,7 +27,7 @@
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_vector.h"
-using namespace Common;
+using Common::AsArray;
namespace Core::Crypto {
@@ -47,105 +47,123 @@ struct Package2Header {
};
static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
-const std::array<SHA256Hash, 0x10> source_hashes{
- "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
- "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
- "21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source
- "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source
- "FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source
- "C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source
- "04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source
- "FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source
- "1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source
- "6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source
- "D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source
- "2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source
- "1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source
- "8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source
- "D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3
- "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0
+// clang-format off
+constexpr std::array source_hashes{
+ AsArray("B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"), // keyblob_mac_key_source
+ AsArray("7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"), // master_key_source
+ AsArray("21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"), // package2_key_source
+ AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // aes_kek_generation_source
+ AsArray("FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"), // aes_key_generation_source
+ AsArray("C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"), // titlekek_source
+ AsArray("04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"), // key_area_key_application_source
+ AsArray("FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"), // key_area_key_ocean_source
+ AsArray("1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"), // key_area_key_system_source
+ AsArray("6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"), // sd_card_kek_source
+ AsArray("D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"), // sd_card_save_key_source
+ AsArray("2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"), // sd_card_nca_key_source
+ AsArray("1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"), // header_kek_source
+ AsArray("8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"), // header_key_source
+ AsArray("D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"), // rsa_kek_seed3
+ AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // rsa_kek_mask0
};
-
-const std::array<SHA256Hash, 0x20> keyblob_source_hashes{
- "8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00
- "2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01
- "61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02
- "8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03
- "95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04
- "3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07
-
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F
-
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17
-
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F
+// clang-format on
+
+// clang-format off
+constexpr std::array keyblob_source_hashes{
+ AsArray("8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"), // keyblob_key_source_00
+ AsArray("2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"), // keyblob_key_source_01
+ AsArray("61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"), // keyblob_key_source_02
+ AsArray("8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"), // keyblob_key_source_03
+ AsArray("95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"), // keyblob_key_source_04
+ AsArray("3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"), // keyblob_key_source_05
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_06
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_07
+
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_08
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_09
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0A
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0B
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0C
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0D
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0E
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0F
+
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_10
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_11
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_12
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_13
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_14
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_15
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_16
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_17
+
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_18
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_19
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1A
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1B
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1C
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1D
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1E
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1F
};
-
-const std::array<SHA256Hash, 0x20> master_key_hashes{
- "0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00
- "4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01
- "79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02
- "4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03
- "75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04
- "EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07
-
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F
-
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17
-
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
+// clang-format on
+
+// clang-format off
+constexpr std::array master_key_hashes{
+ AsArray("0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"), // master_key_00
+ AsArray("4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"), // master_key_01
+ AsArray("79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"), // master_key_02
+ AsArray("4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"), // master_key_03
+ AsArray("75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"), // master_key_04
+ AsArray("EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"), // master_key_05
+ AsArray("9497E6779F5D840F2BBA1DE4E95BA1D6F21EFC94717D5AE5CA37D7EC5BD37A19"), // master_key_06
+ AsArray("4EC96B8CB01B8DCE382149443430B2B6EBCB2983348AFA04A25E53609DABEDF6"), // master_key_07
+
+ AsArray("2998E2E23609BC2675FF062A2D64AF5B1B78DFF463B24119D64A1B64F01B2D51"), // master_key_08
+ AsArray("9D486A98067C44B37CF173D3BF577891EB6081FF6B4A166347D9DBBF7025076B"), // master_key_09
+ AsArray("4EC5A237A75A083A9C5F6CF615601522A7F822D06BD4BA32612C9CEBBB29BD45"), // master_key_0A
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0B
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0C
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0D
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0E
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0F
+
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_10
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_11
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_12
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_13
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_14
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_15
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_16
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_17
+
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_18
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_19
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1A
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1B
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1C
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1D
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1E
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1F
};
+// clang-format on
+
+static constexpr u8 CalculateMaxKeyblobSourceHash() {
+ const auto is_zero = [](const auto& data) {
+ // TODO: Replace with std::all_of whenever mingw decides to update their
+ // libraries to include the constexpr variant of it.
+ for (const auto element : data) {
+ if (element != 0) {
+ return false;
+ }
+ }
+ return true;
+ };
-static u8 CalculateMaxKeyblobSourceHash() {
for (s8 i = 0x1F; i >= 0; --i) {
- if (keyblob_source_hashes[i] != SHA256Hash{})
+ if (!is_zero(keyblob_source_hashes[i])) {
return static_cast<u8>(i + 1);
+ }
}
return 0;
@@ -349,8 +367,8 @@ static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header
Package2Header temp = header;
AESCipher<Key128> cipher(key, Mode::CTR);
cipher.SetIV(header.header_ctr);
- cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr,
- Op::Decrypt);
+ cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - sizeof(Package2Header::signature),
+ &temp.header_ctr, Op::Decrypt);
if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
header = temp;
return true;
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 285277ef8..9ffda2e14 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -86,7 +86,7 @@ VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
auto& keys = Core::Crypto::KeyManager::Instance();
Core::Crypto::PartitionDataManager pdm{
Core::System::GetInstance().GetFilesystem()->OpenDirectory(
- FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)};
+ Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), Mode::Read)};
keys.PopulateFromPartitionData(pdm);
switch (id) {
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index f831487dd..da01002d5 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -257,8 +257,7 @@ std::vector<NcaID> PlaceholderCache::List() const {
for (const auto& sdir : dir->GetSubdirectories()) {
for (const auto& file : sdir->GetFiles()) {
const auto name = file->GetName();
- if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' &&
- name[35] == 'a') {
+ if (name.length() == 36 && name.ends_with(".nca")) {
out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32)));
}
}
@@ -621,25 +620,25 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
bool overwrite_if_exists, const VfsCopyFunction& copy) {
- CNMTHeader header{
- nca.GetTitleId(), // Title ID
- 0, // Ignore/Default title version
- type, // Type
- {}, // Padding
- 0x10, // Default table offset
- 1, // 1 Content Entry
- 0, // No Meta Entries
- {}, // Padding
- {}, // Reserved 1
- 0, // Is committed
- 0, // Required download system version
- {}, // Reserved 2
+ const CNMTHeader header{
+ .title_id = nca.GetTitleId(),
+ .title_version = 0,
+ .type = type,
+ .reserved = {},
+ .table_offset = 0x10,
+ .number_content_entries = 1,
+ .number_meta_entries = 0,
+ .attributes = 0,
+ .reserved2 = {},
+ .is_committed = 0,
+ .required_download_system_version = 0,
+ .reserved3 = {},
};
- OptionalHeader opt_header{0, 0};
+ const OptionalHeader opt_header{0, 0};
ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}};
const auto& data = nca.GetBaseFile()->ReadBytes(0x100000);
mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0);
- memcpy(&c_rec.nca_id, &c_rec.hash, 16);
+ std::memcpy(&c_rec.nca_id, &c_rec.hash, 16);
const CNMT new_cnmt(header, opt_header, {c_rec}, {});
if (!RawInstallYuzuMeta(new_cnmt)) {
return InstallResult::ErrorMetaFailed;
@@ -728,7 +727,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
LOG_WARNING(Loader, "Overwriting existing NCA...");
VirtualDir c_dir;
{ c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); }
- c_dir->DeleteFile(FileUtil::GetFilename(path));
+ c_dir->DeleteFile(Common::FS::GetFilename(path));
}
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:
// Parsing function defines the conversion from raw file to NCA. If there are other steps
// besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
// parsing function.
- explicit RegisteredCache(VirtualDir dir,
- ContentProviderParsingFunction parsing_function =
- [](const VirtualFile& file, const NcaID& id) { return file; });
+ explicit RegisteredCache(
+ VirtualDir dir, ContentProviderParsingFunction parsing_function =
+ [](const VirtualFile& file, const NcaID& id) { return file; });
~RegisteredCache() override;
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() {
auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{},
std::vector<VirtualDir>{}, "data");
- out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_LINEAR.size()>>(
- MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat"));
- out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_SRGB.size()>>(
- MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat"));
- out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_LINEAR.size()>>(
- MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat"));
- out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_SRGB.size()>>(
- MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat"));
- out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_HIGH.size()>>(
- MiiModelData::SHAPE_HIGH, "ShapeHigh.dat"));
- out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>(
- MiiModelData::SHAPE_MID, "ShapeMid.dat"));
+ out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat"));
+ out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat"));
+ out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat"));
+ out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat"));
+ out->AddFile(MakeArrayFile(MiiModelData::SHAPE_HIGH, "ShapeHigh.dat"));
+ out->AddFile(MakeArrayFile(MiiModelData::SHAPE_MID, "ShapeMid.dat"));
return out;
}
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{
} // namespace NgWord1Data
VirtualDir NgWord1() {
- std::vector<VirtualFile> files(NgWord1Data::NUMBER_WORD_TXT_FILES);
+ std::vector<VirtualFile> files;
+ files.reserve(NgWord1Data::NUMBER_WORD_TXT_FILES);
for (std::size_t i = 0; i < files.size(); ++i) {
- files[i] = std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>(
- NgWord1Data::WORD_TXT, fmt::format("{}.txt", i));
+ files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, fmt::format("{}.txt", i)));
}
- files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>(
- NgWord1Data::WORD_TXT, "common.txt"));
- files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::VERSION_DAT.size()>>(
- NgWord1Data::VERSION_DAT, "version.dat"));
+ files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, "common.txt"));
+ files.push_back(MakeArrayFile(NgWord1Data::VERSION_DAT, "version.dat"));
- return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data");
+ return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{},
+ "data");
}
namespace NgWord2Data {
@@ -55,27 +54,22 @@ constexpr std::array<u8, 0x2C> AC_NX_DATA{
} // namespace NgWord2Data
VirtualDir NgWord2() {
- std::vector<VirtualFile> files(NgWord2Data::NUMBER_AC_NX_FILES * 3);
+ std::vector<VirtualFile> files;
+ files.reserve(NgWord2Data::NUMBER_AC_NX_FILES * 3);
for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) {
- files[3 * i] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
- NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i));
- files[3 * i + 1] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
- NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i));
- files[3 * i + 2] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
- NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i));
+ files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i)));
+ files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i)));
+ files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i)));
}
- files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
- NgWord2Data::AC_NX_DATA, "ac_common_b1_nx"));
- files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
- NgWord2Data::AC_NX_DATA, "ac_common_b2_nx"));
- files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
- NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx"));
- files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::VERSION_DAT.size()>>(
- NgWord2Data::VERSION_DAT, "version.dat"));
+ files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b1_nx"));
+ files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b2_nx"));
+ files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx"));
+ files.push_back(MakeArrayFile(NgWord2Data::VERSION_DAT, "version.dat"));
- return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data");
+ return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{},
+ "data");
}
} // 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() {
}
VirtualDir TimeZoneBinary() {
- const std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>(
+ std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>(
std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{},
"zoneinfo")};
- const std::vector<VirtualFile> root_files{
- std::make_shared<ArrayVfsFile<LOCATION_NAMES.size()>>(LOCATION_NAMES, "binaryList.txt")};
- return std::make_shared<VectorVfsDirectory>(root_files, root_dirs, "data");
+ std::vector<VirtualFile> root_files{MakeArrayFile(LOCATION_NAMES, "binaryList.txt")};
+
+ return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(root_dirs),
+ "data");
}
} // 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 {
}
VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
- const auto path = FileUtil::SanitizePath(path_);
+ const auto path = Common::FS::SanitizePath(path_);
if (root->GetFileRelative(path) != nullptr)
return VfsEntryType::File;
if (root->GetDirectoryRelative(path) != nullptr)
@@ -40,22 +40,22 @@ VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
}
VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
- const auto path = FileUtil::SanitizePath(path_);
+ const auto path = Common::FS::SanitizePath(path_);
return root->GetFileRelative(path);
}
VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
- const auto path = FileUtil::SanitizePath(path_);
+ const auto path = Common::FS::SanitizePath(path_);
return root->CreateFileRelative(path);
}
VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
- const auto old_path = FileUtil::SanitizePath(old_path_);
- const auto new_path = FileUtil::SanitizePath(new_path_);
+ const auto old_path = Common::FS::SanitizePath(old_path_);
+ const auto new_path = Common::FS::SanitizePath(new_path_);
// VfsDirectory impls are only required to implement copy across the current directory.
- if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) {
- if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path)))
+ if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) {
+ if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path)))
return nullptr;
return OpenFile(new_path, Mode::ReadWrite);
}
@@ -76,8 +76,8 @@ VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view
}
VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) {
- const auto sanitized_old_path = FileUtil::SanitizePath(old_path);
- const auto sanitized_new_path = FileUtil::SanitizePath(new_path);
+ const auto sanitized_old_path = Common::FS::SanitizePath(old_path);
+ const auto sanitized_new_path = Common::FS::SanitizePath(new_path);
// Again, non-default impls are highly encouraged to provide a more optimized version of this.
auto out = CopyFile(sanitized_old_path, sanitized_new_path);
@@ -89,26 +89,26 @@ VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view
}
bool VfsFilesystem::DeleteFile(std::string_view path_) {
- const auto path = FileUtil::SanitizePath(path_);
- auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write);
+ const auto path = Common::FS::SanitizePath(path_);
+ auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write);
if (parent == nullptr)
return false;
- return parent->DeleteFile(FileUtil::GetFilename(path));
+ return parent->DeleteFile(Common::FS::GetFilename(path));
}
VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
- const auto path = FileUtil::SanitizePath(path_);
+ const auto path = Common::FS::SanitizePath(path_);
return root->GetDirectoryRelative(path);
}
VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
- const auto path = FileUtil::SanitizePath(path_);
+ const auto path = Common::FS::SanitizePath(path_);
return root->CreateDirectoryRelative(path);
}
VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) {
- const auto old_path = FileUtil::SanitizePath(old_path_);
- const auto new_path = FileUtil::SanitizePath(new_path_);
+ const auto old_path = Common::FS::SanitizePath(old_path_);
+ const auto new_path = Common::FS::SanitizePath(new_path_);
// Non-default impls are highly encouraged to provide a more optimized version of this.
auto old_dir = OpenDirectory(old_path, Mode::Read);
@@ -139,8 +139,8 @@ VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_
}
VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) {
- const auto sanitized_old_path = FileUtil::SanitizePath(old_path);
- const auto sanitized_new_path = FileUtil::SanitizePath(new_path);
+ const auto sanitized_old_path = Common::FS::SanitizePath(old_path);
+ const auto sanitized_new_path = Common::FS::SanitizePath(new_path);
// Non-default impls are highly encouraged to provide a more optimized version of this.
auto out = CopyDirectory(sanitized_old_path, sanitized_new_path);
@@ -152,17 +152,17 @@ VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_v
}
bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
- const auto path = FileUtil::SanitizePath(path_);
- auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write);
+ const auto path = Common::FS::SanitizePath(path_);
+ auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write);
if (parent == nullptr)
return false;
- return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path));
+ return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path));
}
VfsFile::~VfsFile() = default;
std::string VfsFile::GetExtension() const {
- return std::string(FileUtil::GetExtensionFromFilename(GetName()));
+ return std::string(Common::FS::GetExtensionFromFilename(GetName()));
}
VfsDirectory::~VfsDirectory() = default;
@@ -203,7 +203,7 @@ std::string VfsFile::GetFullPath() const {
}
std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const {
- auto vec = FileUtil::SplitPathComponents(path);
+ auto vec = Common::FS::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty()) {
@@ -239,7 +239,7 @@ std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(std::string_view path) co
}
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const {
- auto vec = FileUtil::SplitPathComponents(path);
+ auto vec = Common::FS::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty()) {
@@ -301,7 +301,7 @@ std::size_t VfsDirectory::GetSize() const {
}
std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) {
- auto vec = FileUtil::SplitPathComponents(path);
+ auto vec = Common::FS::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty()) {
@@ -320,7 +320,7 @@ std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path)
}
}
- return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path));
+ return dir->CreateFileRelative(Common::FS::GetPathWithoutTop(path));
}
std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) {
@@ -332,7 +332,7 @@ std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path)
}
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) {
- auto vec = FileUtil::SplitPathComponents(path);
+ auto vec = Common::FS::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty()) {
@@ -351,7 +351,7 @@ std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_
}
}
- return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path));
+ return dir->CreateDirectoryRelative(Common::FS::GetPathWithoutTop(path));
}
std::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) {
if (zip_fread(file2.get(), buf.data(), buf.size()) != s64(buf.size()))
return nullptr;
- const auto parts = FileUtil::SplitPathComponents(stat.name);
+ const auto parts = Common::FS::SplitPathComponents(stat.name);
const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back());
std::shared_ptr<VectorVfsDirectory> dtrv = out;
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 0db0091f6..488687ba9 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -14,6 +14,8 @@
namespace FileSys {
+namespace FS = Common::FS;
+
static std::string ModeFlagsToString(Mode mode) {
std::string mode_str;
@@ -57,79 +59,82 @@ bool RealVfsFilesystem::IsWritable() const {
}
VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
- const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
- if (!FileUtil::Exists(path))
+ const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
+ if (!FS::Exists(path)) {
return VfsEntryType::None;
- if (FileUtil::IsDirectory(path))
+ }
+ if (FS::IsDirectory(path)) {
return VfsEntryType::Directory;
+ }
return VfsEntryType::File;
}
VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
- const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
- if (cache.find(path) != cache.end()) {
- auto weak = cache[path];
+ const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
+
+ if (const auto weak_iter = cache.find(path); weak_iter != cache.cend()) {
+ const auto& weak = weak_iter->second;
+
if (!weak.expired()) {
return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms));
}
}
- if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) {
- FileUtil::CreateEmptyFile(path);
+ if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) {
+ FS::CreateEmptyFile(path);
}
- auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str());
- cache[path] = backing;
+ auto backing = std::make_shared<FS::IOFile>(path, ModeFlagsToString(perms).c_str());
+ cache.insert_or_assign(path, backing);
// Cannot use make_shared as RealVfsFile constructor is private
return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms));
}
VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
- const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
- const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash);
- if (!FileUtil::Exists(path)) {
- FileUtil::CreateFullPath(path_fwd);
- if (!FileUtil::CreateEmptyFile(path))
+ const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
+ const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash);
+ if (!FS::Exists(path)) {
+ FS::CreateFullPath(path_fwd);
+ if (!FS::CreateEmptyFile(path)) {
return nullptr;
+ }
}
return OpenFile(path, perms);
}
VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
- const auto old_path =
- FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
- const auto new_path =
- FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
+ const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
+ const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
- if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
- FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path))
+ if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
+ !FS::Copy(old_path, new_path)) {
return nullptr;
+ }
return OpenFile(new_path, Mode::ReadWrite);
}
VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
- const auto old_path =
- FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
- const auto new_path =
- FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
+ const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
+ const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
+ const auto cached_file_iter = cache.find(old_path);
- if (cache.find(old_path) != cache.end()) {
- auto file = cache[old_path].lock();
+ if (cached_file_iter != cache.cend()) {
+ auto file = cached_file_iter->second.lock();
- if (!cache[old_path].expired()) {
+ if (!cached_file_iter->second.expired()) {
file->Close();
}
- if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
- FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) {
+ if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
+ !FS::Rename(old_path, new_path)) {
return nullptr;
}
cache.erase(old_path);
file->Open(new_path, "r+b");
- cache[new_path] = file;
+ cache.insert_or_assign(new_path, std::move(file));
} else {
UNREACHABLE();
return nullptr;
@@ -139,28 +144,33 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
}
bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
- const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
- if (cache.find(path) != cache.end()) {
- if (!cache[path].expired())
- cache[path].lock()->Close();
+ const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
+ const auto cached_iter = cache.find(path);
+
+ if (cached_iter != cache.cend()) {
+ if (!cached_iter->second.expired()) {
+ cached_iter->second.lock()->Close();
+ }
cache.erase(path);
}
- return FileUtil::Delete(path);
+
+ return FS::Delete(path);
}
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
- const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
+ const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
// Cannot use make_shared as RealVfsDirectory constructor is private
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
}
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
- const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
- const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash);
- if (!FileUtil::Exists(path)) {
- FileUtil::CreateFullPath(path_fwd);
- if (!FileUtil::CreateDir(path))
+ const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
+ const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash);
+ if (!FS::Exists(path)) {
+ FS::CreateFullPath(path_fwd);
+ if (!FS::CreateDir(path)) {
return nullptr;
+ }
}
// Cannot use make_shared as RealVfsDirectory constructor is private
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
@@ -168,67 +178,75 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
std::string_view new_path_) {
- const auto old_path =
- FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
- const auto new_path =
- FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
- if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
- !FileUtil::IsDirectory(old_path))
+ const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
+ const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
+ if (!FS::Exists(old_path) || FS::Exists(new_path) || !FS::IsDirectory(old_path)) {
return nullptr;
- FileUtil::CopyDir(old_path, new_path);
+ }
+ FS::CopyDir(old_path, new_path);
return OpenDirectory(new_path, Mode::ReadWrite);
}
VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
std::string_view new_path_) {
- const auto old_path =
- FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
- const auto new_path =
- FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
- if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
- FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
+ const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
+ const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
+
+ if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
+ !FS::Rename(old_path, new_path)) {
return nullptr;
+ }
for (auto& kv : cache) {
- // Path in cache starts with old_path
- if (kv.first.rfind(old_path, 0) == 0) {
- const auto file_old_path =
- FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault);
- const auto file_new_path =
- FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
- FileUtil::DirectorySeparator::PlatformDefault);
- auto cached = cache[file_old_path];
- if (!cached.expired()) {
- auto file = cached.lock();
- file->Open(file_new_path, "r+b");
- cache.erase(file_old_path);
- cache[file_new_path] = file;
- }
+ // If the path in the cache doesn't start with old_path, then bail on this file.
+ if (kv.first.rfind(old_path, 0) != 0) {
+ continue;
+ }
+
+ const auto file_old_path =
+ FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault);
+ auto file_new_path = FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
+ FS::DirectorySeparator::PlatformDefault);
+ const auto& cached = cache[file_old_path];
+
+ if (cached.expired()) {
+ continue;
}
+
+ auto file = cached.lock();
+ file->Open(file_new_path, "r+b");
+ cache.erase(file_old_path);
+ cache.insert_or_assign(std::move(file_new_path), std::move(file));
}
return OpenDirectory(new_path, Mode::ReadWrite);
}
bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
- const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
+ const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
+
for (auto& kv : cache) {
- // Path in cache starts with old_path
- if (kv.first.rfind(path, 0) == 0) {
- if (!cache[kv.first].expired())
- cache[kv.first].lock()->Close();
- cache.erase(kv.first);
+ // If the path in the cache doesn't start with path, then bail on this file.
+ if (kv.first.rfind(path, 0) != 0) {
+ continue;
+ }
+
+ const auto& entry = cache[kv.first];
+ if (!entry.expired()) {
+ entry.lock()->Close();
}
+
+ cache.erase(kv.first);
}
- return FileUtil::DeleteDirRecursively(path);
+
+ return FS::DeleteDirRecursively(path);
}
-RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_,
+RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FS::IOFile> backing_,
const std::string& path_, Mode perms_)
- : base(base_), backing(std::move(backing_)), path(path_),
- parent_path(FileUtil::GetParentPath(path_)),
- path_components(FileUtil::SplitPathComponents(path_)),
- parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
+ : base(base_), backing(std::move(backing_)), path(path_), parent_path(FS::GetParentPath(path_)),
+ path_components(FS::SplitPathComponents(path_)),
+ parent_components(FS::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {}
RealVfsFile::~RealVfsFile() = default;
@@ -258,14 +276,16 @@ bool RealVfsFile::IsReadable() const {
}
std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
- if (!backing->Seek(offset, SEEK_SET))
+ if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) {
return 0;
+ }
return backing->ReadBytes(data, length);
}
std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
- if (!backing->Seek(offset, SEEK_SET))
+ if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) {
return 0;
+ }
return backing->WriteBytes(data, length);
}
@@ -282,16 +302,18 @@ bool RealVfsFile::Close() {
template <>
std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const {
- if (perms == Mode::Append)
+ if (perms == Mode::Append) {
return {};
+ }
std::vector<VirtualFile> out;
- FileUtil::ForeachDirectoryEntry(
+ FS::ForeachDirectoryEntry(
nullptr, path,
[&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
const std::string full_path = directory + DIR_SEP + filename;
- if (!FileUtil::IsDirectory(full_path))
+ if (!FS::IsDirectory(full_path)) {
out.emplace_back(base.OpenFile(full_path, perms));
+ }
return true;
});
@@ -300,16 +322,18 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>(
template <>
std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const {
- if (perms == Mode::Append)
+ if (perms == Mode::Append) {
return {};
+ }
std::vector<VirtualDir> out;
- FileUtil::ForeachDirectoryEntry(
+ FS::ForeachDirectoryEntry(
nullptr, path,
[&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
const std::string full_path = directory + DIR_SEP + filename;
- if (FileUtil::IsDirectory(full_path))
+ if (FS::IsDirectory(full_path)) {
out.emplace_back(base.OpenDirectory(full_path, perms));
+ }
return true;
});
@@ -317,29 +341,30 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi
}
RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
- : base(base_), path(FileUtil::RemoveTrailingSlash(path_)),
- parent_path(FileUtil::GetParentPath(path)),
- path_components(FileUtil::SplitPathComponents(path)),
- parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
+ : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)),
+ path_components(FS::SplitPathComponents(path)),
+ parent_components(FS::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {
- if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) {
- FileUtil::CreateDir(path);
+ if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) {
+ FS::CreateDir(path);
}
}
RealVfsDirectory::~RealVfsDirectory() = default;
std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
- const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
- if (!FileUtil::Exists(full_path) || FileUtil::IsDirectory(full_path))
+ const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
+ if (!FS::Exists(full_path) || FS::IsDirectory(full_path)) {
return nullptr;
+ }
return base.OpenFile(full_path, perms);
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const {
- const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
- if (!FileUtil::Exists(full_path) || !FileUtil::IsDirectory(full_path))
+ const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
+ if (!FS::Exists(full_path) || !FS::IsDirectory(full_path)) {
return nullptr;
+ }
return base.OpenDirectory(full_path, perms);
}
@@ -352,17 +377,17 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view
}
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) {
- const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
+ const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
return base.CreateFile(full_path, perms);
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) {
- const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
+ const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
return base.CreateDirectory(full_path, perms);
}
bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
- auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name));
+ const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(name));
return base.DeleteDirectory(full_path);
}
@@ -387,8 +412,9 @@ std::string RealVfsDirectory::GetName() const {
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
- if (path_components.size() <= 1)
+ if (path_components.size() <= 1) {
return nullptr;
+ }
return base.OpenDirectory(parent_path, perms);
}
@@ -425,16 +451,17 @@ std::string RealVfsDirectory::GetFullPath() const {
}
std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const {
- if (perms == Mode::Append)
+ if (perms == Mode::Append) {
return {};
+ }
std::map<std::string, VfsEntryType, std::less<>> out;
- FileUtil::ForeachDirectoryEntry(
+ FS::ForeachDirectoryEntry(
nullptr, path,
[&out](u64* entries_out, const std::string& directory, const std::string& filename) {
const std::string full_path = directory + DIR_SEP + filename;
- out.emplace(filename, FileUtil::IsDirectory(full_path) ? VfsEntryType::Directory
- : VfsEntryType::File);
+ out.emplace(filename,
+ FS::IsDirectory(full_path) ? VfsEntryType::Directory : VfsEntryType::File);
return true;
});
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 @@
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs.h"
-namespace FileUtil {
+namespace Common::FS {
class IOFile;
}
@@ -36,7 +36,7 @@ public:
bool DeleteDirectory(std::string_view path) override;
private:
- boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache;
+ boost::container::flat_map<std::string, std::weak_ptr<Common::FS::IOFile>> cache;
};
// An implmentation of VfsFile that represents a file on the user's computer.
@@ -58,13 +58,13 @@ public:
bool Rename(std::string_view name) override;
private:
- RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
+ RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<Common::FS::IOFile> backing,
const std::string& path, Mode perms = Mode::Read);
bool Close();
RealVfsFilesystem& base;
- std::shared_ptr<FileUtil::IOFile> backing;
+ std::shared_ptr<Common::FS::IOFile> backing;
std::string path;
std::string parent_path;
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 @@
#pragma once
+#include <array>
#include <cstring>
+#include <memory>
+#include <string>
+#include <vector>
#include "core/file_sys/vfs.h"
namespace FileSys {
@@ -13,7 +17,8 @@ namespace FileSys {
template <std::size_t size>
class ArrayVfsFile : public VfsFile {
public:
- ArrayVfsFile(std::array<u8, size> data, std::string name = "", VirtualDir parent = nullptr)
+ explicit ArrayVfsFile(const std::array<u8, size>& data, std::string name = "",
+ VirtualDir parent = nullptr)
: data(data), name(std::move(name)), parent(std::move(parent)) {}
std::string GetName() const override {
@@ -61,6 +66,12 @@ private:
VirtualDir parent;
};
+template <std::size_t Size, typename... Args>
+std::shared_ptr<ArrayVfsFile<Size>> MakeArrayFile(const std::array<u8, Size>& data,
+ Args&&... args) {
+ return std::make_shared<ArrayVfsFile<Size>>(data, std::forward<Args>(args)...);
+}
+
// An implementation of VfsFile that is backed by a vector optionally supplied upon construction
class VectorVfsFile : public VfsFile {
public:
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
}
NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
- std::string path = FileUtil::SanitizePath(file->GetFullPath());
+ std::string path = Common::FS::SanitizePath(file->GetFullPath());
static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca",
std::regex_constants::ECMAScript |
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:
class Scoped {
public:
- explicit Scoped(GraphicsContext& context_) : context(context_) {
+ [[nodiscard]] explicit Scoped(GraphicsContext& context_) : context(context_) {
context.MakeCurrent();
}
~Scoped() {
@@ -52,7 +52,7 @@ public:
/// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
/// ends
- Scoped Acquire() {
+ [[nodiscard]] Scoped Acquire() {
return Scoped{*this};
}
};
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
do {
current_value = monitor.ExclusiveRead32(current_core, address);
- if (current_value != value) {
+ if (current_value != static_cast<u32>(value)) {
return ERR_INVALID_STATE;
}
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 {
BufferDescriptorA()[buffer_index].Size()};
if (is_buffer_a) {
- ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return buffer; },
- "BufferDescriptorA invalid buffer_index {}", buffer_index);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorA().size() > buffer_index, { return buffer; },
+ "BufferDescriptorA invalid buffer_index {}", buffer_index);
buffer.resize(BufferDescriptorA()[buffer_index].Size());
memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
} else {
- ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return buffer; },
- "BufferDescriptorX invalid buffer_index {}", buffer_index);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorX().size() > buffer_index, { return buffer; },
+ "BufferDescriptorX invalid buffer_index {}", buffer_index);
buffer.resize(BufferDescriptorX()[buffer_index].Size());
memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
}
@@ -324,16 +326,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
}
if (is_buffer_b) {
- ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index &&
- BufferDescriptorB()[buffer_index].Size() >= size,
- { return 0; }, "BufferDescriptorB is invalid, index={}, size={}",
- buffer_index, size);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorB().size() > buffer_index &&
+ BufferDescriptorB()[buffer_index].Size() >= size,
+ { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size);
memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
} else {
- ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index &&
- BufferDescriptorC()[buffer_index].Size() >= size,
- { return 0; }, "BufferDescriptorC is invalid, index={}, size={}",
- buffer_index, size);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorC().size() > buffer_index &&
+ BufferDescriptorC()[buffer_index].Size() >= size,
+ { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size);
memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
}
@@ -344,12 +346,14 @@ std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
BufferDescriptorA()[buffer_index].Size()};
if (is_buffer_a) {
- ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return 0; },
- "BufferDescriptorA invalid buffer_index {}", buffer_index);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorA().size() > buffer_index, { return 0; },
+ "BufferDescriptorA invalid buffer_index {}", buffer_index);
return BufferDescriptorA()[buffer_index].Size();
} else {
- ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return 0; },
- "BufferDescriptorX invalid buffer_index {}", buffer_index);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorX().size() > buffer_index, { return 0; },
+ "BufferDescriptorX invalid buffer_index {}", buffer_index);
return BufferDescriptorX()[buffer_index].Size();
}
}
@@ -358,12 +362,14 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons
const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
BufferDescriptorB()[buffer_index].Size()};
if (is_buffer_b) {
- ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index, { return 0; },
- "BufferDescriptorB invalid buffer_index {}", buffer_index);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorB().size() > buffer_index, { return 0; },
+ "BufferDescriptorB invalid buffer_index {}", buffer_index);
return BufferDescriptorB()[buffer_index].Size();
} else {
- ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index, { return 0; },
- "BufferDescriptorC invalid buffer_index {}", buffer_index);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorC().size() > buffer_index, { return 0; },
+ "BufferDescriptorC invalid buffer_index {}", buffer_index);
return BufferDescriptorC()[buffer_index].Size();
}
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
if (const auto result{
Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())};
result.IsError()) {
- const MemoryInfo info{block_manager->FindBlock(cur_addr).GetMemoryInfo()};
const std::size_t num_pages{(addr - cur_addr) / PageSize};
ASSERT(
@@ -852,11 +851,12 @@ ResultCode PageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
return result;
}
- block_manager->UpdateLock(addr, size / PageSize,
- [](MemoryBlockManager::iterator block, MemoryPermission perm) {
- block->ShareToDevice(perm);
- },
- perm);
+ block_manager->UpdateLock(
+ addr, size / PageSize,
+ [](MemoryBlockManager::iterator block, MemoryPermission perm) {
+ block->ShareToDevice(perm);
+ },
+ perm);
return RESULT_SUCCESS;
}
@@ -874,11 +874,12 @@ ResultCode PageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
return result;
}
- block_manager->UpdateLock(addr, size / PageSize,
- [](MemoryBlockManager::iterator block, MemoryPermission perm) {
- block->UnshareToDevice(perm);
- },
- perm);
+ block_manager->UpdateLock(
+ addr, size / PageSize,
+ [](MemoryBlockManager::iterator block, MemoryPermission perm) {
+ block->UnshareToDevice(perm);
+ },
+ perm);
return RESULT_SUCCESS;
}
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 @@
#include "core/hle/kernel/memory/system_control.h"
namespace Kernel::Memory::SystemControl {
-
-u64 GenerateRandomU64ForInit() {
- static std::random_device device;
- static std::mt19937 gen(device());
- static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
- return distribution(gen);
-}
-
+namespace {
template <typename F>
u64 GenerateUniformRange(u64 min, u64 max, F f) {
- /* Handle the case where the difference is too large to represent. */
+ // Handle the case where the difference is too large to represent.
if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
return f();
}
- /* Iterate until we get a value in range. */
+ // Iterate until we get a value in range.
const u64 range_size = ((max + 1) - min);
const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
while (true) {
@@ -32,6 +25,14 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) {
}
}
+u64 GenerateRandomU64ForInit() {
+ static std::random_device device;
+ static std::mt19937 gen(device());
+ static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
+ return distribution(gen);
+}
+} // Anonymous namespace
+
u64 GenerateRandomRange(u64 min, u64 max) {
return GenerateUniformRange(min, max, GenerateRandomU64ForInit);
}
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 @@
namespace Kernel::Memory::SystemControl {
-u64 GenerateRandomU64ForInit();
-
-template <typename F>
-u64 GenerateUniformRange(u64 min, u64 max, F f);
-
u64 GenerateRandomRange(u64 min, u64 max);
} // 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() {
u32 cores_needing_context_switch{};
for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
Scheduler& sched = kernel.Scheduler(core);
- ASSERT(top_threads[core] == nullptr || top_threads[core]->GetProcessorID() == core);
+ ASSERT(top_threads[core] == nullptr ||
+ static_cast<u32>(top_threads[core]->GetProcessorID()) == core);
if (update_thread(top_threads[core], sched)) {
cores_needing_context_switch |= (1ul << core);
}
@@ -663,32 +664,26 @@ void Scheduler::Reload() {
}
void Scheduler::SwitchContextStep2() {
- Thread* previous_thread = current_thread_prev.get();
- Thread* new_thread = selected_thread.get();
-
// Load context of new thread
- Process* const previous_process =
- previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr;
-
- if (new_thread) {
- ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
+ if (selected_thread) {
+ ASSERT_MSG(selected_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
"Thread must be runnable.");
// Cancel any outstanding wakeup events for this thread
- new_thread->SetIsRunning(true);
- new_thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
- new_thread->SetWasRunning(false);
+ selected_thread->SetIsRunning(true);
+ selected_thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
+ selected_thread->SetWasRunning(false);
auto* const thread_owner_process = current_thread->GetOwnerProcess();
if (thread_owner_process != nullptr) {
system.Kernel().MakeCurrentProcess(thread_owner_process);
}
- if (!new_thread->IsHLEThread()) {
- Core::ARM_Interface& cpu_core = new_thread->ArmInterface();
- cpu_core.LoadContext(new_thread->GetContext32());
- cpu_core.LoadContext(new_thread->GetContext64());
- cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
- cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
+ if (!selected_thread->IsHLEThread()) {
+ Core::ARM_Interface& cpu_core = selected_thread->ArmInterface();
+ cpu_core.LoadContext(selected_thread->GetContext32());
+ cpu_core.LoadContext(selected_thread->GetContext64());
+ cpu_core.SetTlsAddress(selected_thread->GetTLSAddress());
+ cpu_core.SetTPIDR_EL0(selected_thread->GetTPIDR_EL0());
cpu_core.ChangeProcessorID(this->core_id);
cpu_core.ClearExclusiveState();
}
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:
class SchedulerLock {
public:
- explicit SchedulerLock(KernelCore& kernel);
+ [[nodiscard]] explicit SchedulerLock(KernelCore& kernel);
~SchedulerLock();
protected:
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) {
*/
#define CASCADE_RESULT(target, source) \
auto CONCAT2(check_result_L, __LINE__) = source; \
- if (CONCAT2(check_result_L, __LINE__).Failed()) \
+ if (CONCAT2(check_result_L, __LINE__).Failed()) { \
return CONCAT2(check_result_L, __LINE__).Code(); \
+ } \
target = std::move(*CONCAT2(check_result_L, __LINE__))
/**
@@ -351,6 +352,9 @@ ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) {
* non-success, or discarded otherwise.
*/
#define CASCADE_CODE(source) \
- auto CONCAT2(check_result_L, __LINE__) = source; \
- if (CONCAT2(check_result_L, __LINE__).IsError()) \
- return CONCAT2(check_result_L, __LINE__);
+ do { \
+ auto CONCAT2(check_result_L, __LINE__) = source; \
+ if (CONCAT2(check_result_L, __LINE__).IsError()) { \
+ return CONCAT2(check_result_L, __LINE__); \
+ } \
+ } 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};
constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
static std::string GetImagePath(Common::UUID uuid) {
- return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
+ return Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
}
@@ -318,7 +318,7 @@ protected:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- const FileUtil::IOFile image(GetImagePath(user_id), "rb");
+ const Common::FS::IOFile image(GetImagePath(user_id), "rb");
if (!image.IsOpen()) {
LOG_WARNING(Service_ACC,
"Failed to load user provided image! Falling back to built-in backup...");
@@ -340,7 +340,7 @@ protected:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- const FileUtil::IOFile image(GetImagePath(user_id), "rb");
+ const Common::FS::IOFile image(GetImagePath(user_id), "rb");
if (!image.IsOpen()) {
LOG_WARNING(Service_ACC,
@@ -405,7 +405,7 @@ protected:
ProfileData data;
std::memcpy(&data, user_data.data(), sizeof(ProfileData));
- FileUtil::IOFile image(GetImagePath(user_id), "wb");
+ Common::FS::IOFile image(GetImagePath(user_id), "wb");
if (!image.IsOpen() || !image.Resize(image_data.size()) ||
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 @@
namespace Service::Account {
+namespace FS = Common::FS;
using Common::UUID;
struct UserRaw {
@@ -318,9 +319,8 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase&
}
void ProfileManager::ParseUserSaveFile() {
- FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
- ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat",
- "rb");
+ const FS::IOFile save(
+ FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", "rb");
if (!save.IsOpen()) {
LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
@@ -366,22 +366,22 @@ void ProfileManager::WriteUserSaveFile() {
};
}
- const auto raw_path =
- FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010";
- if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
- FileUtil::Delete(raw_path);
+ const auto raw_path = FS::GetUserPath(FS::UserPath::NANDDir) + "/system/save/8000000000000010";
+ if (FS::Exists(raw_path) && !FS::IsDirectory(raw_path)) {
+ FS::Delete(raw_path);
+ }
- const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
- ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
+ const auto path =
+ FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
- if (!FileUtil::CreateFullPath(path)) {
+ if (!FS::CreateFullPath(path)) {
LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory "
"nand/system/save/8000000000000010/su/avators to mitigate this "
"issue.");
return;
}
- FileUtil::IOFile save(path, "wb");
+ FS::IOFile save(path, "wb");
if (!save.IsOpen()) {
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&
}
void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto permission = rp.PopEnum<ScreenshotPermission>();
+ LOG_DEBUG(Service_AM, "called, permission={}", permission);
+
+ screenshot_permission = permission;
IPC::ResponseBuilder rb{ctx, 2};
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:
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
+ enum class ScreenshotPermission : u32 {
+ Inherit = 0,
+ Enable = 1,
+ Disable = 2,
+ };
+
Core::System& system;
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
Kernel::EventPair launchable_event;
@@ -157,6 +163,7 @@ private:
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
bool is_auto_sleep_disabled = false;
+ ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit;
};
class 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() {
switch (request) {
case Request::Calc: {
- broker.PushNormalDataFromApplet(
- std::make_shared<IStorage>(std::move(std::vector<u8>{1})));
+ broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{1}));
broker.SignalStateChanged();
break;
}
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() {
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data)));
broker.SignalStateChanged();
- if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) {
- FileUtil::DeleteDirRecursively(temporary_dir);
+ if (!temporary_dir.empty() && Common::FS::IsDirectory(temporary_dir)) {
+ Common::FS::DeleteDirRecursively(temporary_dir);
}
}
@@ -452,10 +452,10 @@ void WebBrowser::InitializeOffline() {
};
temporary_dir =
- FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + "web_applet_" +
- WEB_SOURCE_NAMES[static_cast<u32>(source) - 1],
- FileUtil::DirectorySeparator::PlatformDefault);
- FileUtil::DeleteDirRecursively(temporary_dir);
+ Common::FS::SanitizePath(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
+ "web_applet_" + WEB_SOURCE_NAMES[static_cast<u32>(source) - 1],
+ Common::FS::DirectorySeparator::PlatformDefault);
+ Common::FS::DeleteDirRecursively(temporary_dir);
u64 title_id = 0; // 0 corresponds to current process
ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8);
@@ -492,8 +492,8 @@ void WebBrowser::InitializeOffline() {
}
filename =
- FileUtil::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename,
- FileUtil::DirectorySeparator::PlatformDefault);
+ Common::FS::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename,
+ Common::FS::DirectorySeparator::PlatformDefault);
}
void WebBrowser::ExecuteShop() {
@@ -551,7 +551,8 @@ void WebBrowser::ExecuteShop() {
}
void WebBrowser::ExecuteOffline() {
- frontend.OpenPageLocal(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
+ frontend.OpenPageLocal(
+ filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
}
} // 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:
AudioCore::StreamPtr stream;
std::string device_name;
- [[maybe_unused]] AudoutParams audio_params {};
+ [[maybe_unused]] AudoutParams audio_params{};
/// This is the event handle used to check if the audio buffer was released
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;
std::string GetBINFilePath(u64 title_id) {
return fmt::format("{}bcat/{:016X}/launchparam.bin",
- FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id);
+ Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id);
}
std::string GetZIPFilePath(u64 title_id) {
return fmt::format("{}bcat/{:016X}/data.zip",
- FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id);
+ Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id);
}
// If the error is something the user should know about (build ID mismatch, bad client version),
@@ -205,8 +205,8 @@ private:
{std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)},
};
- if (FileUtil::Exists(path)) {
- FileUtil::IOFile file{path, "rb"};
+ if (Common::FS::Exists(path)) {
+ Common::FS::IOFile file{path, "rb"};
if (file.IsOpen()) {
std::vector<u8> bytes(file.GetSize());
file.ReadBytes(bytes.data(), bytes.size());
@@ -236,8 +236,8 @@ private:
return DownloadResult::InvalidContentType;
}
- FileUtil::CreateFullPath(path);
- FileUtil::IOFile file{path, "wb"};
+ Common::FS::CreateFullPath(path);
+ Common::FS::IOFile file{path, "wb"};
if (!file.IsOpen())
return DownloadResult::GeneralFSError;
if (!file.Resize(response->body.size()))
@@ -290,7 +290,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
- FileUtil::Delete(zip_path);
+ Common::FS::Delete(zip_path);
}
HandleDownloadDisplayResult(applet_manager, res);
@@ -300,7 +300,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
progress.StartProcessingDataList();
- FileUtil::IOFile zip{zip_path, "rb"};
+ Common::FS::IOFile zip{zip_path, "rb"};
const auto size = zip.GetSize();
std::vector<u8> bytes(size);
if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
@@ -365,8 +365,7 @@ bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress)
std::thread([this, title, &progress] {
SynchronizeInternal(applet_manager, dir_getter, title, progress);
- })
- .detach();
+ }).detach();
return true;
}
@@ -377,8 +376,7 @@ bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name,
std::thread([this, title, name, &progress] {
SynchronizeInternal(applet_manager, dir_getter, title, progress, name);
- })
- .detach();
+ }).detach();
return true;
}
@@ -422,7 +420,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
- FileUtil::Delete(path);
+ Common::FS::Delete(path);
}
HandleDownloadDisplayResult(applet_manager, res);
@@ -430,7 +428,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
}
}
- FileUtil::IOFile bin{path, "rb"};
+ Common::FS::IOFile bin{path, "rb"};
const auto size = bin.GetSize();
std::vector<u8> bytes(size);
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;
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
std::string_view dir_name_) {
- std::string dir_name(FileUtil::SanitizePath(dir_name_));
+ std::string dir_name(Common::FS::SanitizePath(dir_name_));
if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
return base;
@@ -53,13 +53,13 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
}
ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
- std::string path(FileUtil::SanitizePath(path_));
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
+ std::string path(Common::FS::SanitizePath(path_));
+ auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
// dir can be nullptr if path contains subdirectories, create those prior to creating the file.
if (dir == nullptr) {
- dir = backing->CreateSubdirectory(FileUtil::GetParentPath(path));
+ dir = backing->CreateSubdirectory(Common::FS::GetParentPath(path));
}
- auto file = dir->CreateFile(FileUtil::GetFilename(path));
+ auto file = dir->CreateFile(Common::FS::GetFilename(path));
if (file == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
@@ -72,17 +72,17 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
}
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
- std::string path(FileUtil::SanitizePath(path_));
+ std::string path(Common::FS::SanitizePath(path_));
if (path.empty()) {
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
return RESULT_SUCCESS;
}
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) {
+ auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
+ if (dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND;
}
- if (!dir->DeleteFile(FileUtil::GetFilename(path))) {
+ if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
}
@@ -91,11 +91,11 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
}
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
- std::string path(FileUtil::SanitizePath(path_));
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
+ std::string path(Common::FS::SanitizePath(path_));
+ auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
+ if (dir == nullptr && Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty())
dir = backing;
- auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path));
+ auto new_dir = dir->CreateSubdirectory(Common::FS::GetFilename(path));
if (new_dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
@@ -104,9 +104,9 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_)
}
ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
- std::string path(FileUtil::SanitizePath(path_));
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) {
+ std::string path(Common::FS::SanitizePath(path_));
+ auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
+ if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
}
@@ -114,9 +114,9 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_)
}
ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
- std::string path(FileUtil::SanitizePath(path_));
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) {
+ std::string path(Common::FS::SanitizePath(path_));
+ auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
+ if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
}
@@ -124,10 +124,10 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
}
ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
- const std::string sanitized_path(FileUtil::SanitizePath(path));
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(sanitized_path));
+ const std::string sanitized_path(Common::FS::SanitizePath(path));
+ auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));
- if (!dir->CleanSubdirectoryRecursive(FileUtil::GetFilename(sanitized_path))) {
+ if (!dir->CleanSubdirectoryRecursive(Common::FS::GetFilename(sanitized_path))) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
}
@@ -137,14 +137,14 @@ ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::stri
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
const std::string& dest_path_) const {
- std::string src_path(FileUtil::SanitizePath(src_path_));
- std::string dest_path(FileUtil::SanitizePath(dest_path_));
+ std::string src_path(Common::FS::SanitizePath(src_path_));
+ std::string dest_path(Common::FS::SanitizePath(dest_path_));
auto src = backing->GetFileRelative(src_path);
- if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
+ if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
// Use more-optimized vfs implementation rename.
if (src == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
- if (!src->Rename(FileUtil::GetFilename(dest_path))) {
+ if (!src->Rename(Common::FS::GetFilename(dest_path))) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
}
@@ -162,7 +162,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
"Could not write all of the bytes but everything else has succeded.");
- if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) {
+ if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
}
@@ -172,14 +172,14 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
const std::string& dest_path_) const {
- std::string src_path(FileUtil::SanitizePath(src_path_));
- std::string dest_path(FileUtil::SanitizePath(dest_path_));
+ std::string src_path(Common::FS::SanitizePath(src_path_));
+ std::string dest_path(Common::FS::SanitizePath(dest_path_));
auto src = GetDirectoryRelativeWrapped(backing, src_path);
- if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
+ if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
// Use more-optimized vfs implementation rename.
if (src == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
- if (!src->Rename(FileUtil::GetFilename(dest_path))) {
+ if (!src->Rename(Common::FS::GetFilename(dest_path))) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
}
@@ -198,7 +198,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
FileSys::Mode mode) const {
- const std::string path(FileUtil::SanitizePath(path_));
+ const std::string path(Common::FS::SanitizePath(path_));
std::string_view npath = path;
while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
npath.remove_prefix(1);
@@ -218,7 +218,7 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::
}
ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) {
- std::string path(FileUtil::SanitizePath(path_));
+ std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, path);
if (dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
@@ -229,11 +229,11 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s
ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
const std::string& path_) const {
- std::string path(FileUtil::SanitizePath(path_));
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
+ std::string path(Common::FS::SanitizePath(path_));
+ auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
- auto filename = FileUtil::GetFilename(path);
+ auto filename = Common::FS::GetFilename(path);
// TODO(Subv): Some games use the '/' path, find out what this means.
if (filename.empty())
return MakeResult(FileSys::EntryType::Directory);
@@ -695,13 +695,13 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
sdmc_factory = nullptr;
}
- auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
+ auto nand_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir),
FileSys::Mode::ReadWrite);
- auto sd_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
+ auto sd_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir),
FileSys::Mode::ReadWrite);
- auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
+ auto load_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
FileSys::Mode::ReadWrite);
- auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
+ auto dump_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir),
FileSys::Mode::ReadWrite);
if (bis_factory == nullptr) {
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index ef67ad690..45fde8df2 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -24,6 +24,7 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr u32 BATTERY_FULL = 2;
constexpr u32 MAX_NPAD_ID = 7;
+constexpr std::size_t HANDHELD_INDEX = 8;
constexpr std::array<u32, 10> npad_id_list{
0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
};
@@ -33,19 +34,41 @@ enum class JoystickId : std::size_t {
Joystick_Right,
};
-static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) {
+Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
+ Settings::ControllerType type) {
switch (type) {
case Settings::ControllerType::ProController:
- return Controller_NPad::NPadControllerType::ProController;
- case Settings::ControllerType::DualJoycon:
- return Controller_NPad::NPadControllerType::JoyDual;
+ return NPadControllerType::ProController;
+ case Settings::ControllerType::DualJoyconDetached:
+ return NPadControllerType::JoyDual;
case Settings::ControllerType::LeftJoycon:
- return Controller_NPad::NPadControllerType::JoyLeft;
+ return NPadControllerType::JoyLeft;
case Settings::ControllerType::RightJoycon:
- return Controller_NPad::NPadControllerType::JoyRight;
+ return NPadControllerType::JoyRight;
+ case Settings::ControllerType::Handheld:
+ return NPadControllerType::Handheld;
default:
UNREACHABLE();
- return Controller_NPad::NPadControllerType::JoyDual;
+ return NPadControllerType::ProController;
+ }
+}
+
+Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
+ Controller_NPad::NPadControllerType type) {
+ switch (type) {
+ case NPadControllerType::ProController:
+ return Settings::ControllerType::ProController;
+ case NPadControllerType::JoyDual:
+ return Settings::ControllerType::DualJoyconDetached;
+ case NPadControllerType::JoyLeft:
+ return Settings::ControllerType::LeftJoycon;
+ case NPadControllerType::JoyRight:
+ return Settings::ControllerType::RightJoycon;
+ case NPadControllerType::Handheld:
+ return Settings::ControllerType::Handheld;
+ default:
+ UNREACHABLE();
+ return Settings::ControllerType::ProController;
}
}
@@ -60,9 +83,9 @@ std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
case 6:
case 7:
return npad_id;
- case 8:
+ case HANDHELD_INDEX:
case NPAD_HANDHELD:
- return 8;
+ return HANDHELD_INDEX;
case 9:
case NPAD_UNKNOWN:
return 9;
@@ -83,38 +106,48 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
case 6:
case 7:
return static_cast<u32>(index);
- case 8:
+ case HANDHELD_INDEX:
return NPAD_HANDHELD;
case 9:
return NPAD_UNKNOWN;
default:
UNIMPLEMENTED_MSG("Unknown npad index {}", index);
return 0;
- };
+ }
}
Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
Controller_NPad::~Controller_NPad() = default;
-void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
+void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
const auto controller_type = connected_controllers[controller_idx].type;
auto& controller = shared_memory_entries[controller_idx];
if (controller_type == NPadControllerType::None) {
+ styleset_changed_events[controller_idx].writable->Signal();
return;
}
controller.joy_styles.raw = 0; // Zero out
controller.device_type.raw = 0;
+ controller.properties.raw = 0;
switch (controller_type) {
case NPadControllerType::None:
UNREACHABLE();
break;
+ case NPadControllerType::ProController:
+ controller.joy_styles.pro_controller.Assign(1);
+ controller.device_type.pro_controller.Assign(1);
+ controller.properties.is_vertical.Assign(1);
+ controller.properties.use_plus.Assign(1);
+ controller.properties.use_minus.Assign(1);
+ controller.pad_assignment = NPadAssignments::Single;
+ break;
case NPadControllerType::Handheld:
controller.joy_styles.handheld.Assign(1);
controller.device_type.handheld.Assign(1);
- controller.pad_assignment = NPadAssignments::Dual;
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
+ controller.pad_assignment = NPadAssignments::Dual;
break;
case NPadControllerType::JoyDual:
controller.joy_styles.joycon_dual.Assign(1);
@@ -144,14 +177,6 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.device_type.pokeball.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
break;
- case NPadControllerType::ProController:
- controller.joy_styles.pro_controller.Assign(1);
- controller.device_type.pro_controller.Assign(1);
- controller.properties.is_vertical.Assign(1);
- controller.properties.use_plus.Assign(1);
- controller.properties.use_minus.Assign(1);
- controller.pad_assignment = NPadAssignments::Single;
- break;
}
controller.single_color_error = ColorReadError::ReadOk;
@@ -192,36 +217,25 @@ void Controller_NPad::OnInit() {
style.pokeball.Assign(1);
}
- std::transform(
- Settings::values.players.begin(), Settings::values.players.end(),
- connected_controllers.begin(), [](const Settings::PlayerInput& player) {
- return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected};
- });
-
- std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
- [](const ControllerHolder& holder) { return holder.is_connected; });
+ std::transform(Settings::values.players.begin(), Settings::values.players.end(),
+ connected_controllers.begin(), [](const Settings::PlayerInput& player) {
+ return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
+ player.connected};
+ });
// Account for handheld
- if (connected_controllers[8].is_connected)
- connected_controllers[8].type = NPadControllerType::Handheld;
+ if (connected_controllers[HANDHELD_INDEX].is_connected) {
+ connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
+ }
supported_npad_id_types.resize(npad_id_list.size());
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
npad_id_list.size() * sizeof(u32));
- // Add a default dual joycon controller if none are present.
- if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
- [](const ControllerHolder& controller) { return controller.is_connected; })) {
- supported_npad_id_types.resize(npad_id_list.size());
- std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
- npad_id_list.size() * sizeof(u32));
- AddNewController(NPadControllerType::JoyDual);
- }
-
for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
const auto& controller = connected_controllers[i];
if (controller.is_connected) {
- AddNewControllerAt(controller.type, IndexToNPad(i));
+ AddNewControllerAt(controller.type, i);
}
}
}
@@ -309,8 +323,9 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t data_len) {
- if (!IsControllerActivated())
+ if (!IsControllerActivated()) {
return;
+ }
for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
auto& npad = shared_memory_entries[i];
const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
@@ -360,13 +375,25 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
libnx_entry.connection_status.raw = 0;
+ libnx_entry.connection_status.IsConnected.Assign(1);
switch (controller_type) {
case NPadControllerType::None:
UNREACHABLE();
break;
+ case NPadControllerType::ProController:
+ main_controller.connection_status.raw = 0;
+ main_controller.connection_status.IsConnected.Assign(1);
+ main_controller.connection_status.IsWired.Assign(1);
+ main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
+ main_controller.pad.l_stick = pad_state.l_stick;
+ main_controller.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsWired.Assign(1);
+ break;
case NPadControllerType::Handheld:
handheld_entry.connection_status.raw = 0;
+ handheld_entry.connection_status.IsConnected.Assign(1);
handheld_entry.connection_status.IsWired.Assign(1);
handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
@@ -375,57 +402,52 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
handheld_entry.pad.l_stick = pad_state.l_stick;
handheld_entry.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsWired.Assign(1);
+ libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
+ libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
+ libnx_entry.connection_status.IsLeftJoyWired.Assign(1);
+ libnx_entry.connection_status.IsRightJoyWired.Assign(1);
break;
case NPadControllerType::JoyDual:
dual_entry.connection_status.raw = 0;
-
+ dual_entry.connection_status.IsConnected.Assign(1);
dual_entry.connection_status.IsLeftJoyConnected.Assign(1);
dual_entry.connection_status.IsRightJoyConnected.Assign(1);
- dual_entry.connection_status.IsConnected.Assign(1);
-
- libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
- libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
- libnx_entry.connection_status.IsConnected.Assign(1);
-
dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
dual_entry.pad.l_stick = pad_state.l_stick;
dual_entry.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
+ libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
break;
case NPadControllerType::JoyLeft:
left_entry.connection_status.raw = 0;
-
left_entry.connection_status.IsConnected.Assign(1);
+ left_entry.connection_status.IsLeftJoyConnected.Assign(1);
left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
left_entry.pad.l_stick = pad_state.l_stick;
left_entry.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
break;
case NPadControllerType::JoyRight:
right_entry.connection_status.raw = 0;
-
right_entry.connection_status.IsConnected.Assign(1);
+ right_entry.connection_status.IsRightJoyConnected.Assign(1);
right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
right_entry.pad.l_stick = pad_state.l_stick;
right_entry.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
break;
case NPadControllerType::Pokeball:
pokeball_entry.connection_status.raw = 0;
-
pokeball_entry.connection_status.IsConnected.Assign(1);
- pokeball_entry.connection_status.IsWired.Assign(1);
-
pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
pokeball_entry.pad.l_stick = pad_state.l_stick;
pokeball_entry.pad.r_stick = pad_state.r_stick;
break;
- case NPadControllerType::ProController:
- main_controller.connection_status.raw = 0;
-
- main_controller.connection_status.IsConnected.Assign(1);
- main_controller.connection_status.IsWired.Assign(1);
- main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
- main_controller.pad.l_stick = pad_state.l_stick;
- main_controller.pad.r_stick = pad_state.r_stick;
- break;
}
// LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
@@ -453,26 +475,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
supported_npad_id_types.clear();
supported_npad_id_types.resize(length / sizeof(u32));
std::memcpy(supported_npad_id_types.data(), data, length);
- for (std::size_t i = 0; i < connected_controllers.size(); i++) {
- auto& controller = connected_controllers[i];
- if (!controller.is_connected) {
- continue;
- }
- const auto requested_controller =
- i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
- : NPadControllerType::Handheld;
- if (!IsControllerSupported(requested_controller)) {
- const auto is_handheld = requested_controller == NPadControllerType::Handheld;
- if (is_handheld) {
- controller.type = NPadControllerType::None;
- controller.is_connected = false;
- AddNewController(requested_controller);
- } else {
- controller.type = requested_controller;
- InitNewlyAddedControler(i);
- }
- }
- }
}
void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
@@ -504,7 +506,7 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
const std::vector<Vibration>& vibrations) {
LOG_DEBUG(Service_HID, "(STUBBED) called");
- if (!can_controllers_vibrate) {
+ if (!Settings::values.vibration_enabled || !can_controllers_vibrate) {
return;
}
for (std::size_t i = 0; i < controller_ids.size(); i++) {
@@ -517,8 +519,6 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
}
std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
- // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
- // be signalled at least once, and signaled after a new controller is connected?
const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
return styleset_event.readable;
}
@@ -527,43 +527,43 @@ Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
return last_processed_vibration;
}
-void Controller_NPad::AddNewController(NPadControllerType controller) {
- controller = DecideBestController(controller);
- if (controller == NPadControllerType::Handheld) {
- connected_controllers[8] = {controller, true};
- InitNewlyAddedControler(8);
- return;
- }
- const auto pos =
- std::find_if(connected_controllers.begin(), connected_controllers.end() - 2,
- [](const ControllerHolder& holder) { return !holder.is_connected; });
- if (pos == connected_controllers.end() - 2) {
- LOG_ERROR(Service_HID, "Cannot connect any more controllers!");
+void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
+ UpdateControllerAt(controller, npad_index, true);
+}
+
+void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
+ bool connected) {
+ if (!connected) {
+ DisconnectNPad(IndexToNPad(npad_index));
return;
}
- const auto controller_id = std::distance(connected_controllers.begin(), pos);
- connected_controllers[controller_id] = {controller, true};
- InitNewlyAddedControler(controller_id);
-}
-void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
- controller = DecideBestController(controller);
if (controller == NPadControllerType::Handheld) {
- connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true};
- InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD));
+ Settings::values.players[HANDHELD_INDEX].controller_type =
+ MapNPadToSettingsType(controller);
+ Settings::values.players[HANDHELD_INDEX].connected = true;
+ connected_controllers[HANDHELD_INDEX] = {controller, true};
+ InitNewlyAddedController(HANDHELD_INDEX);
return;
}
- connected_controllers[NPadIdToIndex(npad_id)] = {controller, true};
- InitNewlyAddedControler(NPadIdToIndex(npad_id));
-}
-
-void Controller_NPad::ConnectNPad(u32 npad_id) {
- connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
+ Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller);
+ Settings::values.players[npad_index].connected = true;
+ connected_controllers[npad_index] = {controller, true};
+ InitNewlyAddedController(npad_index);
}
void Controller_NPad::DisconnectNPad(u32 npad_id) {
- connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
+ const auto npad_index = NPadIdToIndex(npad_id);
+ connected_controllers[npad_index].is_connected = false;
+ Settings::values.players[npad_index].connected = false;
+
+ auto& controller = shared_memory_entries[npad_index];
+ controller.joy_styles.raw = 0; // Zero out
+ controller.device_type.raw = 0;
+ controller.properties.raw = 0;
+
+ styleset_changed_events[npad_index].writable->Signal();
}
void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
@@ -599,8 +599,8 @@ bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) {
std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type);
- InitNewlyAddedControler(npad_index_1);
- InitNewlyAddedControler(npad_index_2);
+ AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1);
+ AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2);
return true;
}
@@ -614,11 +614,11 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
case 0:
return LedPattern{1, 0, 0, 0};
case 1:
- return LedPattern{0, 1, 0, 0};
+ return LedPattern{1, 1, 0, 0};
case 2:
- return LedPattern{0, 0, 1, 0};
+ return LedPattern{1, 1, 1, 0};
case 3:
- return LedPattern{0, 0, 0, 1};
+ return LedPattern{1, 1, 1, 1};
case 4:
return LedPattern{1, 0, 0, 1};
case 5:
@@ -628,9 +628,8 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
case 7:
return LedPattern{0, 1, 1, 0};
default:
- UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
return LedPattern{0, 0, 0, 0};
- };
+ }
}
void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 5d4c58a43..75ce5b731 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -118,10 +118,11 @@ public:
std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
Vibration GetLastVibration() const;
- void AddNewController(NPadControllerType controller);
- void AddNewControllerAt(NPadControllerType controller, u32 npad_id);
+ // Adds a new controller at an index.
+ void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index);
+ // Adds a new controller at an index with connection status.
+ void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
- void ConnectNPad(u32 npad_id);
void DisconnectNPad(u32 npad_id);
void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
@@ -141,6 +142,8 @@ public:
// Specifically for cheat engine and other features.
u32 GetAndResetPressState();
+ static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type);
+ static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type);
static std::size_t NPadIdToIndex(u32 npad_id);
static u32 IndexToNPad(std::size_t index);
@@ -309,7 +312,7 @@ private:
bool is_connected;
};
- void InitNewlyAddedControler(std::size_t controller_idx);
+ void InitNewlyAddedController(std::size_t controller_idx);
bool IsControllerSupported(NPadControllerType controller) const;
NPadControllerType DecideBestController(NPadControllerType priority) const;
void RequestPadStateUpdate(u32 npad_id);
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 1e95b7580..33416b5dd 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -38,11 +38,9 @@
namespace Service::HID {
// Updating period for each HID device.
-// TODO(ogniK): Find actual polling rate of hid
-constexpr auto pad_update_ns = std::chrono::nanoseconds{1000000000 / 66};
-[[maybe_unused]] constexpr auto accelerometer_update_ns =
- std::chrono::nanoseconds{1000000000 / 100};
-[[maybe_unused]] constexpr auto gyroscope_update_ticks = std::chrono::nanoseconds{1000000000 / 100};
+// HID is polled every 15ms, this value was derived from
+// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
+constexpr auto pad_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.6Hz)
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource(Core::System& system)
@@ -845,8 +843,7 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto can_vibrate{rp.Pop<bool>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetVibrationEnabled(can_vibrate);
+ Settings::values.vibration_enabled = can_vibrate;
LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
@@ -859,8 +856,7 @@ void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push(
- applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled());
+ rb.Push(Settings::values.vibration_enabled);
}
void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 1b52511a5..0240d6643 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -21,8 +21,9 @@ namespace Service::Nvidia::Devices {
/// implement the ioctl interface.
class nvdevice {
public:
- explicit nvdevice(Core::System& system) : system{system} {};
+ explicit nvdevice(Core::System& system) : system{system} {}
virtual ~nvdevice() = default;
+
union Ioctl {
u32_le raw;
BitField<0, 8, u32> cmd;
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index ff85cbba6..1ebe949c0 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -86,11 +86,13 @@ public:
[[nodiscard]] s64 GetNextTicks() const;
- [[nodiscard]] std::unique_lock<std::mutex> Lock() const { return std::unique_lock{*guard}; }
+ [[nodiscard]] std::unique_lock<std::mutex> Lock() const {
+ return std::unique_lock{*guard};
+ }
- private :
- /// Finds the display identified by the specified ID.
- [[nodiscard]] VI::Display* FindDisplay(u64 display_id);
+private:
+ /// Finds the display identified by the specified ID.
+ [[nodiscard]] VI::Display* FindDisplay(u64 display_id);
/// Finds the display identified by the specified ID.
[[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:
ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name);
ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name);
- template <Common::IsBaseOf<Kernel::SessionRequestHandler> T>
+ template <Common::DerivedFrom<Kernel::SessionRequestHandler> T>
std::shared_ptr<T> GetService(const std::string& service_name) const {
auto service = registered_services.find(service_name);
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::
std::string location_name;
const auto timezone_setting = Settings::GetTimeZoneString();
- if (timezone_setting == "auto") {
+ if (timezone_setting == "auto" || timezone_setting == "default") {
location_name = Common::TimeZone::GetDefaultTimeZone();
- } else if (timezone_setting == "default") {
- location_name = location_name;
} else {
location_name = timezone_setting;
}
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index b8f8f1448..9bc3a8840 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -25,7 +25,7 @@ namespace Loader {
namespace {
-template <Common::IsBaseOf<AppLoader> T>
+template <Common::DerivedFrom<AppLoader> T>
std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
const auto file_type = T::IdentifyType(file);
if (file_type != FileType::Error) {
@@ -67,7 +67,7 @@ FileType GuessFromFilename(const std::string& name) {
return FileType::NCA;
const std::string extension =
- Common::ToLower(std::string(FileUtil::GetExtensionFromFilename(name)));
+ Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
if (extension == "elf")
return FileType::ELF;
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index eeebdf02e..e503118dd 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -42,7 +42,7 @@ u64 StandardVmCallbacks::HidKeysDown() {
if (applet_resource == nullptr) {
LOG_WARNING(CheatEngine,
"Attempted to read input state, but applet resource is not initialized!");
- return false;
+ return 0;
}
const auto press_state =
@@ -199,17 +199,29 @@ void CheatEngine::Initialize() {
metadata.title_id = system.CurrentProcess()->GetTitleID();
const auto& page_table = system.CurrentProcess()->PageTable();
- metadata.heap_extents = {page_table.GetHeapRegionStart(), page_table.GetHeapRegionSize()};
- metadata.address_space_extents = {page_table.GetAddressSpaceStart(),
- page_table.GetAddressSpaceSize()};
- metadata.alias_extents = {page_table.GetAliasCodeRegionStart(),
- page_table.GetAliasCodeRegionSize()};
+ metadata.heap_extents = {
+ .base = page_table.GetHeapRegionStart(),
+ .size = page_table.GetHeapRegionSize(),
+ };
+
+ metadata.address_space_extents = {
+ .base = page_table.GetAddressSpaceStart(),
+ .size = page_table.GetAddressSpaceSize(),
+ };
+
+ metadata.alias_extents = {
+ .base = page_table.GetAliasCodeRegionStart(),
+ .size = page_table.GetAliasCodeRegionSize(),
+ };
is_pending_reload.exchange(true);
}
void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
- metadata.main_nso_extents = {main_region_begin, main_region_size};
+ metadata.main_nso_extents = {
+ .base = main_region_begin,
+ .size = main_region_size,
+ };
}
void 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() {
std::ostringstream stream;
std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index,
std::ostream_iterator<double>(stream, "\n"));
- const std::string& path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
+ const std::string& path = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
// %F Date format expanded is "%Y-%m-%d"
const std::string filename =
fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id);
- FileUtil::IOFile file(filename, "w");
+ Common::FS::IOFile file(filename, "w");
file.WriteString(stream.str());
}
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 @@
namespace {
std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) {
- return fmt::format("{}{}/{:016X}_{}.json", FileUtil::GetUserPath(FileUtil::UserPath::LogDir),
- type, title_id, timestamp);
+ return fmt::format("{}{}/{:016X}_{}.json",
+ Common::FS::GetUserPath(Common::FS::UserPath::LogDir), type, title_id,
+ timestamp);
}
std::string GetTimestamp() {
@@ -40,13 +41,13 @@ std::string GetTimestamp() {
using namespace nlohmann;
void SaveToFile(json json, const std::string& filename) {
- if (!FileUtil::CreateFullPath(filename)) {
+ if (!Common::FS::CreateFullPath(filename)) {
LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename);
return;
}
std::ofstream file(
- FileUtil::SanitizePath(filename, FileUtil::DirectorySeparator::PlatformDefault));
+ Common::FS::SanitizePath(filename, Common::FS::DirectorySeparator::PlatformDefault));
file << std::setw(4) << json << std::endl;
}
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 416b2d866..28d3f9099 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -13,56 +13,6 @@
namespace Settings {
-namespace NativeButton {
-const std::array<const char*, NumButtons> mapping = {{
- "button_a",
- "button_b",
- "button_x",
- "button_y",
- "button_lstick",
- "button_rstick",
- "button_l",
- "button_r",
- "button_zl",
- "button_zr",
- "button_plus",
- "button_minus",
- "button_dleft",
- "button_dup",
- "button_dright",
- "button_ddown",
- "button_lstick_left",
- "button_lstick_up",
- "button_lstick_right",
- "button_lstick_down",
- "button_rstick_left",
- "button_rstick_up",
- "button_rstick_right",
- "button_rstick_down",
- "button_sl",
- "button_sr",
- "button_home",
- "button_screenshot",
-}};
-}
-
-namespace NativeAnalog {
-const std::array<const char*, NumAnalogs> mapping = {{
- "lstick",
- "rstick",
-}};
-}
-
-namespace NativeMouseButton {
-const std::array<const char*, NumMouseButtons> mapping = {{
- "left",
- "right",
- "middle",
- "forward",
- "back",
-}};
-}
-
Values values = {};
bool configuring_global = true;
@@ -121,8 +71,8 @@ void LogSettings() {
log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
log_setting("Audio_OutputDevice", values.audio_device_id);
log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd);
- log_setting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
- log_setting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
+ log_setting("DataStorage_NandDir", Common::FS::GetUserPath(Common::FS::UserPath::NANDDir));
+ log_setting("DataStorage_SdmcDir", Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir));
log_setting("Debugging_UseGdbstub", values.use_gdbstub);
log_setting("Debugging_GdbstubPort", values.gdbstub_port);
log_setting("Debugging_ProgramArgs", values.program_args);
diff --git a/src/core/settings.h b/src/core/settings.h
index bb145f193..732c6a894 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -12,340 +12,10 @@
#include <string>
#include <vector>
#include "common/common_types.h"
+#include "input_common/settings.h"
namespace Settings {
-namespace NativeButton {
-enum Values {
- A,
- B,
- X,
- Y,
- LStick,
- RStick,
- L,
- R,
- ZL,
- ZR,
- Plus,
- Minus,
-
- DLeft,
- DUp,
- DRight,
- DDown,
-
- LStick_Left,
- LStick_Up,
- LStick_Right,
- LStick_Down,
-
- RStick_Left,
- RStick_Up,
- RStick_Right,
- RStick_Down,
-
- SL,
- SR,
-
- Home,
- Screenshot,
-
- NumButtons,
-};
-
-constexpr int BUTTON_HID_BEGIN = A;
-constexpr int BUTTON_NS_BEGIN = Home;
-
-constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
-constexpr int BUTTON_NS_END = NumButtons;
-
-constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
-constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
-
-extern const std::array<const char*, NumButtons> mapping;
-
-} // namespace NativeButton
-
-namespace NativeAnalog {
-enum Values {
- LStick,
- RStick,
-
- NumAnalogs,
-};
-
-constexpr int STICK_HID_BEGIN = LStick;
-constexpr int STICK_HID_END = NumAnalogs;
-constexpr int NUM_STICKS_HID = NumAnalogs;
-
-extern const std::array<const char*, NumAnalogs> mapping;
-} // namespace NativeAnalog
-
-namespace NativeMouseButton {
-enum Values {
- Left,
- Right,
- Middle,
- Forward,
- Back,
-
- NumMouseButtons,
-};
-
-constexpr int MOUSE_HID_BEGIN = Left;
-constexpr int MOUSE_HID_END = NumMouseButtons;
-constexpr int NUM_MOUSE_HID = NumMouseButtons;
-
-extern const std::array<const char*, NumMouseButtons> mapping;
-} // namespace NativeMouseButton
-
-namespace NativeKeyboard {
-enum Keys {
- None,
- Error,
-
- A = 4,
- B,
- C,
- D,
- E,
- F,
- G,
- H,
- I,
- J,
- K,
- L,
- M,
- N,
- O,
- P,
- Q,
- R,
- S,
- T,
- U,
- V,
- W,
- X,
- Y,
- Z,
- N1,
- N2,
- N3,
- N4,
- N5,
- N6,
- N7,
- N8,
- N9,
- N0,
- Enter,
- Escape,
- Backspace,
- Tab,
- Space,
- Minus,
- Equal,
- LeftBrace,
- RightBrace,
- Backslash,
- Tilde,
- Semicolon,
- Apostrophe,
- Grave,
- Comma,
- Dot,
- Slash,
- CapsLockKey,
-
- F1,
- F2,
- F3,
- F4,
- F5,
- F6,
- F7,
- F8,
- F9,
- F10,
- F11,
- F12,
-
- SystemRequest,
- ScrollLockKey,
- Pause,
- Insert,
- Home,
- PageUp,
- Delete,
- End,
- PageDown,
- Right,
- Left,
- Down,
- Up,
-
- NumLockKey,
- KPSlash,
- KPAsterisk,
- KPMinus,
- KPPlus,
- KPEnter,
- KP1,
- KP2,
- KP3,
- KP4,
- KP5,
- KP6,
- KP7,
- KP8,
- KP9,
- KP0,
- KPDot,
-
- Key102,
- Compose,
- Power,
- KPEqual,
-
- F13,
- F14,
- F15,
- F16,
- F17,
- F18,
- F19,
- F20,
- F21,
- F22,
- F23,
- F24,
-
- Open,
- Help,
- Properties,
- Front,
- Stop,
- Repeat,
- Undo,
- Cut,
- Copy,
- Paste,
- Find,
- Mute,
- VolumeUp,
- VolumeDown,
- CapsLockActive,
- NumLockActive,
- ScrollLockActive,
- KPComma,
-
- KPLeftParenthesis,
- KPRightParenthesis,
-
- LeftControlKey = 0xE0,
- LeftShiftKey,
- LeftAltKey,
- LeftMetaKey,
- RightControlKey,
- RightShiftKey,
- RightAltKey,
- RightMetaKey,
-
- MediaPlayPause,
- MediaStopCD,
- MediaPrevious,
- MediaNext,
- MediaEject,
- MediaVolumeUp,
- MediaVolumeDown,
- MediaMute,
- MediaWebsite,
- MediaBack,
- MediaForward,
- MediaStop,
- MediaFind,
- MediaScrollUp,
- MediaScrollDown,
- MediaEdit,
- MediaSleep,
- MediaCoffee,
- MediaRefresh,
- MediaCalculator,
-
- NumKeyboardKeys,
-};
-
-static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
-
-enum Modifiers {
- LeftControl,
- LeftShift,
- LeftAlt,
- LeftMeta,
- RightControl,
- RightShift,
- RightAlt,
- RightMeta,
- CapsLock,
- ScrollLock,
- NumLock,
-
- NumKeyboardMods,
-};
-
-constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
-constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
-constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
-
-constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
-constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
-constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
-
-} // namespace NativeKeyboard
-
-using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
-using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
-using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
-using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
-using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
-
-constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
-constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
-constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
-constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
-
-enum class ControllerType {
- ProController,
- DualJoycon,
- RightJoycon,
- LeftJoycon,
-};
-
-struct PlayerInput {
- bool connected;
- ControllerType type;
- ButtonsRaw buttons;
- AnalogsRaw analogs;
-
- u32 body_color_right;
- u32 button_color_right;
- u32 body_color_left;
- u32 button_color_left;
-};
-
-struct TouchscreenInput {
- bool enabled;
- std::string device;
-
- u32 finger;
- u32 diameter_x;
- u32 diameter_y;
- u32 rotation_angle;
-};
-
enum class RendererBackend {
OpenGL = 0,
Vulkan = 1,
@@ -359,7 +29,8 @@ enum class GPUAccuracy : u32 {
enum class CPUAccuracy {
Accurate = 0,
- DebugMode = 1,
+ Unsafe = 1,
+ DebugMode = 2,
};
extern bool configuring_global;
@@ -419,6 +90,9 @@ struct Values {
bool cpuopt_misc_ir;
bool cpuopt_reduce_misalign_checks;
+ bool cpuopt_unsafe_unfuse_fma;
+ bool cpuopt_unsafe_reduce_fp_error;
+
// Renderer
Setting<RendererBackend> renderer_backend;
bool renderer_debug;
@@ -457,6 +131,8 @@ struct Values {
// Controls
std::array<PlayerInput, 10> players;
+ bool use_docked_mode;
+
bool mouse_enabled;
std::string mouse_device;
MouseButtonsRaw mouse_buttons;
@@ -470,14 +146,15 @@ struct Values {
AnalogsRaw debug_pad_analogs;
std::string motion_device;
+
+ bool vibration_enabled;
+
TouchscreenInput touchscreen;
std::atomic_bool is_device_reload_pending{true};
std::string udp_input_address;
u16 udp_input_port;
u8 udp_pad_index;
- bool use_docked_mode;
-
// Data Storage
bool use_virtual_sd;
bool gamecard_inserted;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 5a30c75da..da09c0dbc 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -25,6 +25,8 @@
namespace Core {
+namespace Telemetry = Common::Telemetry;
+
static u64 GenerateTelemetryId() {
u64 telemetry_id{};
@@ -70,12 +72,12 @@ static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) {
u64 GetTelemetryId() {
u64 telemetry_id{};
- const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
+ const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) +
"telemetry_id"};
- bool generate_new_id = !FileUtil::Exists(filename);
+ bool generate_new_id = !Common::FS::Exists(filename);
if (!generate_new_id) {
- FileUtil::IOFile file(filename, "rb");
+ Common::FS::IOFile file(filename, "rb");
if (!file.IsOpen()) {
LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
return {};
@@ -88,7 +90,7 @@ u64 GetTelemetryId() {
}
if (generate_new_id) {
- FileUtil::IOFile file(filename, "wb");
+ Common::FS::IOFile file(filename, "wb");
if (!file.IsOpen()) {
LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
return {};
@@ -102,10 +104,10 @@ u64 GetTelemetryId() {
u64 RegenerateTelemetryId() {
const u64 new_telemetry_id{GenerateTelemetryId()};
- const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
+ const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) +
"telemetry_id"};
- FileUtil::IOFile file(filename, "wb");
+ Common::FS::IOFile file(filename, "wb");
if (!file.IsOpen()) {
LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
return {};
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index 17ac22377..66789d4bd 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -52,7 +52,7 @@ public:
* @param value Value for the field to add.
*/
template <typename T>
- void AddField(Telemetry::FieldType type, const char* name, T value) {
+ void AddField(Common::Telemetry::FieldType type, const char* name, T value) {
field_collection.AddField(type, name, std::move(value));
}
@@ -63,7 +63,8 @@ public:
bool SubmitTestcase();
private:
- Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session
+ /// Tracks all added fields for the session
+ Common::Telemetry::FieldCollection field_collection;
};
/**
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) {
LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
- entries.erase(
- std::remove_if(entries.begin(), entries.end(),
- [&address](const Entry& entry) { return entry.address == address; }),
- entries.end());
+ std::erase_if(entries, [address](const Entry& entry) { return entry.address == address; });
}
bool Freezer::IsFrozen(VAddr address) const {
std::lock_guard lock{entries_mutex};
- return std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) {
- return entry.address == address;
- }) != entries.end();
+ return FindEntry(address) != entries.cend();
}
void Freezer::SetFrozenValue(VAddr address, u64 value) {
std::lock_guard lock{entries_mutex};
- const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) {
- return entry.address == address;
- });
+ const auto iter = FindEntry(address);
- if (iter == entries.end()) {
+ if (iter == entries.cend()) {
LOG_ERROR(Common_Memory,
"Tried to set freeze value for address={:016X} that is not frozen!", address);
return;
@@ -143,11 +136,9 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) {
std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
std::lock_guard lock{entries_mutex};
- const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) {
- return entry.address == address;
- });
+ const auto iter = FindEntry(address);
- if (iter == entries.end()) {
+ if (iter == entries.cend()) {
return std::nullopt;
}
@@ -160,6 +151,16 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const {
return entries;
}
+Freezer::Entries::iterator Freezer::FindEntry(VAddr address) {
+ return std::find_if(entries.begin(), entries.end(),
+ [address](const Entry& entry) { return entry.address == address; });
+}
+
+Freezer::Entries::const_iterator Freezer::FindEntry(VAddr address) const {
+ return std::find_if(entries.begin(), entries.end(),
+ [address](const Entry& entry) { return entry.address == address; });
+}
+
void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
if (!IsActive()) {
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:
std::vector<Entry> GetEntries() const;
private:
+ using Entries = std::vector<Entry>;
+
+ Entries::iterator FindEntry(VAddr address);
+ Entries::const_iterator FindEntry(VAddr address) const;
+
void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void FillEntryReads();
std::atomic_bool active{false};
mutable std::mutex entries_mutex;
- std::vector<Entry> entries;
+ Entries entries;
std::shared_ptr<Core::Timing::EventType> event;
Core::Timing::CoreTiming& core_timing;
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 317c25bad..56267c8a8 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -7,6 +7,8 @@ add_library(input_common STATIC
main.h
motion_emu.cpp
motion_emu.h
+ settings.cpp
+ settings.h
gcadapter/gc_adapter.cpp
gcadapter/gc_adapter.h
gcadapter/gc_poller.cpp
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index f45983f3f..71cd85eeb 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -148,19 +148,17 @@ void GCButtonFactory::EndConfiguration() {
class GCAnalog final : public Input::AnalogDevice {
public:
- GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter)
+ GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter,
+ float range_)
: port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
origin_value_x(adapter->GetOriginValue(port_, axis_x_)),
- origin_value_y(adapter->GetOriginValue(port_, axis_y_)) {}
+ origin_value_y(adapter->GetOriginValue(port_, axis_y_)), range(range_) {}
float GetAxis(int axis) const {
if (gcadapter->DeviceConnected(port)) {
std::lock_guard lock{mutex};
const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
- // division is not by a perfect 128 to account for some variance in center location
- // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
- // [20-230]
- return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / 95.0f;
+ return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / (100.0f * range);
}
return 0.0f;
}
@@ -193,7 +191,7 @@ public:
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
const auto [x, y] = GetStatus();
- const float directional_deadzone = 0.4f;
+ const float directional_deadzone = 0.5f;
switch (direction) {
case Input::AnalogDirection::RIGHT:
return x > directional_deadzone;
@@ -215,6 +213,7 @@ private:
GCAdapter::Adapter* gcadapter;
const float origin_value_x;
const float origin_value_y;
+ const float range;
mutable std::mutex mutex;
};
@@ -233,9 +232,10 @@ std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::Param
const int port = params.Get("port", 0);
const int axis_x = params.Get("axis_x", 0);
const int axis_y = params.Get("axis_y", 1);
- const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
+ const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
+ const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
- return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get());
+ return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range);
}
void GCAnalogFactory::BeginConfiguration() {
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index b9d5d0ec3..57e7a25fe 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -18,67 +18,166 @@
namespace InputCommon {
-static std::shared_ptr<Keyboard> keyboard;
-static std::shared_ptr<MotionEmu> motion_emu;
+struct InputSubsystem::Impl {
+ void Initialize() {
+ auto gcadapter = std::make_shared<GCAdapter::Adapter>();
+ gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
+ Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
+ gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
+ Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
+
+ keyboard = std::make_shared<Keyboard>();
+ Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
+ Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
+ std::make_shared<AnalogFromButton>());
+ motion_emu = std::make_shared<MotionEmu>();
+ Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
+
#ifdef HAVE_SDL2
-static std::unique_ptr<SDL::State> sdl;
+ sdl = SDL::Init();
#endif
-static std::unique_ptr<CemuhookUDP::State> udp;
-static std::shared_ptr<GCButtonFactory> gcbuttons;
-static std::shared_ptr<GCAnalogFactory> gcanalog;
-
-void Init() {
- auto gcadapter = std::make_shared<GCAdapter::Adapter>();
- gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
- Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
- gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
- Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
-
- keyboard = std::make_shared<Keyboard>();
- Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
- Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
- std::make_shared<AnalogFromButton>());
- motion_emu = std::make_shared<MotionEmu>();
- Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
+ udp = CemuhookUDP::Init();
+ }
+
+ void Shutdown() {
+ Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
+ keyboard.reset();
+ Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
+ Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
+ motion_emu.reset();
#ifdef HAVE_SDL2
- sdl = SDL::Init();
+ sdl.reset();
#endif
+ udp.reset();
+ Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
+ Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
+
+ gcbuttons.reset();
+ gcanalog.reset();
+ }
+
+ [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
+ std::vector<Common::ParamPackage> devices = {
+ Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
+ Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "key"}},
+ };
+#ifdef HAVE_SDL2
+ auto sdl_devices = sdl->GetInputDevices();
+ devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
+#endif
+ auto udp_devices = udp->GetInputDevices();
+ devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
+ return devices;
+ }
+
+ [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
+ const Common::ParamPackage& params) const {
+ if (!params.Has("class") || params.Get("class", "") == "any") {
+ return {};
+ }
+ if (params.Get("class", "") == "key") {
+ // TODO consider returning the SDL key codes for the default keybindings
+ return {};
+ }
+#ifdef HAVE_SDL2
+ if (params.Get("class", "") == "sdl") {
+ return sdl->GetAnalogMappingForDevice(params);
+ }
+#endif
+ return {};
+ }
+
+ [[nodiscard]] ButtonMapping GetButtonMappingForDevice(
+ const Common::ParamPackage& params) const {
+ if (!params.Has("class") || params.Get("class", "") == "any") {
+ return {};
+ }
+ if (params.Get("class", "") == "key") {
+ // TODO consider returning the SDL key codes for the default keybindings
+ return {};
+ }
+#ifdef HAVE_SDL2
+ if (params.Get("class", "") == "sdl") {
+ return sdl->GetButtonMappingForDevice(params);
+ }
+#endif
+ return {};
+ }
- udp = CemuhookUDP::Init();
-}
-
-void Shutdown() {
- Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
- keyboard.reset();
- Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
- Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
- motion_emu.reset();
+ std::shared_ptr<Keyboard> keyboard;
+ std::shared_ptr<MotionEmu> motion_emu;
#ifdef HAVE_SDL2
- sdl.reset();
+ std::unique_ptr<SDL::State> sdl;
#endif
- udp.reset();
- Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
- Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
+ std::unique_ptr<CemuhookUDP::State> udp;
+ std::shared_ptr<GCButtonFactory> gcbuttons;
+ std::shared_ptr<GCAnalogFactory> gcanalog;
+};
+
+InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
+
+InputSubsystem::~InputSubsystem() = default;
+
+void InputSubsystem::Initialize() {
+ impl->Initialize();
+}
+
+void InputSubsystem::Shutdown() {
+ impl->Shutdown();
+}
+
+Keyboard* InputSubsystem::GetKeyboard() {
+ return impl->keyboard.get();
+}
+
+const Keyboard* InputSubsystem::GetKeyboard() const {
+ return impl->keyboard.get();
+}
- gcbuttons.reset();
- gcanalog.reset();
+MotionEmu* InputSubsystem::GetMotionEmu() {
+ return impl->motion_emu.get();
}
-Keyboard* GetKeyboard() {
- return keyboard.get();
+const MotionEmu* InputSubsystem::GetMotionEmu() const {
+ return impl->motion_emu.get();
}
-MotionEmu* GetMotionEmu() {
- return motion_emu.get();
+std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
+ return impl->GetInputDevices();
}
-GCButtonFactory* GetGCButtons() {
- return gcbuttons.get();
+AnalogMapping InputSubsystem::GetAnalogMappingForDevice(const Common::ParamPackage& device) const {
+ return impl->GetAnalogMappingForDevice(device);
}
-GCAnalogFactory* GetGCAnalogs() {
- return gcanalog.get();
+ButtonMapping InputSubsystem::GetButtonMappingForDevice(const Common::ParamPackage& device) const {
+ return impl->GetButtonMappingForDevice(device);
+}
+
+GCAnalogFactory* InputSubsystem::GetGCAnalogs() {
+ return impl->gcanalog.get();
+}
+
+const GCAnalogFactory* InputSubsystem::GetGCAnalogs() const {
+ return impl->gcanalog.get();
+}
+
+GCButtonFactory* InputSubsystem::GetGCButtons() {
+ return impl->gcbuttons.get();
+}
+
+const GCButtonFactory* InputSubsystem::GetGCButtons() const {
+ return impl->gcbuttons.get();
+}
+
+std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
+ Polling::DeviceType type) const {
+#ifdef HAVE_SDL2
+ return impl->sdl->GetPollers(type);
+#else
+ return {};
+#endif
}
std::string GenerateKeyboardParam(int key_code) {
@@ -102,18 +201,4 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left,
};
return circle_pad_param.Serialize();
}
-
-namespace Polling {
-
-std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
- std::vector<std::unique_ptr<DevicePoller>> pollers;
-
-#ifdef HAVE_SDL2
- pollers = sdl->GetPollers(type);
-#endif
-
- return pollers;
-}
-
-} // namespace Polling
} // namespace InputCommon
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 0e32856f6..58e5dc250 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -6,45 +6,25 @@
#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
-#include "input_common/gcadapter/gc_poller.h"
namespace Common {
class ParamPackage;
}
-namespace InputCommon {
-
-/// Initializes and registers all built-in input device factories.
-void Init();
-
-/// Deregisters all built-in input device factories and shuts them down.
-void Shutdown();
-
-class Keyboard;
-
-/// Gets the keyboard button device factory.
-Keyboard* GetKeyboard();
-
-class MotionEmu;
-
-/// Gets the motion emulation factory.
-MotionEmu* GetMotionEmu();
-
-GCButtonFactory* GetGCButtons();
-
-GCAnalogFactory* GetGCAnalogs();
-
-/// Generates a serialized param package for creating a keyboard button device
-std::string GenerateKeyboardParam(int key_code);
+namespace Settings::NativeAnalog {
+enum Values : int;
+}
-/// Generates a serialized param package for creating an analog device taking input from keyboard
-std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
- int key_modifier, float modifier_scale);
+namespace Settings::NativeButton {
+enum Values : int;
+}
+namespace InputCommon {
namespace Polling {
-enum class DeviceType { Button, Analog };
+enum class DeviceType { Button, AnalogPreferred };
/**
* A class that can be used to get inputs from an input device like controllers without having to
@@ -54,7 +34,9 @@ class DevicePoller {
public:
virtual ~DevicePoller() = default;
/// Setup and start polling for inputs, should be called before GetNextInput
- virtual void Start() = 0;
+ /// If a device_id is provided, events should be filtered to only include events from this
+ /// device id
+ virtual void Start(const std::string& device_id = "") = 0;
/// Stop polling
virtual void Stop() = 0;
/**
@@ -64,8 +46,89 @@ public:
*/
virtual Common::ParamPackage GetNextInput() = 0;
};
-
-// Get all DevicePoller from all backends for a specific device type
-std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type);
} // namespace Polling
+
+class GCAnalogFactory;
+class GCButtonFactory;
+class Keyboard;
+class MotionEmu;
+
+/**
+ * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
+ * mapping for the device. This is currently only implemented for the SDL backend devices.
+ */
+using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
+using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
+
+class InputSubsystem {
+public:
+ explicit InputSubsystem();
+ ~InputSubsystem();
+
+ InputSubsystem(const InputSubsystem&) = delete;
+ InputSubsystem& operator=(const InputSubsystem&) = delete;
+
+ InputSubsystem(InputSubsystem&&) = delete;
+ InputSubsystem& operator=(InputSubsystem&&) = delete;
+
+ /// Initializes and registers all built-in input device factories.
+ void Initialize();
+
+ /// Unregisters all built-in input device factories and shuts them down.
+ void Shutdown();
+
+ /// Retrieves the underlying keyboard device.
+ [[nodiscard]] Keyboard* GetKeyboard();
+
+ /// Retrieves the underlying keyboard device.
+ [[nodiscard]] const Keyboard* GetKeyboard() const;
+
+ /// Retrieves the underlying motion emulation factory.
+ [[nodiscard]] MotionEmu* GetMotionEmu();
+
+ /// Retrieves the underlying motion emulation factory.
+ [[nodiscard]] const MotionEmu* GetMotionEmu() const;
+
+ /**
+ * Returns all available input devices that this Factory can create a new device with.
+ * Each returned ParamPackage should have a `display` field used for display, a class field for
+ * backends to determine if this backend is meant to service the request and any other
+ * information needed to identify this in the backend later.
+ */
+ [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const;
+
+ /// Retrieves the analog mappings for the given device.
+ [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& device) const;
+
+ /// Retrieves the button mappings for the given device.
+ [[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const;
+
+ /// Retrieves the underlying GameCube analog handler.
+ [[nodiscard]] GCAnalogFactory* GetGCAnalogs();
+
+ /// Retrieves the underlying GameCube analog handler.
+ [[nodiscard]] const GCAnalogFactory* GetGCAnalogs() const;
+
+ /// Retrieves the underlying GameCube button handler.
+ [[nodiscard]] GCButtonFactory* GetGCButtons();
+
+ /// Retrieves the underlying GameCube button handler.
+ [[nodiscard]] const GCButtonFactory* GetGCButtons() const;
+
+ /// Get all DevicePoller from all backends for a specific device type
+ [[nodiscard]] std::vector<std::unique_ptr<Polling::DevicePoller>> GetPollers(
+ Polling::DeviceType type) const;
+
+private:
+ struct Impl;
+ std::unique_ptr<Impl> impl;
+};
+
+/// Generates a serialized param package for creating a keyboard button device
+std::string GenerateKeyboardParam(int key_code);
+
+/// Generates a serialized param package for creating an analog device taking input from keyboard
+std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
+ int key_modifier, float modifier_scale);
+
} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index 5306daa70..f3554be9a 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -6,6 +6,7 @@
#include <memory>
#include <vector>
+#include "common/param_package.h"
#include "input_common/main.h"
namespace InputCommon::Polling {
@@ -22,14 +23,24 @@ public:
/// Unregisters SDL device factories and shut them down.
virtual ~State() = default;
- virtual Pollers GetPollers(Polling::DeviceType type) = 0;
+ virtual Pollers GetPollers(Polling::DeviceType type) {
+ return {};
+ }
+
+ virtual std::vector<Common::ParamPackage> GetInputDevices() {
+ return {};
+ }
+
+ virtual ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage&) {
+ return {};
+ }
+ virtual AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage&) {
+ return {};
+ }
};
class NullState : public State {
public:
- Pollers GetPollers(Polling::DeviceType type) override {
- return {};
- }
};
std::unique_ptr<State> Init();
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 675b477fa..a9e676f4b 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -3,10 +3,13 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <array>
#include <atomic>
#include <cmath>
#include <functional>
#include <mutex>
+#include <optional>
+#include <sstream>
#include <string>
#include <thread>
#include <tuple>
@@ -15,15 +18,16 @@
#include <vector>
#include <SDL.h>
#include "common/logging/log.h"
-#include "common/math_util.h"
#include "common/param_package.h"
#include "common/threadsafe_queue.h"
#include "core/frontend/input.h"
#include "input_common/sdl/sdl_impl.h"
+#include "input_common/settings.h"
namespace InputCommon::SDL {
-static std::string GetGUID(SDL_Joystick* joystick) {
+namespace {
+std::string GetGUID(SDL_Joystick* joystick) {
const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
char guid_str[33];
SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
@@ -31,7 +35,8 @@ static std::string GetGUID(SDL_Joystick* joystick) {
}
/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
-static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
+Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
+} // Anonymous namespace
static int SDLEventWatcher(void* user_data, SDL_Event* event) {
auto* const sdl_state = static_cast<SDLState*>(user_data);
@@ -48,8 +53,10 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
class SDLJoystick {
public:
- SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick)
- : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {}
+ SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
+ SDL_GameController* gamecontroller)
+ : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
+ sdl_controller{gamecontroller, &SDL_GameControllerClose} {}
void SetButton(int button, bool value) {
std::lock_guard lock{mutex};
@@ -66,14 +73,14 @@ public:
state.axes.insert_or_assign(axis, value);
}
- float GetAxis(int axis) const {
+ float GetAxis(int axis, float range) const {
std::lock_guard lock{mutex};
- return state.axes.at(axis) / 32767.0f;
+ return state.axes.at(axis) / (32767.0f * range);
}
- std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const {
- float x = GetAxis(axis_x);
- float y = GetAxis(axis_y);
+ std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const {
+ float x = GetAxis(axis_x, range);
+ float y = GetAxis(axis_y, range);
y = -y; // 3DS uses an y-axis inverse from SDL
// Make sure the coordinates are in the unit circle,
@@ -115,10 +122,15 @@ public:
return sdl_joystick.get();
}
- void SetSDLJoystick(SDL_Joystick* joystick) {
+ void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
+ sdl_controller.reset(controller);
sdl_joystick.reset(joystick);
}
+ SDL_GameController* GetSDLGameController() const {
+ return sdl_controller.get();
+ }
+
private:
struct State {
std::unordered_map<int, bool> buttons;
@@ -128,6 +140,7 @@ private:
std::string guid;
int port;
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
+ std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
mutable std::mutex mutex;
};
@@ -136,18 +149,19 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& g
const auto it = joystick_map.find(guid);
if (it != joystick_map.end()) {
while (it->second.size() <= static_cast<std::size_t>(port)) {
- auto joystick =
- std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), nullptr);
+ auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
+ nullptr, nullptr);
it->second.emplace_back(std::move(joystick));
}
return it->second[port];
}
- auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr);
+ auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
return joystick_map[guid].emplace_back(std::move(joystick));
}
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
+ auto sdl_controller = SDL_GameControllerFromInstanceID(sdl_id);
const std::string guid = GetGUID(sdl_joystick);
std::lock_guard lock{joystick_map_mutex};
@@ -171,23 +185,27 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_
});
if (nullptr_it != map_it->second.end()) {
// ... and map it
- (*nullptr_it)->SetSDLJoystick(sdl_joystick);
+ (*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller);
return *nullptr_it;
}
// There is no SDLJoystick without a mapped SDL_Joystick
// Create a new SDLJoystick
const int port = static_cast<int>(map_it->second.size());
- auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick);
+ auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_controller);
return map_it->second.emplace_back(std::move(joystick));
}
- auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
+ auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_controller);
return joystick_map[guid].emplace_back(std::move(joystick));
}
void SDLState::InitJoystick(int joystick_index) {
SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
+ SDL_GameController* sdl_gamecontroller = nullptr;
+ if (SDL_IsGameController(joystick_index)) {
+ sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
+ }
if (!sdl_joystick) {
LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
return;
@@ -196,7 +214,7 @@ void SDLState::InitJoystick(int joystick_index) {
std::lock_guard lock{joystick_map_mutex};
if (joystick_map.find(guid) == joystick_map.end()) {
- auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
+ auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
joystick_map[guid].emplace_back(std::move(joystick));
return;
}
@@ -205,11 +223,11 @@ void SDLState::InitJoystick(int joystick_index) {
joystick_guid_list.begin(), joystick_guid_list.end(),
[](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
if (it != joystick_guid_list.end()) {
- (*it)->SetSDLJoystick(sdl_joystick);
+ (*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
return;
}
const int port = static_cast<int>(joystick_guid_list.size());
- auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick);
+ auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
joystick_guid_list.emplace_back(std::move(joystick));
}
@@ -231,7 +249,7 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
// Destruct SDL_Joystick outside the lock guard because SDL can internally call the
// event callback which locks the mutex again.
- joystick->SetSDLJoystick(nullptr);
+ joystick->SetSDLJoystick(nullptr, nullptr);
}
void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
@@ -313,7 +331,7 @@ public:
trigger_if_greater(trigger_if_greater_) {}
bool GetStatus() const override {
- const float axis_value = joystick->GetAxis(axis);
+ const float axis_value = joystick->GetAxis(axis, 1.0f);
if (trigger_if_greater) {
return axis_value > threshold;
}
@@ -329,22 +347,24 @@ private:
class SDLAnalog final : public Input::AnalogDevice {
public:
- SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_)
- : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_) {}
+ SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_,
+ float range_)
+ : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_),
+ range(range_) {}
std::tuple<float, float> GetStatus() const override {
- const auto [x, y] = joystick->GetAnalog(axis_x, axis_y);
+ const auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range);
const float r = std::sqrt((x * x) + (y * y));
if (r > deadzone) {
return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
y / r * (r - deadzone) / (1 - deadzone));
}
- return std::make_tuple<float, float>(0.0f, 0.0f);
+ return {};
}
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
const auto [x, y] = GetStatus();
- const float directional_deadzone = 0.4f;
+ const float directional_deadzone = 0.5f;
switch (direction) {
case Input::AnalogDirection::RIGHT:
return x > directional_deadzone;
@@ -363,6 +383,7 @@ private:
const int axis_x;
const int axis_y;
const float deadzone;
+ const float range;
};
/// A button device factory that creates button devices from SDL joystick
@@ -457,14 +478,14 @@ public:
const int port = params.Get("port", 0);
const int axis_x = params.Get("axis_x", 0);
const int axis_y = params.Get("axis_y", 1);
- const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
-
+ const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
+ const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
auto joystick = state.GetSDLJoystickByGUID(guid, port);
// This is necessary so accessing GetAxis with axis_x and axis_y won't crash
joystick->SetAxis(axis_x, 0);
joystick->SetAxis(axis_y, 0);
- return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone);
+ return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone, range);
}
private:
@@ -473,8 +494,10 @@ private:
SDLState::SDLState() {
using namespace Input;
- RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this));
- RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this));
+ analog_factory = std::make_shared<SDLAnalogFactory>(*this);
+ button_factory = std::make_shared<SDLButtonFactory>(*this);
+ RegisterFactory<AnalogDevice>("sdl", analog_factory);
+ RegisterFactory<ButtonDevice>("sdl", button_factory);
// If the frontend is going to manage the event loop, then we dont start one here
start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK);
@@ -482,6 +505,7 @@ SDLState::SDLState() {
LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
return;
}
+ has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
}
@@ -494,7 +518,7 @@ SDLState::SDLState() {
using namespace std::chrono_literals;
while (initialized) {
SDL_PumpEvents();
- std::this_thread::sleep_for(10ms);
+ std::this_thread::sleep_for(5ms);
}
});
}
@@ -520,65 +544,230 @@ SDLState::~SDLState() {
}
}
-static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
+std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
+ std::scoped_lock lock(joystick_map_mutex);
+ std::vector<Common::ParamPackage> devices;
+ for (const auto& [key, value] : joystick_map) {
+ for (const auto& joystick : value) {
+ auto joy = joystick->GetSDLJoystick();
+ if (auto controller = joystick->GetSDLGameController()) {
+ std::string name =
+ fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
+ devices.emplace_back(Common::ParamPackage{
+ {"class", "sdl"},
+ {"display", std::move(name)},
+ {"guid", joystick->GetGUID()},
+ {"port", std::to_string(joystick->GetPort())},
+ });
+ } else if (joy) {
+ std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
+ devices.emplace_back(Common::ParamPackage{
+ {"class", "sdl"},
+ {"display", std::move(name)},
+ {"guid", joystick->GetGUID()},
+ {"port", std::to_string(joystick->GetPort())},
+ });
+ }
+ }
+ }
+ return devices;
+}
+
+namespace {
+Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis,
+ float value = 0.1f) {
+ Common::ParamPackage params({{"engine", "sdl"}});
+ params.Set("port", port);
+ params.Set("guid", std::move(guid));
+ params.Set("axis", axis);
+ if (value > 0) {
+ params.Set("direction", "+");
+ params.Set("threshold", "0.5");
+ } else {
+ params.Set("direction", "-");
+ params.Set("threshold", "-0.5");
+ }
+ return params;
+}
+
+Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) {
Common::ParamPackage params({{"engine", "sdl"}});
+ params.Set("port", port);
+ params.Set("guid", std::move(guid));
+ params.Set("button", button);
+ return params;
+}
+
+Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) {
+ Common::ParamPackage params({{"engine", "sdl"}});
+
+ params.Set("port", port);
+ params.Set("guid", std::move(guid));
+ params.Set("hat", hat);
+ switch (value) {
+ case SDL_HAT_UP:
+ params.Set("direction", "up");
+ break;
+ case SDL_HAT_DOWN:
+ params.Set("direction", "down");
+ break;
+ case SDL_HAT_LEFT:
+ params.Set("direction", "left");
+ break;
+ case SDL_HAT_RIGHT:
+ params.Set("direction", "right");
+ break;
+ default:
+ return {};
+ }
+ return params;
+}
+Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
switch (event.type) {
case SDL_JOYAXISMOTION: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
- params.Set("port", joystick->GetPort());
- params.Set("guid", joystick->GetGUID());
- params.Set("axis", event.jaxis.axis);
- if (event.jaxis.value > 0) {
- params.Set("direction", "+");
- params.Set("threshold", "0.5");
- } else {
- params.Set("direction", "-");
- params.Set("threshold", "-0.5");
- }
- break;
+ return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
+ event.jaxis.axis, event.jaxis.value);
}
case SDL_JOYBUTTONUP: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
- params.Set("port", joystick->GetPort());
- params.Set("guid", joystick->GetGUID());
- params.Set("button", event.jbutton.button);
- break;
+ return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
+ event.jbutton.button);
}
case SDL_JOYHATMOTION: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
- params.Set("port", joystick->GetPort());
- params.Set("guid", joystick->GetGUID());
- params.Set("hat", event.jhat.hat);
- switch (event.jhat.value) {
- case SDL_HAT_UP:
- params.Set("direction", "up");
- break;
- case SDL_HAT_DOWN:
- params.Set("direction", "down");
- break;
- case SDL_HAT_LEFT:
- params.Set("direction", "left");
- break;
- case SDL_HAT_RIGHT:
- params.Set("direction", "right");
- break;
- default:
- return {};
- }
- break;
+ return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
+ event.jhat.hat, event.jhat.value);
}
}
+ return {};
+}
+
+Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
+ const SDL_GameControllerButtonBind& binding) {
+ switch (binding.bindType) {
+ case SDL_CONTROLLER_BINDTYPE_AXIS:
+ return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
+ case SDL_CONTROLLER_BINDTYPE_BUTTON:
+ return BuildButtonParamPackageForButton(port, guid, binding.value.button);
+ case SDL_CONTROLLER_BINDTYPE_HAT:
+ return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
+ binding.value.hat.hat_mask);
+ }
+ return {};
+}
+
+Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
+ int axis_y) {
+ Common::ParamPackage params;
+ params.Set("engine", "sdl");
+ params.Set("port", port);
+ params.Set("guid", guid);
+ params.Set("axis_x", axis_x);
+ params.Set("axis_y", axis_y);
return params;
}
+} // Anonymous namespace
-namespace Polling {
+ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) {
+ if (!params.Has("guid") || !params.Has("port")) {
+ return {};
+ }
+ const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
+ auto* controller = joystick->GetSDLGameController();
+ if (controller == nullptr) {
+ return {};
+ }
+
+ // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
+ // We will add those afterwards
+ // This list also excludes Screenshot since theres not really a mapping for that
+ using ButtonBindings =
+ std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
+ static constexpr ButtonBindings switch_to_sdl_button{{
+ {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
+ {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
+ {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
+ {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
+ {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
+ {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
+ {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
+ {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
+ {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
+ {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
+ {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
+ {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
+ {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
+ {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
+ {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
+ {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
+ {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
+ }};
+
+ // Add the missing bindings for ZL/ZR
+ using ZBindings =
+ std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
+ static constexpr ZBindings switch_to_sdl_axis{{
+ {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
+ {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
+ }};
+
+ ButtonMapping mapping;
+ mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
+
+ for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
+ const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
+ mapping.insert_or_assign(
+ switch_button,
+ BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
+ }
+ for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
+ const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
+ mapping.insert_or_assign(
+ switch_button,
+ BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
+ }
+
+ return mapping;
+}
+AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
+ if (!params.Has("guid") || !params.Has("port")) {
+ return {};
+ }
+ const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
+ auto* controller = joystick->GetSDLGameController();
+ if (controller == nullptr) {
+ return {};
+ }
+
+ AnalogMapping mapping = {};
+ const auto& binding_left_x =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
+ const auto& binding_left_y =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
+ mapping.insert_or_assign(Settings::NativeAnalog::LStick,
+ BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
+ binding_left_x.value.axis,
+ binding_left_y.value.axis));
+ const auto& binding_right_x =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
+ const auto& binding_right_y =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
+ mapping.insert_or_assign(Settings::NativeAnalog::RStick,
+ BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
+ binding_right_x.value.axis,
+ binding_right_y.value.axis));
+ return mapping;
+}
+
+namespace Polling {
class SDLPoller : public InputCommon::Polling::DevicePoller {
public:
explicit SDLPoller(SDLState& state_) : state(state_) {}
- void Start() override {
+ void Start(const std::string& device_id) override {
state.event_queue.Clear();
state.polling = true;
}
@@ -598,71 +787,106 @@ public:
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (state.event_queue.Pop(event)) {
- switch (event.type) {
- case SDL_JOYAXISMOTION:
- if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
- break;
- }
- [[fallthrough]];
- case SDL_JOYBUTTONUP:
- case SDL_JOYHATMOTION:
- return SDLEventToButtonParamPackage(state, event);
+ const auto package = FromEvent(event);
+ if (package) {
+ return *package;
}
}
return {};
}
+ [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const {
+ switch (event.type) {
+ case SDL_JOYAXISMOTION:
+ if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
+ break;
+ }
+ [[fallthrough]];
+ case SDL_JOYBUTTONUP:
+ case SDL_JOYHATMOTION:
+ return {SDLEventToButtonParamPackage(state, event)};
+ }
+ return std::nullopt;
+ }
};
-class SDLAnalogPoller final : public SDLPoller {
+/**
+ * Attempts to match the press to a controller joy axis (left/right stick) and if a match
+ * isn't found, checks if the event matches anything from SDLButtonPoller and uses that
+ * instead
+ */
+class SDLAnalogPreferredPoller final : public SDLPoller {
public:
- explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {}
-
- void Start() override {
- SDLPoller::Start();
+ explicit SDLAnalogPreferredPoller(SDLState& state_)
+ : SDLPoller(state_), button_poller(state_) {}
+ void Start(const std::string& device_id) override {
+ SDLPoller::Start(device_id);
+ // Load the game controller
// Reset stored axes
analog_x_axis = -1;
analog_y_axis = -1;
- analog_axes_joystick = -1;
}
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (state.event_queue.Pop(event)) {
- if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
+ // Filter out axis events that are below a threshold
+ if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
continue;
}
- // An analog device needs two axes, so we need to store the axis for later and wait for
- // a second SDL event. The axes also must be from the same joystick.
- const int axis = event.jaxis.axis;
- if (analog_x_axis == -1) {
- analog_x_axis = axis;
- analog_axes_joystick = event.jaxis.which;
- } else if (analog_y_axis == -1 && analog_x_axis != axis &&
- analog_axes_joystick == event.jaxis.which) {
- analog_y_axis = axis;
+ // Simplify controller config by testing if game controller support is enabled.
+ if (event.type == SDL_JOYAXISMOTION) {
+ const auto axis = event.jaxis.axis;
+ const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
+ const auto controller = joystick->GetSDLGameController();
+ if (controller) {
+ const auto axis_left_x =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)
+ .value.axis;
+ const auto axis_left_y =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY)
+ .value.axis;
+ const auto axis_right_x =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX)
+ .value.axis;
+ const auto axis_right_y =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY)
+ .value.axis;
+
+ if (axis == axis_left_x || axis == axis_left_y) {
+ analog_x_axis = axis_left_x;
+ analog_y_axis = axis_left_y;
+ break;
+ } else if (axis == axis_right_x || axis == axis_right_y) {
+ analog_x_axis = axis_right_x;
+ analog_y_axis = axis_right_y;
+ break;
+ }
+ }
+ }
+
+ // If the press wasn't accepted as a joy axis, check for a button press
+ auto button_press = button_poller.FromEvent(event);
+ if (button_press) {
+ return *button_press;
}
}
- Common::ParamPackage params;
+
if (analog_x_axis != -1 && analog_y_axis != -1) {
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
- params.Set("engine", "sdl");
- params.Set("port", joystick->GetPort());
- params.Set("guid", joystick->GetGUID());
- params.Set("axis_x", analog_x_axis);
- params.Set("axis_y", analog_y_axis);
+ auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
+ analog_x_axis, analog_y_axis);
analog_x_axis = -1;
analog_y_axis = -1;
- analog_axes_joystick = -1;
return params;
}
- return params;
+ return {};
}
private:
int analog_x_axis = -1;
int analog_y_axis = -1;
- SDL_JoystickID analog_axes_joystick = -1;
+ SDLButtonPoller button_poller;
};
} // namespace Polling
@@ -670,8 +894,8 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
Pollers pollers;
switch (type) {
- case InputCommon::Polling::DeviceType::Analog:
- pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this));
+ case InputCommon::Polling::DeviceType::AnalogPreferred:
+ pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this));
break;
case InputCommon::Polling::DeviceType::Button:
pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index 606a32c5b..bd19ba61d 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -50,6 +50,11 @@ public:
std::atomic<bool> polling = false;
Common::SPSCQueue<SDL_Event> event_queue;
+ std::vector<Common::ParamPackage> GetInputDevices() override;
+
+ ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
+ AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
+
private:
void InitJoystick(int joystick_index);
void CloseJoystick(SDL_Joystick* sdl_joystick);
@@ -57,6 +62,9 @@ private:
/// Needs to be called before SDL_QuitSubSystem.
void CloseJoysticks();
+ // Set to true if SDL supports game controller subsystem
+ bool has_gamecontroller = false;
+
/// Map of GUID of a list of corresponding virtual Joysticks
std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
std::mutex joystick_map_mutex;
diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp
new file mode 100644
index 000000000..80c719cf4
--- /dev/null
+++ b/src/input_common/settings.cpp
@@ -0,0 +1,33 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "input_common/settings.h"
+
+namespace Settings {
+namespace NativeButton {
+const std::array<const char*, NumButtons> mapping = {{
+ "button_a", "button_b", "button_x", "button_y", "button_lstick",
+ "button_rstick", "button_l", "button_r", "button_zl", "button_zr",
+ "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
+ "button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot",
+}};
+}
+
+namespace NativeAnalog {
+const std::array<const char*, NumAnalogs> mapping = {{
+ "lstick",
+ "rstick",
+}};
+}
+
+namespace NativeMouseButton {
+const std::array<const char*, NumMouseButtons> mapping = {{
+ "left",
+ "right",
+ "middle",
+ "forward",
+ "back",
+}};
+}
+} // namespace Settings
diff --git a/src/input_common/settings.h b/src/input_common/settings.h
new file mode 100644
index 000000000..2d258960b
--- /dev/null
+++ b/src/input_common/settings.h
@@ -0,0 +1,335 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <string>
+#include "common/common_types.h"
+
+namespace Settings {
+namespace NativeButton {
+enum Values : int {
+ A,
+ B,
+ X,
+ Y,
+ LStick,
+ RStick,
+ L,
+ R,
+ ZL,
+ ZR,
+ Plus,
+ Minus,
+
+ DLeft,
+ DUp,
+ DRight,
+ DDown,
+
+ SL,
+ SR,
+
+ Home,
+ Screenshot,
+
+ NumButtons,
+};
+
+constexpr int BUTTON_HID_BEGIN = A;
+constexpr int BUTTON_NS_BEGIN = Home;
+
+constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
+constexpr int BUTTON_NS_END = NumButtons;
+
+constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
+constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
+
+extern const std::array<const char*, NumButtons> mapping;
+
+} // namespace NativeButton
+
+namespace NativeAnalog {
+enum Values : int {
+ LStick,
+ RStick,
+
+ NumAnalogs,
+};
+
+constexpr int STICK_HID_BEGIN = LStick;
+constexpr int STICK_HID_END = NumAnalogs;
+constexpr int NUM_STICKS_HID = NumAnalogs;
+
+extern const std::array<const char*, NumAnalogs> mapping;
+} // namespace NativeAnalog
+
+namespace NativeMouseButton {
+enum Values {
+ Left,
+ Right,
+ Middle,
+ Forward,
+ Back,
+
+ NumMouseButtons,
+};
+
+constexpr int MOUSE_HID_BEGIN = Left;
+constexpr int MOUSE_HID_END = NumMouseButtons;
+constexpr int NUM_MOUSE_HID = NumMouseButtons;
+
+extern const std::array<const char*, NumMouseButtons> mapping;
+} // namespace NativeMouseButton
+
+namespace NativeKeyboard {
+enum Keys {
+ None,
+ Error,
+
+ A = 4,
+ B,
+ C,
+ D,
+ E,
+ F,
+ G,
+ H,
+ I,
+ J,
+ K,
+ L,
+ M,
+ N,
+ O,
+ P,
+ Q,
+ R,
+ S,
+ T,
+ U,
+ V,
+ W,
+ X,
+ Y,
+ Z,
+ N1,
+ N2,
+ N3,
+ N4,
+ N5,
+ N6,
+ N7,
+ N8,
+ N9,
+ N0,
+ Enter,
+ Escape,
+ Backspace,
+ Tab,
+ Space,
+ Minus,
+ Equal,
+ LeftBrace,
+ RightBrace,
+ Backslash,
+ Tilde,
+ Semicolon,
+ Apostrophe,
+ Grave,
+ Comma,
+ Dot,
+ Slash,
+ CapsLockKey,
+
+ F1,
+ F2,
+ F3,
+ F4,
+ F5,
+ F6,
+ F7,
+ F8,
+ F9,
+ F10,
+ F11,
+ F12,
+
+ SystemRequest,
+ ScrollLockKey,
+ Pause,
+ Insert,
+ Home,
+ PageUp,
+ Delete,
+ End,
+ PageDown,
+ Right,
+ Left,
+ Down,
+ Up,
+
+ NumLockKey,
+ KPSlash,
+ KPAsterisk,
+ KPMinus,
+ KPPlus,
+ KPEnter,
+ KP1,
+ KP2,
+ KP3,
+ KP4,
+ KP5,
+ KP6,
+ KP7,
+ KP8,
+ KP9,
+ KP0,
+ KPDot,
+
+ Key102,
+ Compose,
+ Power,
+ KPEqual,
+
+ F13,
+ F14,
+ F15,
+ F16,
+ F17,
+ F18,
+ F19,
+ F20,
+ F21,
+ F22,
+ F23,
+ F24,
+
+ Open,
+ Help,
+ Properties,
+ Front,
+ Stop,
+ Repeat,
+ Undo,
+ Cut,
+ Copy,
+ Paste,
+ Find,
+ Mute,
+ VolumeUp,
+ VolumeDown,
+ CapsLockActive,
+ NumLockActive,
+ ScrollLockActive,
+ KPComma,
+
+ KPLeftParenthesis,
+ KPRightParenthesis,
+
+ LeftControlKey = 0xE0,
+ LeftShiftKey,
+ LeftAltKey,
+ LeftMetaKey,
+ RightControlKey,
+ RightShiftKey,
+ RightAltKey,
+ RightMetaKey,
+
+ MediaPlayPause,
+ MediaStopCD,
+ MediaPrevious,
+ MediaNext,
+ MediaEject,
+ MediaVolumeUp,
+ MediaVolumeDown,
+ MediaMute,
+ MediaWebsite,
+ MediaBack,
+ MediaForward,
+ MediaStop,
+ MediaFind,
+ MediaScrollUp,
+ MediaScrollDown,
+ MediaEdit,
+ MediaSleep,
+ MediaCoffee,
+ MediaRefresh,
+ MediaCalculator,
+
+ NumKeyboardKeys,
+};
+
+static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
+
+enum Modifiers {
+ LeftControl,
+ LeftShift,
+ LeftAlt,
+ LeftMeta,
+ RightControl,
+ RightShift,
+ RightAlt,
+ RightMeta,
+ CapsLock,
+ ScrollLock,
+ NumLock,
+
+ NumKeyboardMods,
+};
+
+constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
+constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
+constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
+
+constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
+constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
+constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
+
+} // namespace NativeKeyboard
+
+using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
+using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
+using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
+using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
+using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
+
+constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
+constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
+constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
+constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
+
+enum class ControllerType {
+ ProController,
+ DualJoyconDetached,
+ LeftJoycon,
+ RightJoycon,
+ Handheld,
+};
+
+struct PlayerInput {
+ bool connected;
+ ControllerType controller_type;
+ ButtonsRaw buttons;
+ AnalogsRaw analogs;
+ std::string lstick_mod;
+ std::string rstick_mod;
+
+ u32 body_color_left;
+ u32 body_color_right;
+ u32 button_color_left;
+ u32 button_color_right;
+};
+
+struct TouchscreenInput {
+ bool enabled;
+ std::string device;
+
+ u32 finger;
+ u32 diameter_x;
+ u32 diameter_y;
+ u32 rotation_angle;
+};
+} // namespace Settings
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
} else {
failure_callback();
}
- })
- .detach();
+ }).detach();
}
CalibrationConfigurationJob::CalibrationConfigurationJob(
@@ -279,8 +278,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
complete_event.Wait();
socket.Stop();
worker_thread.join();
- })
- .detach();
+ }).detach();
}
CalibrationConfigurationJob::~CalibrationConfigurationJob() {
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
index 8c6ef1394..4b347e47e 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.cpp
@@ -77,10 +77,11 @@ State::State() {
std::make_unique<Client>(status, Settings::values.udp_input_address,
Settings::values.udp_input_port, Settings::values.udp_pad_index);
- Input::RegisterFactory<Input::TouchDevice>("cemuhookudp",
- std::make_shared<UDPTouchFactory>(status));
- Input::RegisterFactory<Input::MotionDevice>("cemuhookudp",
- std::make_shared<UDPMotionFactory>(status));
+ motion_factory = std::make_shared<UDPMotionFactory>(status);
+ touch_factory = std::make_shared<UDPTouchFactory>(status);
+
+ Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", motion_factory);
+ Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", touch_factory);
}
State::~State() {
@@ -88,6 +89,11 @@ State::~State() {
Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
}
+std::vector<Common::ParamPackage> State::GetInputDevices() const {
+ // TODO support binding udp devices
+ return {};
+}
+
void State::ReloadUDPClient() {
client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port,
Settings::values.udp_pad_index);
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h
index 4f83f0441..672a5c812 100644
--- a/src/input_common/udp/udp.h
+++ b/src/input_common/udp/udp.h
@@ -5,19 +5,26 @@
#pragma once
#include <memory>
+#include <vector>
+#include "common/param_package.h"
namespace InputCommon::CemuhookUDP {
class Client;
+class UDPMotionFactory;
+class UDPTouchFactory;
class State {
public:
State();
~State();
void ReloadUDPClient();
+ std::vector<Common::ParamPackage> GetInputDevices() const;
private:
std::unique_ptr<Client> client;
+ std::shared_ptr<UDPMotionFactory> motion_factory;
+ std::shared_ptr<UDPTouchFactory> touch_factory;
};
std::unique_ptr<State> Init();
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 3cd896a0f..d85f1e9d1 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,3 +1,5 @@
+add_subdirectory(host_shaders)
+
add_library(video_core STATIC
buffer_cache/buffer_block.h
buffer_cache/buffer_cache.h
@@ -244,6 +246,9 @@ create_target_directory_groups(video_core)
target_link_libraries(video_core PUBLIC common core)
target_link_libraries(video_core PRIVATE glad xbyak)
+add_dependencies(video_core host_shaders)
+target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
+
if (ENABLE_VULKAN)
target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
target_compile_definitions(video_core PRIVATE HAS_VULKAN)
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index ff10ff40d..6e50661a3 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -10,7 +10,13 @@
namespace Tegra::Engines {
-Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {}
+Fermi2D::Fermi2D() = default;
+
+Fermi2D::~Fermi2D() = default;
+
+void Fermi2D::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
+ rasterizer = &rasterizer_;
+}
void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
ASSERT_MSG(method < Regs::NUM_REGS,
@@ -87,7 +93,7 @@ void Fermi2D::HandleSurfaceCopy() {
copy_config.src_rect = src_rect;
copy_config.dst_rect = dst_rect;
- if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst, copy_config)) {
+ if (!rasterizer->AccelerateSurfaceCopy(regs.src, regs.dst, copy_config)) {
UNIMPLEMENTED();
}
}
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 8f37d053f..213abfaae 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -34,8 +34,11 @@ namespace Tegra::Engines {
class Fermi2D final : public EngineInterface {
public:
- explicit Fermi2D(VideoCore::RasterizerInterface& rasterizer);
- ~Fermi2D() = default;
+ explicit Fermi2D();
+ ~Fermi2D();
+
+ /// Binds a rasterizer to this engine.
+ void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
/// Write the value to the register identified by method.
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
@@ -149,7 +152,7 @@ public:
};
private:
- VideoCore::RasterizerInterface& rasterizer;
+ VideoCore::RasterizerInterface* rasterizer;
/// Performs the copy from the source surface to the destination surface as configured in the
/// registers.
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index a82b06a38..898370739 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -16,14 +16,15 @@
namespace Tegra::Engines {
-KeplerCompute::KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
- MemoryManager& memory_manager)
- : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, upload_state{
- memory_manager,
- regs.upload} {}
+KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_)
+ : system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {}
KeplerCompute::~KeplerCompute() = default;
+void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
+ rasterizer = &rasterizer_;
+}
+
void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
ASSERT_MSG(method < Regs::NUM_REGS,
"Invalid KeplerCompute register, increase the size of the Regs structure");
@@ -104,11 +105,11 @@ SamplerDescriptor KeplerCompute::AccessSampler(u32 handle) const {
}
VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() {
- return rasterizer.AccessGuestDriverProfile();
+ return rasterizer->AccessGuestDriverProfile();
}
const VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() const {
- return rasterizer.AccessGuestDriverProfile();
+ return rasterizer->AccessGuestDriverProfile();
}
void KeplerCompute::ProcessLaunch() {
@@ -119,7 +120,7 @@ void KeplerCompute::ProcessLaunch() {
const GPUVAddr code_addr = regs.code_loc.Address() + launch_description.program_start;
LOG_TRACE(HW_GPU, "Compute invocation launched at address 0x{:016x}", code_addr);
- rasterizer.DispatchCompute(code_addr);
+ rasterizer->DispatchCompute(code_addr);
}
Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const {
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index b7f668d88..7f2500aab 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -42,10 +42,12 @@ namespace Tegra::Engines {
class KeplerCompute final : public ConstBufferEngineInterface, public EngineInterface {
public:
- explicit KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
- MemoryManager& memory_manager);
+ explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager);
~KeplerCompute();
+ /// Binds a rasterizer to this engine.
+ void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
+
static constexpr std::size_t NumConstBuffers = 8;
struct Regs {
@@ -230,11 +232,6 @@ public:
const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override;
private:
- Core::System& system;
- VideoCore::RasterizerInterface& rasterizer;
- MemoryManager& memory_manager;
- Upload::State upload_state;
-
void ProcessLaunch();
/// Retrieves information about a specific TIC entry from the TIC buffer.
@@ -242,6 +239,11 @@ private:
/// Retrieves information about a specific TSC entry from the TSC buffer.
Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
+
+ Core::System& system;
+ MemoryManager& memory_manager;
+ VideoCore::RasterizerInterface* rasterizer = nullptr;
+ Upload::State upload_state;
};
#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index c01436295..33854445f 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -22,14 +22,19 @@ using VideoCore::QueryType;
/// First register id that is actually a Macro call.
constexpr u32 MacroRegistersStart = 0xE00;
-Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
- MemoryManager& memory_manager)
- : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager},
- macro_engine{GetMacroEngine(*this)}, upload_state{memory_manager, regs.upload} {
+Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
+ : system{system_}, memory_manager{memory_manager_}, macro_engine{GetMacroEngine(*this)},
+ upload_state{memory_manager, regs.upload} {
dirty.flags.flip();
InitializeRegisterDefaults();
}
+Maxwell3D::~Maxwell3D() = default;
+
+void Maxwell3D::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
+ rasterizer = &rasterizer_;
+}
+
void Maxwell3D::InitializeRegisterDefaults() {
// Initializes registers to their default values - what games expect them to be at boot. This is
// for certain registers that may not be explicitly set by games.
@@ -192,7 +197,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
switch (method) {
case MAXWELL3D_REG_INDEX(wait_for_idle): {
- rasterizer.WaitForIdle();
+ rasterizer->WaitForIdle();
break;
}
case MAXWELL3D_REG_INDEX(shadow_ram_control): {
@@ -402,7 +407,7 @@ void Maxwell3D::FlushMMEInlineDraw() {
const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
if (ShouldExecute()) {
- rasterizer.Draw(is_indexed, true);
+ rasterizer->Draw(is_indexed, true);
}
// TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
@@ -465,7 +470,7 @@ void Maxwell3D::ProcessQueryGet() {
switch (regs.query.query_get.operation) {
case Regs::QueryOperation::Release:
if (regs.query.query_get.fence == 1) {
- rasterizer.SignalSemaphore(regs.query.QueryAddress(), regs.query.query_sequence);
+ rasterizer->SignalSemaphore(regs.query.QueryAddress(), regs.query.query_sequence);
} else {
StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0);
}
@@ -533,7 +538,7 @@ void Maxwell3D::ProcessQueryCondition() {
void Maxwell3D::ProcessCounterReset() {
switch (regs.counter_reset) {
case Regs::CounterReset::SampleCnt:
- rasterizer.ResetCounter(QueryType::SamplesPassed);
+ rasterizer->ResetCounter(QueryType::SamplesPassed);
break;
default:
LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}",
@@ -547,7 +552,7 @@ void Maxwell3D::ProcessSyncPoint() {
const u32 increment = regs.sync_info.increment.Value();
[[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value();
if (increment) {
- rasterizer.SignalSyncPoint(sync_point);
+ rasterizer->SignalSyncPoint(sync_point);
}
}
@@ -570,7 +575,7 @@ void Maxwell3D::DrawArrays() {
const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
if (ShouldExecute()) {
- rasterizer.Draw(is_indexed, false);
+ rasterizer->Draw(is_indexed, false);
}
// TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
@@ -590,8 +595,8 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
return 0;
case Regs::QuerySelect::SamplesPassed:
// Deferred.
- rasterizer.Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed,
- system.GPU().GetTicks());
+ rasterizer->Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed,
+ system.GPU().GetTicks());
return {};
default:
LOG_DEBUG(HW_GPU, "Unimplemented query select type {}",
@@ -718,7 +723,7 @@ void Maxwell3D::ProcessClearBuffers() {
regs.clear_buffers.R == regs.clear_buffers.B &&
regs.clear_buffers.R == regs.clear_buffers.A);
- rasterizer.Clear();
+ rasterizer->Clear();
}
u32 Maxwell3D::AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const {
@@ -752,11 +757,11 @@ SamplerDescriptor Maxwell3D::AccessSampler(u32 handle) const {
}
VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() {
- return rasterizer.AccessGuestDriverProfile();
+ return rasterizer->AccessGuestDriverProfile();
}
const VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() const {
- return rasterizer.AccessGuestDriverProfile();
+ return rasterizer->AccessGuestDriverProfile();
}
} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index ef1618990..bc289c55d 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -51,9 +51,11 @@ namespace Tegra::Engines {
class Maxwell3D final : public ConstBufferEngineInterface, public EngineInterface {
public:
- explicit Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
- MemoryManager& memory_manager);
- ~Maxwell3D() = default;
+ explicit Maxwell3D(Core::System& system, MemoryManager& memory_manager);
+ ~Maxwell3D();
+
+ /// Binds a rasterizer to this engine.
+ void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
/// Register structure of the Maxwell3D engine.
/// TODO(Subv): This structure will need to be made bigger as more registers are discovered.
@@ -647,7 +649,7 @@ public:
GetX() + GetWidth(), // right
GetY() // bottom
};
- };
+ }
f32 GetX() const {
return std::max(0.0f, translate_x - std::fabs(scale_x));
@@ -1418,12 +1420,12 @@ public:
return execute_on;
}
- VideoCore::RasterizerInterface& GetRasterizer() {
- return rasterizer;
+ VideoCore::RasterizerInterface& Rasterizer() {
+ return *rasterizer;
}
- const VideoCore::RasterizerInterface& GetRasterizer() const {
- return rasterizer;
+ const VideoCore::RasterizerInterface& Rasterizer() const {
+ return *rasterizer;
}
/// Notify a memory write has happened.
@@ -1460,11 +1462,10 @@ private:
void InitializeRegisterDefaults();
Core::System& system;
-
- VideoCore::RasterizerInterface& rasterizer;
-
MemoryManager& memory_manager;
+ VideoCore::RasterizerInterface* rasterizer = nullptr;
+
/// Start offsets of each macro in macro_memory
std::array<u32, 0x80> macro_positions = {};
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() {
}
void MaxwellDMA::CopyBlockLinearToPitch() {
- ASSERT(regs.src_params.block_size.depth == 0);
+ UNIMPLEMENTED_IF(regs.src_params.block_size.depth != 0);
+ UNIMPLEMENTED_IF(regs.src_params.layer != 0);
// Optimized path for micro copies.
const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
@@ -123,17 +124,12 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
write_buffer.resize(dst_size);
}
- if (Settings::IsGPULevelExtreme()) {
- memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
- memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
- } else {
- memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
- memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
- }
+ memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
+ memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel,
- read_buffer.data() + src_layer_size * src_params.layer, write_buffer.data(),
- block_height, src_params.origin.x, src_params.origin.y);
+ block_height, src_params.origin.x, src_params.origin.y, write_buffer.data(),
+ read_buffer.data());
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
}
@@ -198,7 +194,6 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
if (read_buffer.size() < src_size) {
read_buffer.resize(src_size);
}
-
if (write_buffer.size() < dst_size) {
write_buffer.resize(dst_size);
}
@@ -212,8 +207,8 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
}
UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width,
- bytes_per_pixel, read_buffer.data(), write_buffer.data(),
- regs.src_params.block_size.height, pos_x, pos_y);
+ bytes_per_pixel, regs.src_params.block_size.height, pos_x, pos_y,
+ write_buffer.data(), read_buffer.data());
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
}
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index 8b2a6a42c..06cc12d5a 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -5,15 +5,10 @@
#pragma once
#include <algorithm>
-#include <array>
-#include <memory>
#include <queue>
-#include "common/assert.h"
#include "common/common_types.h"
#include "core/core.h"
-#include "core/memory.h"
-#include "core/settings.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 512578c8b..acb6e6d46 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -27,21 +27,28 @@ namespace Tegra {
MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
-GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, bool is_async)
- : system{system}, renderer{std::move(renderer_)}, is_async{is_async} {
- auto& rasterizer{renderer->Rasterizer()};
- memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer);
- dma_pusher = std::make_unique<Tegra::DmaPusher>(system, *this);
- maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
- fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer);
- kepler_compute = std::make_unique<Engines::KeplerCompute>(system, rasterizer, *memory_manager);
- maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, *memory_manager);
- kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager);
- shader_notify = std::make_unique<VideoCore::ShaderNotify>();
-}
+GPU::GPU(Core::System& system_, bool is_async_)
+ : system{system_}, dma_pusher{std::make_unique<Tegra::DmaPusher>(system, *this)},
+ memory_manager{std::make_unique<Tegra::MemoryManager>(system)},
+ maxwell_3d{std::make_unique<Engines::Maxwell3D>(system, *memory_manager)},
+ fermi_2d{std::make_unique<Engines::Fermi2D>()},
+ kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)},
+ maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)},
+ kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)},
+ shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_} {}
GPU::~GPU() = default;
+void GPU::BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) {
+ renderer = std::move(renderer_);
+
+ VideoCore::RasterizerInterface& rasterizer = renderer->Rasterizer();
+ memory_manager->BindRasterizer(rasterizer);
+ maxwell_3d->BindRasterizer(rasterizer);
+ fermi_2d->BindRasterizer(rasterizer);
+ kepler_compute->BindRasterizer(rasterizer);
+}
+
Engines::Maxwell3D& GPU::Maxwell3D() {
return *maxwell_3d;
}
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index ebfc7b0c7..c7d11deb2 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -142,11 +142,6 @@ class MemoryManager;
class GPU {
public:
- explicit GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
- bool is_async);
-
- virtual ~GPU();
-
struct MethodCall {
u32 method{};
u32 argument{};
@@ -162,6 +157,12 @@ public:
method_count(method_count) {}
};
+ explicit GPU(Core::System& system, bool is_async);
+ virtual ~GPU();
+
+ /// Binds a renderer to the GPU.
+ void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer);
+
/// Calls a GPU method.
void CallMethod(const MethodCall& method_call);
@@ -345,8 +346,8 @@ private:
bool ExecuteMethodOnEngine(u32 method);
protected:
- std::unique_ptr<Tegra::DmaPusher> dma_pusher;
Core::System& system;
+ std::unique_ptr<Tegra::DmaPusher> dma_pusher;
std::unique_ptr<VideoCore::RendererBase> renderer;
private:
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index 7b855f63e..70a3d5738 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -10,16 +10,14 @@
namespace VideoCommon {
-GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_,
- std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
- : GPU(system, std::move(renderer_), true), gpu_thread{system},
- cpu_context(renderer->GetRenderWindow().CreateSharedContext()),
- gpu_context(std::move(context)) {}
+GPUAsynch::GPUAsynch(Core::System& system) : GPU{system, true}, gpu_thread{system} {}
GPUAsynch::~GPUAsynch() = default;
void GPUAsynch::Start() {
- gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher);
+ gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher);
+ cpu_context = renderer->GetRenderWindow().CreateSharedContext();
+ cpu_context->MakeCurrent();
}
void GPUAsynch::ObtainContext() {
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index 15e9f1d38..f89c855a5 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -20,8 +20,7 @@ namespace VideoCommon {
/// Implementation of GPU interface that runs the GPU asynchronously
class GPUAsynch final : public Tegra::GPU {
public:
- explicit GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
- std::unique_ptr<Core::Frontend::GraphicsContext>&& context);
+ explicit GPUAsynch(Core::System& system);
~GPUAsynch() override;
void Start() override;
@@ -42,7 +41,6 @@ protected:
private:
GPUThread::ThreadManager gpu_thread;
std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
- std::unique_ptr<Core::Frontend::GraphicsContext> gpu_context;
};
} // namespace VideoCommon
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
index aaeb9811d..1ca47ddef 100644
--- a/src/video_core/gpu_synch.cpp
+++ b/src/video_core/gpu_synch.cpp
@@ -7,20 +7,18 @@
namespace VideoCommon {
-GPUSynch::GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
- std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
- : GPU(system, std::move(renderer), false), context{std::move(context)} {}
+GPUSynch::GPUSynch(Core::System& system) : GPU{system, false} {}
GPUSynch::~GPUSynch() = default;
void GPUSynch::Start() {}
void GPUSynch::ObtainContext() {
- context->MakeCurrent();
+ renderer->Context().MakeCurrent();
}
void GPUSynch::ReleaseContext() {
- context->DoneCurrent();
+ renderer->Context().DoneCurrent();
}
void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 762c20aa5..297258cb1 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -19,8 +19,7 @@ namespace VideoCommon {
/// Implementation of GPU interface that runs the GPU synchronously
class GPUSynch final : public Tegra::GPU {
public:
- explicit GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
- std::unique_ptr<Core::Frontend::GraphicsContext>&& context);
+ explicit GPUSynch(Core::System& system);
~GPUSynch() override;
void Start() override;
@@ -36,9 +35,6 @@ public:
protected:
void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,
[[maybe_unused]] u32 value) const override {}
-
-private:
- std::unique_ptr<Core::Frontend::GraphicsContext> context;
};
} // namespace VideoCommon
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
new file mode 100644
index 000000000..aa62363a7
--- /dev/null
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -0,0 +1,43 @@
+set(SHADER_FILES
+ opengl_present.frag
+ opengl_present.vert
+)
+
+set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include)
+set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
+
+set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders)
+add_custom_command(
+ OUTPUT
+ ${SHADER_DIR}
+ COMMAND
+ ${CMAKE_COMMAND} -E make_directory ${SHADER_DIR}
+)
+
+set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in)
+set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake)
+
+foreach(FILENAME IN ITEMS ${SHADER_FILES})
+ string(REPLACE "." "_" SHADER_NAME ${FILENAME})
+ set(SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME})
+ set(HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}.h)
+ add_custom_command(
+ OUTPUT
+ ${HEADER_FILE}
+ COMMAND
+ ${CMAKE_COMMAND} -P ${HEADER_GENERATOR} ${SOURCE_FILE} ${HEADER_FILE} ${INPUT_FILE}
+ MAIN_DEPENDENCY
+ ${SOURCE_FILE}
+ DEPENDS
+ ${HEADER_GENERATOR}
+ ${INPUT_FILE}
+ )
+ set(SHADER_HEADERS ${SHADER_HEADERS} ${HEADER_FILE})
+endforeach()
+
+add_custom_target(host_shaders
+ DEPENDS
+ ${SHADER_HEADERS}
+ SOURCES
+ ${SHADER_FILES}
+)
diff --git a/src/video_core/host_shaders/StringShaderHeader.cmake b/src/video_core/host_shaders/StringShaderHeader.cmake
new file mode 100644
index 000000000..368bce0ed
--- /dev/null
+++ b/src/video_core/host_shaders/StringShaderHeader.cmake
@@ -0,0 +1,11 @@
+set(SOURCE_FILE ${CMAKE_ARGV3})
+set(HEADER_FILE ${CMAKE_ARGV4})
+set(INPUT_FILE ${CMAKE_ARGV5})
+
+get_filename_component(CONTENTS_NAME ${SOURCE_FILE} NAME)
+string(REPLACE "." "_" CONTENTS_NAME ${CONTENTS_NAME})
+string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME)
+
+file(READ ${SOURCE_FILE} CONTENTS)
+
+configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY)
diff --git a/src/video_core/host_shaders/opengl_present.frag b/src/video_core/host_shaders/opengl_present.frag
new file mode 100644
index 000000000..8a4cb024b
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_present.frag
@@ -0,0 +1,10 @@
+#version 430 core
+
+layout (location = 0) in vec2 frag_tex_coord;
+layout (location = 0) out vec4 color;
+
+layout (binding = 0) uniform sampler2D color_texture;
+
+void main() {
+ color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
+}
diff --git a/src/video_core/host_shaders/opengl_present.vert b/src/video_core/host_shaders/opengl_present.vert
new file mode 100644
index 000000000..2235d31a4
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_present.vert
@@ -0,0 +1,24 @@
+#version 430 core
+
+out gl_PerVertex {
+ vec4 gl_Position;
+};
+
+layout (location = 0) in vec2 vert_position;
+layout (location = 1) in vec2 vert_tex_coord;
+layout (location = 0) out vec2 frag_tex_coord;
+
+// This is a truncated 3x3 matrix for 2D transformations:
+// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
+// The third column performs translation.
+// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
+// implicitly be [0, 0, 1]
+layout (location = 0) uniform mat3x2 modelview_matrix;
+
+void main() {
+ // Multiply input position by the rotscale part of the matrix and then manually translate by
+ // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
+ // to `vec3(vert_position.xy, 1.0)`
+ gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
+ frag_tex_coord = vert_tex_coord;
+}
diff --git a/src/video_core/host_shaders/source_shader.h.in b/src/video_core/host_shaders/source_shader.h.in
new file mode 100644
index 000000000..ccdb0d2a9
--- /dev/null
+++ b/src/video_core/host_shaders/source_shader.h.in
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <string_view>
+
+namespace HostShaders {
+
+constexpr std::string_view @CONTENTS_NAME@ = R"(@CONTENTS@)";
+
+} // namespace HostShaders
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 0c9ff59a4..df00b57df 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -24,7 +24,7 @@ void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
maxwell3d.regs.index_array.first = parameters[4];
if (maxwell3d.ShouldExecute()) {
- maxwell3d.GetRasterizer().Draw(true, true);
+ maxwell3d.Rasterizer().Draw(true, true);
}
maxwell3d.regs.index_array.count = 0;
maxwell3d.mme_draw.instance_count = 0;
@@ -42,7 +42,7 @@ void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
maxwell3d.mme_draw.instance_count = count;
if (maxwell3d.ShouldExecute()) {
- maxwell3d.GetRasterizer().Draw(false, true);
+ maxwell3d.Rasterizer().Draw(false, true);
}
maxwell3d.regs.vertex_buffer.count = 0;
maxwell3d.mme_draw.instance_count = 0;
@@ -65,7 +65,7 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
maxwell3d.regs.draw.topology.Assign(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
if (maxwell3d.ShouldExecute()) {
- maxwell3d.GetRasterizer().Draw(true, true);
+ maxwell3d.Rasterizer().Draw(true, true);
}
maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base?
maxwell3d.regs.index_array.count = 0;
diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp
index aa5256419..bd01fd1f2 100644
--- a/src/video_core/macro/macro_interpreter.cpp
+++ b/src/video_core/macro/macro_interpreter.cpp
@@ -34,7 +34,6 @@ void MacroInterpreterImpl::Execute(const std::vector<u32>& parameters, u32 metho
this->parameters = std::make_unique<u32[]>(num_parameters);
}
std::memcpy(this->parameters.get(), parameters.data(), num_parameters * sizeof(u32));
- this->num_parameters = num_parameters;
// Execute the code until we hit an exit condition.
bool keep_executing = true;
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 844164645..16b2aaa27 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -14,11 +14,15 @@
namespace Tegra {
-MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
- : system{system}, rasterizer{rasterizer}, page_table(page_table_size) {}
+MemoryManager::MemoryManager(Core::System& system_)
+ : system{system_}, page_table(page_table_size) {}
MemoryManager::~MemoryManager() = default;
+void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
+ rasterizer = &rasterizer_;
+}
+
GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
u64 remaining_size{size};
for (u64 offset{}; offset < size; offset += page_size) {
@@ -217,7 +221,7 @@ void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::siz
// Flush must happen on the rasterizer interface, such that memory is always synchronous
// when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
- rasterizer.FlushRegion(src_addr, copy_amount);
+ rasterizer->FlushRegion(src_addr, copy_amount);
system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
}
@@ -266,7 +270,7 @@ void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, s
// Invalidate must happen on the rasterizer interface, such that memory is always
// synchronous when it is written (even when in asynchronous GPU mode).
- rasterizer.InvalidateRegion(dest_addr, copy_amount);
+ rasterizer->InvalidateRegion(dest_addr, copy_amount);
system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
}
@@ -312,10 +316,10 @@ void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_add
WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size);
}
-bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) {
+bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
const auto cpu_addr{GpuToCpuAddress(gpu_addr)};
if (!cpu_addr) {
- return {};
+ return false;
}
const std::size_t page{(*cpu_addr & Core::Memory::PAGE_MASK) + size};
return page <= Core::Memory::PAGE_SIZE;
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 681bd9588..53c8d122a 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -31,19 +31,19 @@ public:
constexpr PageEntry(State state) : state{state} {}
constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {}
- constexpr bool IsUnmapped() const {
+ [[nodiscard]] constexpr bool IsUnmapped() const {
return state == State::Unmapped;
}
- constexpr bool IsAllocated() const {
+ [[nodiscard]] constexpr bool IsAllocated() const {
return state == State::Allocated;
}
- constexpr bool IsValid() const {
+ [[nodiscard]] constexpr bool IsValid() const {
return !IsUnmapped() && !IsAllocated();
}
- constexpr VAddr ToAddress() const {
+ [[nodiscard]] constexpr VAddr ToAddress() const {
if (!IsValid()) {
return {};
}
@@ -51,7 +51,7 @@ public:
return static_cast<VAddr>(state) << ShiftBits;
}
- constexpr PageEntry operator+(u64 offset) {
+ [[nodiscard]] constexpr PageEntry operator+(u64 offset) const {
// If this is a reserved value, offsets do not apply
if (!IsValid()) {
return *this;
@@ -68,19 +68,22 @@ static_assert(sizeof(PageEntry) == 4, "PageEntry is too large");
class MemoryManager final {
public:
- explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer);
+ explicit MemoryManager(Core::System& system);
~MemoryManager();
- std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
+ /// Binds a renderer to the memory manager.
+ void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
+
+ [[nodiscard]] std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
template <typename T>
- T Read(GPUVAddr addr) const;
+ [[nodiscard]] T Read(GPUVAddr addr) const;
template <typename T>
void Write(GPUVAddr addr, T data);
- u8* GetPointer(GPUVAddr addr);
- const u8* GetPointer(GPUVAddr addr) const;
+ [[nodiscard]] u8* GetPointer(GPUVAddr addr);
+ [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const;
/**
* ReadBlock and WriteBlock are full read and write operations over virtual
@@ -109,24 +112,24 @@ public:
/**
* IsGranularRange checks if a gpu region can be simply read with a pointer.
*/
- bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size);
+ [[nodiscard]] bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const;
- GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size);
- GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align);
- std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size);
- GPUVAddr Allocate(std::size_t size, std::size_t align);
+ [[nodiscard]] GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size);
+ [[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align);
+ [[nodiscard]] std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size);
+ [[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align);
void Unmap(GPUVAddr gpu_addr, std::size_t size);
private:
- PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
+ [[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size);
GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size);
- std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const;
+ [[nodiscard]] std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const;
void TryLockPage(PageEntry page_entry, std::size_t size);
void TryUnlockPage(PageEntry page_entry, std::size_t size);
- static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
+ [[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
return (gpu_addr >> page_bits) & page_table_mask;
}
@@ -141,7 +144,7 @@ private:
Core::System& system;
- VideoCore::RasterizerInterface& rasterizer;
+ VideoCore::RasterizerInterface* rasterizer = nullptr;
std::vector<PageEntry> page_table;
};
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index dfb06e87e..a93a1732c 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -9,7 +9,9 @@
namespace VideoCore {
-RendererBase::RendererBase(Core::Frontend::EmuWindow& window) : render_window{window} {
+RendererBase::RendererBase(Core::Frontend::EmuWindow& window_,
+ std::unique_ptr<Core::Frontend::GraphicsContext> context_)
+ : render_window{window_}, context{std::move(context_)} {
RefreshBaseSettings();
}
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 1d85219b6..649074acd 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -15,7 +15,8 @@
namespace Core::Frontend {
class EmuWindow;
-}
+class GraphicsContext;
+} // namespace Core::Frontend
namespace VideoCore {
@@ -25,14 +26,15 @@ struct RendererSettings {
// Screenshot
std::atomic<bool> screenshot_requested{false};
- void* screenshot_bits;
+ void* screenshot_bits{};
std::function<void()> screenshot_complete_callback;
Layout::FramebufferLayout screenshot_framebuffer_layout;
};
class RendererBase : NonCopyable {
public:
- explicit RendererBase(Core::Frontend::EmuWindow& window);
+ explicit RendererBase(Core::Frontend::EmuWindow& window,
+ std::unique_ptr<Core::Frontend::GraphicsContext> context);
virtual ~RendererBase();
/// Initialize the renderer
@@ -68,6 +70,14 @@ public:
return *rasterizer;
}
+ Core::Frontend::GraphicsContext& Context() {
+ return *context;
+ }
+
+ const Core::Frontend::GraphicsContext& Context() const {
+ return *context;
+ }
+
Core::Frontend::EmuWindow& GetRenderWindow() {
return render_window;
}
@@ -94,6 +104,7 @@ public:
protected:
Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
std::unique_ptr<RasterizerInterface> rasterizer;
+ std::unique_ptr<Core::Frontend::GraphicsContext> context;
f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer
int m_current_frame = 0; ///< Current frame, should be set by the renderer
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp
index ec5421afa..3d2588dd2 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp
@@ -4,16 +4,17 @@
#include "common/assert.h"
+#include <glad/glad.h>
+
#include "video_core/renderer_opengl/gl_buffer_cache.h"
#include "video_core/renderer_opengl/gl_fence_manager.h"
namespace OpenGL {
-GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed)
- : VideoCommon::FenceBase(payload, is_stubbed), sync_object{} {}
+GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed) : FenceBase(payload, is_stubbed) {}
GLInnerFence::GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed)
- : VideoCommon::FenceBase(address, payload, is_stubbed), sync_object{} {}
+ : FenceBase(address, payload, is_stubbed) {}
GLInnerFence::~GLInnerFence() = default;
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h
index c917b3343..1686cf5c8 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.h
+++ b/src/video_core/renderer_opengl/gl_fence_manager.h
@@ -5,7 +5,6 @@
#pragma once
#include <memory>
-#include <glad/glad.h>
#include "common/common_types.h"
#include "video_core/fence_manager.h"
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index cb284db77..4af5824cd 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -177,15 +177,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
}
if (device.UseAsynchronousShaders()) {
- // Max worker threads we should allow
- constexpr u32 MAX_THREADS = 4;
- // Deduce how many threads we can use
- const u32 threads_used = std::thread::hardware_concurrency() / 4;
- // Always allow at least 1 thread regardless of our settings
- const auto max_worker_count = std::max(1U, threads_used);
- // Don't use more than MAX_THREADS
- const auto worker_count = std::min(max_worker_count, MAX_THREADS);
- async_shaders.AllocateWorkers(worker_count);
+ async_shaders.AllocateWorkers();
}
}
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index a787e27d2..0ebcec427 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <string_view>
#include <utility>
#include <glad/glad.h>
#include "common/common_types.h"
@@ -82,11 +83,13 @@ void OGLSampler::Release() {
handle = 0;
}
-void OGLShader::Create(const char* source, GLenum type) {
- if (handle != 0)
+void OGLShader::Create(std::string_view source, GLenum type) {
+ if (handle != 0) {
return;
- if (source == nullptr)
+ }
+ if (source.empty()) {
return;
+ }
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
handle = GLShader::LoadShader(source, type);
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index b05cb641c..f48398669 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -4,6 +4,7 @@
#pragma once
+#include <string_view>
#include <utility>
#include <glad/glad.h>
#include "common/common_types.h"
@@ -127,7 +128,7 @@ public:
return *this;
}
- void Create(const char* source, GLenum type);
+ void Create(std::string_view source, GLenum type);
void Release();
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index be71e1733..a07d56ef0 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -22,6 +22,7 @@
#include "video_core/memory_manager.h"
#include "video_core/renderer_opengl/gl_arb_decompiler.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
+#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_cache.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
@@ -403,7 +404,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
}
};
- const auto num_workers{static_cast<std::size_t>(std::thread::hardware_concurrency() + 1ULL)};
+ const std::size_t num_workers{std::max(1U, std::thread::hardware_concurrency())};
const std::size_t bucket_size{transferable->size() / num_workers};
std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers);
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..40c0877c1 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -73,7 +73,7 @@ ShaderDiskCacheEntry::ShaderDiskCacheEntry() = default;
ShaderDiskCacheEntry::~ShaderDiskCacheEntry() = default;
-bool ShaderDiskCacheEntry::Load(FileUtil::IOFile& file) {
+bool ShaderDiskCacheEntry::Load(Common::FS::IOFile& file) {
if (file.ReadBytes(&type, sizeof(u32)) != sizeof(u32)) {
return false;
}
@@ -144,7 +144,7 @@ bool ShaderDiskCacheEntry::Load(FileUtil::IOFile& file) {
return true;
}
-bool ShaderDiskCacheEntry::Save(FileUtil::IOFile& file) const {
+bool ShaderDiskCacheEntry::Save(Common::FS::IOFile& file) const {
if (file.WriteObject(static_cast<u32>(type)) != 1 ||
file.WriteObject(static_cast<u32>(code.size())) != 1 ||
file.WriteObject(static_cast<u32>(code_b.size())) != 1) {
@@ -214,20 +214,20 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
// Skip games without title id
const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) {
- return {};
+ return std::nullopt;
}
- FileUtil::IOFile file(GetTransferablePath(), "rb");
+ Common::FS::IOFile file(GetTransferablePath(), "rb");
if (!file.IsOpen()) {
LOG_INFO(Render_OpenGL, "No transferable shader cache found");
is_usable = true;
- return {};
+ return std::nullopt;
}
u32 version{};
if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) {
LOG_ERROR(Render_OpenGL, "Failed to get transferable cache version, skipping it");
- return {};
+ return std::nullopt;
}
if (version < NativeVersion) {
@@ -235,12 +235,12 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
file.Close();
InvalidateTransferable();
is_usable = true;
- return {};
+ return std::nullopt;
}
if (version > NativeVersion) {
LOG_WARNING(Render_OpenGL, "Transferable shader cache was generated with a newer version "
"of the emulator, skipping");
- return {};
+ return std::nullopt;
}
// Version is valid, load the shaders
@@ -249,7 +249,7 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
ShaderDiskCacheEntry& entry = entries.emplace_back();
if (!entry.Load(file)) {
LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry, skipping");
- return {};
+ return std::nullopt;
}
}
@@ -262,7 +262,7 @@ std::vector<ShaderDiskCachePrecompiled> ShaderDiskCacheOpenGL::LoadPrecompiled()
return {};
}
- FileUtil::IOFile file(GetPrecompiledPath(), "rb");
+ Common::FS::IOFile file(GetPrecompiledPath(), "rb");
if (!file.IsOpen()) {
LOG_INFO(Render_OpenGL, "No precompiled shader cache found");
return {};
@@ -279,7 +279,7 @@ std::vector<ShaderDiskCachePrecompiled> ShaderDiskCacheOpenGL::LoadPrecompiled()
}
std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::LoadPrecompiledFile(
- FileUtil::IOFile& file) {
+ Common::FS::IOFile& file) {
// Read compressed file from disk and decompress to virtual precompiled cache file
std::vector<u8> compressed(file.GetSize());
file.ReadBytes(compressed.data(), compressed.size());
@@ -290,12 +290,12 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo
ShaderCacheVersionHash file_hash{};
if (!LoadArrayFromPrecompiled(file_hash.data(), file_hash.size())) {
precompiled_cache_virtual_file_offset = 0;
- return {};
+ return std::nullopt;
}
if (GetShaderCacheVersionHash() != file_hash) {
LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator");
precompiled_cache_virtual_file_offset = 0;
- return {};
+ return std::nullopt;
}
std::vector<ShaderDiskCachePrecompiled> entries;
@@ -305,19 +305,20 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo
if (!LoadObjectFromPrecompiled(entry.unique_identifier) ||
!LoadObjectFromPrecompiled(entry.binary_format) ||
!LoadObjectFromPrecompiled(binary_size)) {
- return {};
+ return std::nullopt;
}
entry.binary.resize(binary_size);
if (!LoadArrayFromPrecompiled(entry.binary.data(), entry.binary.size())) {
- return {};
+ return std::nullopt;
}
}
- return entries;
+
+ return std::move(entries);
}
void ShaderDiskCacheOpenGL::InvalidateTransferable() {
- if (!FileUtil::Delete(GetTransferablePath())) {
+ if (!Common::FS::Delete(GetTransferablePath())) {
LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}",
GetTransferablePath());
}
@@ -328,7 +329,7 @@ void ShaderDiskCacheOpenGL::InvalidatePrecompiled() {
// Clear virtaul precompiled cache file
precompiled_cache_virtual_file.Resize(0);
- if (!FileUtil::Delete(GetPrecompiledPath())) {
+ if (!Common::FS::Delete(GetPrecompiledPath())) {
LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath());
}
}
@@ -344,7 +345,7 @@ void ShaderDiskCacheOpenGL::SaveEntry(const ShaderDiskCacheEntry& entry) {
return;
}
- FileUtil::IOFile file = AppendTransferableFile();
+ Common::FS::IOFile file = AppendTransferableFile();
if (!file.IsOpen()) {
return;
}
@@ -386,15 +387,15 @@ void ShaderDiskCacheOpenGL::SavePrecompiled(u64 unique_identifier, GLuint progra
}
}
-FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const {
+Common::FS::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const {
if (!EnsureDirectories()) {
return {};
}
const auto transferable_path{GetTransferablePath()};
- const bool existed = FileUtil::Exists(transferable_path);
+ const bool existed = Common::FS::Exists(transferable_path);
- FileUtil::IOFile file(transferable_path, "ab");
+ Common::FS::IOFile file(transferable_path, "ab");
if (!file.IsOpen()) {
LOG_ERROR(Render_OpenGL, "Failed to open transferable cache in path={}", transferable_path);
return {};
@@ -426,7 +427,7 @@ void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() {
Common::Compression::CompressDataZSTDDefault(uncompressed.data(), uncompressed.size());
const auto precompiled_path{GetPrecompiledPath()};
- FileUtil::IOFile file(precompiled_path, "wb");
+ Common::FS::IOFile file(precompiled_path, "wb");
if (!file.IsOpen()) {
LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path);
@@ -440,24 +441,24 @@ void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() {
bool ShaderDiskCacheOpenGL::EnsureDirectories() const {
const auto CreateDir = [](const std::string& dir) {
- if (!FileUtil::CreateDir(dir)) {
+ if (!Common::FS::CreateDir(dir)) {
LOG_ERROR(Render_OpenGL, "Failed to create directory={}", dir);
return false;
}
return true;
};
- return CreateDir(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) &&
+ return CreateDir(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)) &&
CreateDir(GetBaseDir()) && CreateDir(GetTransferableDir()) &&
CreateDir(GetPrecompiledDir());
}
std::string ShaderDiskCacheOpenGL::GetTransferablePath() const {
- return FileUtil::SanitizePath(GetTransferableDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
+ return Common::FS::SanitizePath(GetTransferableDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
}
std::string ShaderDiskCacheOpenGL::GetPrecompiledPath() const {
- return FileUtil::SanitizePath(GetPrecompiledDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
+ return Common::FS::SanitizePath(GetPrecompiledDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
}
std::string ShaderDiskCacheOpenGL::GetTransferableDir() const {
@@ -469,7 +470,7 @@ std::string ShaderDiskCacheOpenGL::GetPrecompiledDir() const {
}
std::string ShaderDiskCacheOpenGL::GetBaseDir() const {
- return FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + DIR_SEP "opengl";
+ return Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir) + DIR_SEP "opengl";
}
std::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 {
class System;
}
-namespace FileUtil {
+namespace Common::FS {
class IOFile;
}
@@ -38,9 +38,9 @@ struct ShaderDiskCacheEntry {
ShaderDiskCacheEntry();
~ShaderDiskCacheEntry();
- bool Load(FileUtil::IOFile& file);
+ bool Load(Common::FS::IOFile& file);
- bool Save(FileUtil::IOFile& file) const;
+ bool Save(Common::FS::IOFile& file) const;
bool HasProgramA() const {
return !code.empty() && !code_b.empty();
@@ -97,10 +97,10 @@ public:
private:
/// Loads the transferable cache. Returns empty on failure.
std::optional<std::vector<ShaderDiskCachePrecompiled>> LoadPrecompiledFile(
- FileUtil::IOFile& file);
+ Common::FS::IOFile& file);
/// Opens current game's transferable file and write it's header if it doesn't exist
- FileUtil::IOFile AppendTransferableFile() const;
+ Common::FS::IOFile AppendTransferableFile() const;
/// Save precompiled header to precompiled_cache_in_memory
void SavePrecompiledHeaderToVirtualPrecompiledCache();
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index 9e74eda0d..4bf0d6090 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <string_view>
#include <vector>
#include <glad/glad.h>
#include "common/assert.h"
@@ -11,7 +12,8 @@
namespace OpenGL::GLShader {
namespace {
-const char* GetStageDebugName(GLenum type) {
+
+std::string_view StageDebugName(GLenum type) {
switch (type) {
case GL_VERTEX_SHADER:
return "vertex";
@@ -25,12 +27,17 @@ const char* GetStageDebugName(GLenum type) {
UNIMPLEMENTED();
return "unknown";
}
+
} // Anonymous namespace
-GLuint LoadShader(const char* source, GLenum type) {
- const char* debug_type = GetStageDebugName(type);
+GLuint LoadShader(std::string_view source, GLenum type) {
+ const std::string_view debug_type = StageDebugName(type);
const GLuint shader_id = glCreateShader(type);
- glShaderSource(shader_id, 1, &source, nullptr);
+
+ const GLchar* source_string = source.data();
+ const GLint source_length = static_cast<GLint>(source.size());
+
+ glShaderSource(shader_id, 1, &source_string, &source_length);
LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type);
glCompileShader(shader_id);
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h
index 03b7548c2..1b770532e 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.h
+++ b/src/video_core/renderer_opengl/gl_shader_util.h
@@ -38,7 +38,7 @@ void LogShaderSource(T... shaders) {
* @param source String of the GLSL shader program
* @param type Type of the shader (GL_VERTEX_SHADER, GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER)
*/
-GLuint LoadShader(const char* source, GLenum type);
+GLuint LoadShader(std::string_view source, GLenum type);
/**
* Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader)
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 0a7bc9e2b..f403f388a 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -403,7 +403,7 @@ void CachedSurface::DecorateSurfaceName() {
LabelGLObject(GL_TEXTURE, texture.handle, GetGpuAddr(), params.TargetName());
}
-void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, std::string prefix) {
+void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, const std::string& prefix) {
LabelGLObject(GL_TEXTURE, main_view.handle, gpu_addr, prefix);
}
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index bfc4ddf5d..de8f18489 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -90,7 +90,7 @@ public:
Tegra::Texture::SwizzleSource z_source,
Tegra::Texture::SwizzleSource w_source);
- void DecorateViewName(GPUVAddr gpu_addr, std::string prefix);
+ void DecorateViewName(GPUVAddr gpu_addr, const std::string& prefix);
void MarkAsModified(u64 tick) {
surface.MarkAsModified(true, tick);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 52e9e8250..b759c2dba 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -21,6 +21,8 @@
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
+#include "video_core/host_shaders/opengl_present_frag.h"
+#include "video_core/host_shaders/opengl_present_vert.h"
#include "video_core/morton.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -44,46 +46,6 @@ struct Frame {
bool is_srgb{}; /// Framebuffer is sRGB or RGB
};
-constexpr char VERTEX_SHADER[] = R"(
-#version 430 core
-
-out gl_PerVertex {
- vec4 gl_Position;
-};
-
-layout (location = 0) in vec2 vert_position;
-layout (location = 1) in vec2 vert_tex_coord;
-layout (location = 0) out vec2 frag_tex_coord;
-
-// This is a truncated 3x3 matrix for 2D transformations:
-// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
-// The third column performs translation.
-// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
-// implicitly be [0, 0, 1]
-layout (location = 0) uniform mat3x2 modelview_matrix;
-
-void main() {
- // Multiply input position by the rotscale part of the matrix and then manually translate by
- // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
- // to `vec3(vert_position.xy, 1.0)`
- gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
- frag_tex_coord = vert_tex_coord;
-}
-)";
-
-constexpr char FRAGMENT_SHADER[] = R"(
-#version 430 core
-
-layout (location = 0) in vec2 frag_tex_coord;
-layout (location = 0) out vec4 color;
-
-layout (binding = 0) uniform sampler2D color_texture;
-
-void main() {
- color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
-}
-)";
-
constexpr GLint PositionLocation = 0;
constexpr GLint TexCoordLocation = 1;
constexpr GLint ModelViewMatrixLocation = 0;
@@ -313,10 +275,11 @@ public:
}
};
-RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system,
- Core::Frontend::GraphicsContext& context)
- : RendererBase{emu_window}, emu_window{emu_window}, system{system}, context{context},
- program_manager{device}, has_debug_tool{HasDebugTool()} {}
+RendererOpenGL::RendererOpenGL(Core::System& system_, Core::Frontend::EmuWindow& emu_window_,
+ Tegra::GPU& gpu_,
+ std::unique_ptr<Core::Frontend::GraphicsContext> context_)
+ : RendererBase{emu_window_, std::move(context_)}, system{system_},
+ emu_window{emu_window_}, gpu{gpu_}, program_manager{device}, has_debug_tool{HasDebugTool()} {}
RendererOpenGL::~RendererOpenGL() = default;
@@ -384,7 +347,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
if (has_debug_tool) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
Present(0);
- context.SwapBuffers();
+ context->SwapBuffers();
}
}
@@ -460,10 +423,10 @@ void RendererOpenGL::InitOpenGLObjects() {
// Create shader programs
OGLShader vertex_shader;
- vertex_shader.Create(VERTEX_SHADER, GL_VERTEX_SHADER);
+ vertex_shader.Create(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
OGLShader fragment_shader;
- fragment_shader.Create(FRAGMENT_SHADER, GL_FRAGMENT_SHADER);
+ fragment_shader.Create(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER);
vertex_program.Create(true, false, vertex_shader.handle);
fragment_program.Create(true, false, fragment_shader.handle);
@@ -509,9 +472,10 @@ void RendererOpenGL::AddTelemetryFields() {
LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);
auto& telemetry_session = system.TelemetrySession();
- telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_Vendor", gpu_vendor);
- telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model);
- telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_OpenGL_Version", gl_version);
+ constexpr auto user_system = Common::Telemetry::FieldType::UserSystem;
+ telemetry_session.AddField(user_system, "GPU_Vendor", gpu_vendor);
+ telemetry_session.AddField(user_system, "GPU_Model", gpu_model);
+ telemetry_session.AddField(user_system, "GPU_OpenGL_Version", gl_version);
}
void RendererOpenGL::CreateRasterizer() {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 8b18d32e6..52ea76b7d 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -56,8 +56,9 @@ class FrameMailbox;
class RendererOpenGL final : public VideoCore::RendererBase {
public:
- explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system,
- Core::Frontend::GraphicsContext& context);
+ explicit RendererOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
+ Tegra::GPU& gpu,
+ std::unique_ptr<Core::Frontend::GraphicsContext> context);
~RendererOpenGL() override;
bool Init() override;
@@ -93,9 +94,9 @@ private:
bool Present(int timeout_ms);
- Core::Frontend::EmuWindow& emu_window;
Core::System& system;
- Core::Frontend::GraphicsContext& context;
+ Core::Frontend::EmuWindow& emu_window;
+ Tegra::GPU& gpu;
const Device device;
StateTracker state_tracker{system};
@@ -120,7 +121,7 @@ private:
std::vector<u8> gl_framebuffer_data;
/// Used for transforming the framebuffer orientation
- Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags;
+ Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{};
Common::Rectangle<int> framebuffer_crop_rect;
/// Frame presentation mailbox
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
index 435c8c1b8..5b01020ec 100644
--- a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
+++ b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
@@ -65,10 +65,10 @@ bool NsightAftermathTracker::Initialize() {
return false;
}
- dump_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "gpucrash";
+ dump_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir) + "gpucrash";
- (void)FileUtil::DeleteDirRecursively(dump_dir);
- if (!FileUtil::CreateDir(dump_dir)) {
+ (void)Common::FS::DeleteDirRecursively(dump_dir);
+ if (!Common::FS::CreateDir(dump_dir)) {
LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory");
return false;
}
@@ -106,7 +106,7 @@ void NsightAftermathTracker::SaveShader(const std::vector<u32>& spirv) const {
return;
}
- FileUtil::IOFile file(fmt::format("{}/source_{:016x}.spv", dump_dir, hash.hash), "wb");
+ Common::FS::IOFile file(fmt::format("{}/source_{:016x}.spv", dump_dir, hash.hash), "wb");
if (!file.IsOpen()) {
LOG_ERROR(Render_Vulkan, "Failed to dump SPIR-V module with hash={:016x}", hash.hash);
return;
@@ -156,12 +156,12 @@ void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump,
}();
std::string_view dump_view(static_cast<const char*>(gpu_crash_dump), gpu_crash_dump_size);
- if (FileUtil::WriteStringToFile(false, base_name, dump_view) != gpu_crash_dump_size) {
+ if (Common::FS::WriteStringToFile(false, base_name, dump_view) != gpu_crash_dump_size) {
LOG_ERROR(Render_Vulkan, "Failed to write dump file");
return;
}
const std::string_view json_view(json.data(), json.size());
- if (FileUtil::WriteStringToFile(true, base_name + ".json", json_view) != json.size()) {
+ if (Common::FS::WriteStringToFile(true, base_name + ".json", json_view) != json.size()) {
LOG_ERROR(Render_Vulkan, "Failed to write JSON");
return;
}
@@ -180,7 +180,7 @@ void NsightAftermathTracker::OnShaderDebugInfoCallback(const void* shader_debug_
const std::string path =
fmt::format("{}/shader_{:016x}{:016x}.nvdbg", dump_dir, identifier.id[0], identifier.id[1]);
- FileUtil::IOFile file(path, "wb");
+ Common::FS::IOFile file(path, "wb");
if (!file.IsOpen()) {
LOG_ERROR(Render_Vulkan, "Failed to create file {}", path);
return;
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 2258479f5..ae46e0444 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -78,7 +78,7 @@ Common::DynamicLibrary OpenVulkanLibrary() {
if (!libvulkan_env || !library.Open(libvulkan_env)) {
// Use the libvulkan.dylib from the application bundle.
const std::string filename =
- FileUtil::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib";
+ Common::FS::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib";
library.Open(filename.c_str());
}
#else
@@ -237,8 +237,10 @@ std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_ext
} // Anonymous namespace
-RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system)
- : RendererBase(window), system{system} {}
+RendererVulkan::RendererVulkan(Core::System& system_, Core::Frontend::EmuWindow& emu_window,
+ Tegra::GPU& gpu_,
+ std::unique_ptr<Core::Frontend::GraphicsContext> context)
+ : RendererBase{emu_window, std::move(context)}, system{system_}, gpu{gpu_} {}
RendererVulkan::~RendererVulkan() {
ShutDown();
@@ -439,7 +441,7 @@ void RendererVulkan::Report() const {
LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
auto& telemetry_session = system.TelemetrySession();
- constexpr auto field = Telemetry::FieldType::UserSystem;
+ constexpr auto field = Common::Telemetry::FieldType::UserSystem;
telemetry_session.AddField(field, "GPU_Vendor", vendor_name);
telemetry_session.AddField(field, "GPU_Model", model_name);
telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name);
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 522b5bff8..13debbbc0 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -38,7 +38,9 @@ struct VKScreenInfo {
class RendererVulkan final : public VideoCore::RendererBase {
public:
- explicit RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system);
+ explicit RendererVulkan(Core::System& system, Core::Frontend::EmuWindow& emu_window,
+ Tegra::GPU& gpu,
+ std::unique_ptr<Core::Frontend::GraphicsContext> context);
~RendererVulkan() override;
bool Init() override;
@@ -58,6 +60,7 @@ private:
void Report() const;
Core::System& system;
+ Tegra::GPU& gpu;
Common::DynamicLibrary library;
vk::InstanceDispatch dld;
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index 0c03e4d83..4205bd573 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -380,8 +380,18 @@ bool VKDevice::Create() {
CollectTelemetryParameters();
+ if (ext_extended_dynamic_state && driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR) {
+ // AMD's proprietary driver supports VK_EXT_extended_dynamic_state but the <stride> field
+ // seems to be bugged. Blacklisting it for now.
+ LOG_WARNING(Render_Vulkan,
+ "Blacklisting AMD proprietary from VK_EXT_extended_dynamic_state");
+ ext_extended_dynamic_state = false;
+ }
+
graphics_queue = logical.GetQueue(graphics_family);
present_queue = logical.GetQueue(present_family);
+
+ use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue();
return true;
}
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:
return reported_extensions;
}
+ /// Returns true if the setting for async shader compilation is enabled.
+ bool UseAsynchronousShaders() const {
+ return use_asynchronous_shaders;
+ }
+
/// Checks if the physical device is suitable.
static bool IsSuitable(vk::PhysicalDevice physical, VkSurfaceKHR surface);
@@ -252,6 +257,9 @@ private:
bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
+ // Asynchronous Graphics Pipeline setting
+ bool use_asynchronous_shaders{}; ///< Setting to use asynchronous shaders/graphics pipeline
+
// Telemetry parameters
std::string vendor_name; ///< Device's driver name.
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() {
}
ASSERT(!event);
- event = device.GetLogical().CreateEvent();
+ event = device.GetLogical().CreateNewEvent();
ticks = scheduler.Ticks();
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
const GraphicsPipelineCacheKey& key,
vk::Span<VkDescriptorSetLayoutBinding> bindings,
const SPIRVProgram& program)
- : device{device}, scheduler{scheduler}, fixed_state{key.fixed_state}, hash{key.Hash()},
+ : device{device}, scheduler{scheduler}, cache_key{key}, hash{cache_key.Hash()},
descriptor_set_layout{CreateDescriptorSetLayout(bindings)},
descriptor_allocator{descriptor_pool, *descriptor_set_layout},
update_descriptor_queue{update_descriptor_queue}, layout{CreatePipelineLayout()},
descriptor_template{CreateDescriptorUpdateTemplate(program)}, modules{CreateShaderModules(
program)},
- renderpass{renderpass_cache.GetRenderPass(key.renderpass_params)}, pipeline{CreatePipeline(
- key.renderpass_params,
- program)} {}
+ renderpass{renderpass_cache.GetRenderPass(cache_key.renderpass_params)},
+ pipeline{CreatePipeline(cache_key.renderpass_params, program)} {}
VKGraphicsPipeline::~VKGraphicsPipeline() = default;
@@ -181,7 +180,7 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params,
const SPIRVProgram& program) const {
- const auto& state = fixed_state;
+ const auto& state = cache_key.fixed_state;
const auto& viewport_swizzles = state.viewport_swizzles;
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 {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
-struct GraphicsPipelineCacheKey;
+struct GraphicsPipelineCacheKey {
+ RenderPassParams renderpass_params;
+ u32 padding;
+ std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
+ FixedPipelineState fixed_state;
+
+ std::size_t Hash() const noexcept;
+
+ bool operator==(const GraphicsPipelineCacheKey& rhs) const noexcept;
+
+ bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
+ return !operator==(rhs);
+ }
+
+ std::size_t Size() const noexcept {
+ return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size();
+ }
+};
+static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
+static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
+static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>);
class VKDescriptorPool;
class VKDevice;
@@ -54,6 +74,10 @@ public:
return renderpass;
}
+ GraphicsPipelineCacheKey GetCacheKey() const {
+ return cache_key;
+ }
+
private:
vk::DescriptorSetLayout CreateDescriptorSetLayout(
vk::Span<VkDescriptorSetLayoutBinding> bindings) const;
@@ -70,7 +94,7 @@ private:
const VKDevice& device;
VKScheduler& scheduler;
- const FixedPipelineState fixed_state;
+ const GraphicsPipelineCacheKey cache_key;
const u64 hash;
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 @@
#include "video_core/shader/compiler_settings.h"
#include "video_core/shader/memory_util.h"
#include "video_core/shader_cache.h"
+#include "video_core/shader_notify.h"
namespace Vulkan {
@@ -205,24 +206,43 @@ std::array<Shader*, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
return last_shaders = shaders;
}
-VKGraphicsPipeline& VKPipelineCache::GetGraphicsPipeline(const GraphicsPipelineCacheKey& key) {
+VKGraphicsPipeline* VKPipelineCache::GetGraphicsPipeline(
+ const GraphicsPipelineCacheKey& key, VideoCommon::Shader::AsyncShaders& async_shaders) {
MICROPROFILE_SCOPE(Vulkan_PipelineCache);
if (last_graphics_pipeline && last_graphics_key == key) {
- return *last_graphics_pipeline;
+ return last_graphics_pipeline;
}
last_graphics_key = key;
+ if (device.UseAsynchronousShaders() && async_shaders.IsShaderAsync(system.GPU())) {
+ std::unique_lock lock{pipeline_cache};
+ const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key);
+ if (is_cache_miss) {
+ system.GPU().ShaderNotify().MarkSharderBuilding();
+ LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash());
+ const auto [program, bindings] = DecompileShaders(key.fixed_state);
+ async_shaders.QueueVulkanShader(this, device, scheduler, descriptor_pool,
+ update_descriptor_queue, renderpass_cache, bindings,
+ program, key);
+ }
+ last_graphics_pipeline = pair->second.get();
+ return last_graphics_pipeline;
+ }
+
const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key);
auto& entry = pair->second;
if (is_cache_miss) {
+ system.GPU().ShaderNotify().MarkSharderBuilding();
LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash());
- const auto [program, bindings] = DecompileShaders(key);
+ const auto [program, bindings] = DecompileShaders(key.fixed_state);
entry = std::make_unique<VKGraphicsPipeline>(device, scheduler, descriptor_pool,
update_descriptor_queue, renderpass_cache, key,
bindings, program);
+ system.GPU().ShaderNotify().MarkShaderComplete();
}
- return *(last_graphics_pipeline = entry.get());
+ last_graphics_pipeline = entry.get();
+ return last_graphics_pipeline;
}
VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCacheKey& key) {
@@ -277,6 +297,12 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
return *entry;
}
+void VKPipelineCache::EmplacePipeline(std::unique_ptr<VKGraphicsPipeline> pipeline) {
+ system.GPU().ShaderNotify().MarkShaderComplete();
+ std::unique_lock lock{pipeline_cache};
+ graphics_cache.at(pipeline->GetCacheKey()) = std::move(pipeline);
+}
+
void VKPipelineCache::OnShaderRemoval(Shader* shader) {
bool finished = false;
const auto Finish = [&] {
@@ -312,8 +338,7 @@ void VKPipelineCache::OnShaderRemoval(Shader* shader) {
}
std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>>
-VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
- const auto& fixed_state = key.fixed_state;
+VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) {
auto& memory_manager = system.GPU().MemoryManager();
const auto& gpu = system.GPU().Maxwell3D();
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 @@
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
#include "video_core/renderer_vulkan/wrapper.h"
+#include "video_core/shader/async_shaders.h"
#include "video_core/shader/memory_util.h"
#include "video_core/shader/registry.h"
#include "video_core/shader/shader_ir.h"
@@ -43,28 +44,6 @@ class VKUpdateDescriptorQueue;
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
-struct GraphicsPipelineCacheKey {
- RenderPassParams renderpass_params;
- u32 padding;
- std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
- FixedPipelineState fixed_state;
-
- std::size_t Hash() const noexcept;
-
- bool operator==(const GraphicsPipelineCacheKey& rhs) const noexcept;
-
- bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
- return !operator==(rhs);
- }
-
- std::size_t Size() const noexcept {
- return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size();
- }
-};
-static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
-static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
-static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>);
-
struct ComputePipelineCacheKey {
GPUVAddr shader;
u32 shared_memory_size;
@@ -152,16 +131,19 @@ public:
std::array<Shader*, Maxwell::MaxShaderProgram> GetShaders();
- VKGraphicsPipeline& GetGraphicsPipeline(const GraphicsPipelineCacheKey& key);
+ VKGraphicsPipeline* GetGraphicsPipeline(const GraphicsPipelineCacheKey& key,
+ VideoCommon::Shader::AsyncShaders& async_shaders);
VKComputePipeline& GetComputePipeline(const ComputePipelineCacheKey& key);
+ void EmplacePipeline(std::unique_ptr<VKGraphicsPipeline> pipeline);
+
protected:
void OnShaderRemoval(Shader* shader) final;
private:
std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> DecompileShaders(
- const GraphicsPipelineCacheKey& key);
+ const FixedPipelineState& fixed_state);
Core::System& system;
const VKDevice& device;
@@ -178,6 +160,7 @@ private:
GraphicsPipelineCacheKey last_graphics_key;
VKGraphicsPipeline* last_graphics_pipeline = nullptr;
+ std::mutex pipeline_cache;
std::unordered_map<GraphicsPipelineCacheKey, std::unique_ptr<VKGraphicsPipeline>>
graphics_cache;
std::unordered_map<ComputePipelineCacheKey, std::unique_ptr<VKComputePipeline>> compute_cache;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 7500e8244..ff1b52eab 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -14,6 +14,7 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
+#include "common/scope_exit.h"
#include "core/core.h"
#include "core/settings.h"
#include "video_core/engines/kepler_compute.h"
@@ -400,8 +401,12 @@ RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWind
buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool),
sampler_cache(device),
fence_manager(system, *this, device, scheduler, texture_cache, buffer_cache, query_cache),
- query_cache(system, *this, device, scheduler), wfi_event{device.GetLogical().CreateEvent()} {
+ query_cache(system, *this, device, scheduler),
+ wfi_event{device.GetLogical().CreateNewEvent()}, async_shaders{renderer} {
scheduler.SetQueryCache(query_cache);
+ if (device.UseAsynchronousShaders()) {
+ async_shaders.AllocateWorkers();
+ }
}
RasterizerVulkan::~RasterizerVulkan() = default;
@@ -413,6 +418,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
query_cache.UpdateCounters();
+ SCOPE_EXIT({ system.GPU().TickWork(); });
+
const auto& gpu = system.GPU().Maxwell3D();
GraphicsPipelineCacheKey key;
key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported());
@@ -439,10 +446,15 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
key.renderpass_params = GetRenderPassParams(texceptions);
key.padding = 0;
- auto& pipeline = pipeline_cache.GetGraphicsPipeline(key);
- scheduler.BindGraphicsPipeline(pipeline.GetHandle());
+ auto* pipeline = pipeline_cache.GetGraphicsPipeline(key, async_shaders);
+ if (pipeline == nullptr || pipeline->GetHandle() == VK_NULL_HANDLE) {
+ // Async graphics pipeline was not ready.
+ return;
+ }
+
+ scheduler.BindGraphicsPipeline(pipeline->GetHandle());
- const auto renderpass = pipeline.GetRenderPass();
+ const auto renderpass = pipeline->GetRenderPass();
const auto [framebuffer, render_area] = ConfigureFramebuffers(renderpass);
scheduler.RequestRenderpass(renderpass, framebuffer, render_area);
@@ -452,8 +464,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
BeginTransformFeedback();
- const auto pipeline_layout = pipeline.GetLayout();
- const auto descriptor_set = pipeline.CommitDescriptorSet();
+ const auto pipeline_layout = pipeline->GetLayout();
+ const auto descriptor_set = pipeline->CommitDescriptorSet();
scheduler.Record([pipeline_layout, descriptor_set, draw_params](vk::CommandBuffer cmdbuf) {
if (descriptor_set) {
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
@@ -463,8 +475,6 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
});
EndTransformFeedback();
-
- system.GPU().TickWork();
}
void RasterizerVulkan::Clear() {
@@ -1433,10 +1443,10 @@ void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
}
void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) {
- if (!state_tracker.TouchPrimitiveTopology()) {
+ const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
+ if (!state_tracker.ChangePrimitiveTopology(primitive_topology)) {
return;
}
- const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) {
cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology));
});
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 @@
#include "video_core/renderer_vulkan/vk_texture_cache.h"
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
#include "video_core/renderer_vulkan/wrapper.h"
+#include "video_core/shader/async_shaders.h"
namespace Core {
class System;
@@ -136,6 +137,14 @@ public:
u32 pixel_stride) override;
void SetupDirtyFlags() override;
+ VideoCommon::Shader::AsyncShaders& GetAsyncShaders() {
+ return async_shaders;
+ }
+
+ const VideoCommon::Shader::AsyncShaders& GetAsyncShaders() const {
+ return async_shaders;
+ }
+
/// Maximum supported size that a constbuffer can have in bytes.
static constexpr std::size_t MaxConstbufferSize = 0x10000;
static_assert(MaxConstbufferSize % (4 * sizeof(float)) == 0,
@@ -297,6 +306,7 @@ private:
vk::Buffer default_buffer;
VKMemoryCommit default_buffer_commit;
vk::Event wfi_event;
+ VideoCommon::Shader::AsyncShaders async_shaders;
std::array<View, Maxwell::NumRenderTargets> color_attachments;
View zeta_attachment;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 9151d9fb1..4bd1009f9 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -42,7 +42,6 @@ Flags MakeInvalidationFlags() {
flags[DepthWriteEnable] = true;
flags[DepthCompareOp] = true;
flags[FrontFace] = true;
- flags[PrimitiveTopology] = true;
flags[StencilOp] = true;
flags[StencilTestEnable] = true;
return flags;
@@ -112,10 +111,6 @@ void SetupDirtyFrontFace(Tables& tables) {
table[OFF(screen_y_control)] = FrontFace;
}
-void SetupDirtyPrimitiveTopology(Tables& tables) {
- tables[0][OFF(draw.topology)] = PrimitiveTopology;
-}
-
void SetupDirtyStencilOp(Tables& tables) {
auto& table = tables[0];
table[OFF(stencil_front_op_fail)] = StencilOp;
@@ -156,13 +151,13 @@ void StateTracker::Initialize() {
SetupDirtyDepthWriteEnable(tables);
SetupDirtyDepthCompareOp(tables);
SetupDirtyFrontFace(tables);
- SetupDirtyPrimitiveTopology(tables);
SetupDirtyStencilOp(tables);
SetupDirtyStencilTestEnable(tables);
}
void StateTracker::InvalidateCommandBufferState() {
system.GPU().Maxwell3D().dirty.flags |= invalidation_flags;
+ current_topology = INVALID_TOPOLOGY;
}
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 54ca0d6c6..13a6ce786 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -32,7 +32,6 @@ enum : u8 {
DepthWriteEnable,
DepthCompareOp,
FrontFace,
- PrimitiveTopology,
StencilOp,
StencilTestEnable,
@@ -43,6 +42,8 @@ static_assert(Last <= std::numeric_limits<u8>::max());
} // namespace Dirty
class StateTracker {
+ using Maxwell = Tegra::Engines::Maxwell3D::Regs;
+
public:
explicit StateTracker(Core::System& system);
@@ -102,10 +103,6 @@ public:
return Exchange(Dirty::FrontFace, false);
}
- bool TouchPrimitiveTopology() {
- return Exchange(Dirty::PrimitiveTopology, false);
- }
-
bool TouchStencilOp() {
return Exchange(Dirty::StencilOp, false);
}
@@ -114,7 +111,15 @@ public:
return Exchange(Dirty::StencilTestEnable, false);
}
+ bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) {
+ const bool has_changed = current_topology != new_topology;
+ current_topology = new_topology;
+ return has_changed;
+ }
+
private:
+ static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u);
+
bool Exchange(std::size_t id, bool new_value) const noexcept {
auto& flags = system.GPU().Maxwell3D().dirty.flags;
const bool is_dirty = flags[id];
@@ -124,6 +129,7 @@ private:
Core::System& system;
Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
+ Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY;
};
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index 14cac38ea..013865aa4 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/renderer_vulkan/wrapper.cpp
@@ -644,7 +644,7 @@ ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) cons
return ShaderModule(object, handle, *dld);
}
-Event Device::CreateEvent() const {
+Event Device::CreateNewEvent() const {
static constexpr VkEventCreateInfo ci{
.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO,
.pNext = nullptr,
@@ -786,7 +786,7 @@ std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProp
VK_SUCCESS) {
return std::nullopt;
}
- return properties;
+ return std::move(properties);
}
std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties(
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:
ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
- Event CreateEvent() const;
+ Event CreateNewEvent() const;
SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
@@ -756,8 +756,8 @@ public:
}
VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size,
- void* data, VkDeviceSize stride, VkQueryResultFlags flags) const
- noexcept {
+ void* data, VkDeviceSize stride,
+ VkQueryResultFlags flags) const noexcept {
return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
flags);
}
@@ -849,8 +849,8 @@ public:
dld->vkCmdBindPipeline(handle, bind_point, pipeline);
}
- void BindIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType index_type) const
- noexcept {
+ void BindIndexBuffer(VkBuffer buffer, VkDeviceSize offset,
+ VkIndexType index_type) const noexcept {
dld->vkCmdBindIndexBuffer(handle, buffer, offset, index_type);
}
@@ -863,8 +863,8 @@ public:
BindVertexBuffers(binding, 1, &buffer, &offset);
}
- void Draw(u32 vertex_count, u32 instance_count, u32 first_vertex, u32 first_instance) const
- noexcept {
+ void Draw(u32 vertex_count, u32 instance_count, u32 first_vertex,
+ u32 first_instance) const noexcept {
dld->vkCmdDraw(handle, vertex_count, instance_count, first_vertex, first_instance);
}
@@ -874,15 +874,15 @@ public:
first_instance);
}
- void ClearAttachments(Span<VkClearAttachment> attachments, Span<VkClearRect> rects) const
- noexcept {
+ void ClearAttachments(Span<VkClearAttachment> attachments,
+ Span<VkClearRect> rects) const noexcept {
dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(),
rects.data());
}
void BlitImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image,
- VkImageLayout dst_layout, Span<VkImageBlit> regions, VkFilter filter) const
- noexcept {
+ VkImageLayout dst_layout, Span<VkImageBlit> regions,
+ VkFilter filter) const noexcept {
dld->vkCmdBlitImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(),
regions.data(), filter);
}
@@ -907,8 +907,8 @@ public:
regions.data());
}
- void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, Span<VkBufferCopy> regions) const
- noexcept {
+ void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer,
+ Span<VkBufferCopy> regions) const noexcept {
dld->vkCmdCopyBuffer(handle, src_buffer, dst_buffer, regions.size(), regions.data());
}
@@ -924,8 +924,8 @@ public:
regions.data());
}
- void FillBuffer(VkBuffer dst_buffer, VkDeviceSize dst_offset, VkDeviceSize size, u32 data) const
- noexcept {
+ void FillBuffer(VkBuffer dst_buffer, VkDeviceSize dst_offset, VkDeviceSize size,
+ u32 data) const noexcept {
dld->vkCmdFillBuffer(handle, dst_buffer, dst_offset, size, data);
}
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
@@ -20,9 +19,18 @@ AsyncShaders::~AsyncShaders() {
KillWorkers();
}
-void AsyncShaders::AllocateWorkers(std::size_t num_workers) {
- // If we're already have workers queued or don't want to queue workers, ignore
- if (num_workers == worker_threads.size() || num_workers == 0) {
+void AsyncShaders::AllocateWorkers() {
+ // Max worker threads we should allow
+ constexpr u32 MAX_THREADS = 4;
+ // Deduce how many threads we can use
+ const u32 threads_used = std::thread::hardware_concurrency() / 4;
+ // Always allow at least 1 thread regardless of our settings
+ const auto max_worker_count = std::max(1U, threads_used);
+ // Don't use more than MAX_THREADS
+ const auto num_workers = std::min(max_worker_count, MAX_THREADS);
+
+ // If we already have workers queued, ignore
+ if (num_workers == worker_threads.size()) {
return;
}
@@ -34,8 +42,8 @@ void AsyncShaders::AllocateWorkers(std::size_t num_workers) {
// Create workers
for (std::size_t i = 0; i < num_workers; i++) {
context_list.push_back(emu_window.CreateSharedContext());
- worker_threads.push_back(std::move(
- std::thread(&AsyncShaders::ShaderCompilerThread, this, context_list[i].get())));
+ worker_threads.push_back(
+ std::thread(&AsyncShaders::ShaderCompilerThread, this, context_list[i].get()));
}
}
@@ -111,24 +119,50 @@ void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
VideoCommon::Shader::CompilerSettings compiler_settings,
const VideoCommon::Shader::Registry& registry,
VAddr cpu_addr) {
- WorkerParams params{device.UseAssemblyShaders() ? AsyncShaders::Backend::GLASM
- : AsyncShaders::Backend::OpenGL,
- device,
- shader_type,
- uid,
- std::move(code),
- std::move(code_b),
- main_offset,
- compiler_settings,
- registry,
- cpu_addr};
+ WorkerParams params{
+ .backend = device.UseAssemblyShaders() ? Backend::GLASM : Backend::OpenGL,
+ .device = &device,
+ .shader_type = shader_type,
+ .uid = uid,
+ .code = std::move(code),
+ .code_b = std::move(code_b),
+ .main_offset = main_offset,
+ .compiler_settings = compiler_settings,
+ .registry = registry,
+ .cpu_address = cpu_addr,
+ };
std::unique_lock lock(queue_mutex);
- pending_queue.push_back(std::move(params));
+ pending_queue.push(std::move(params));
+ cv.notify_one();
+}
+
+void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
+ const Vulkan::VKDevice& device, Vulkan::VKScheduler& scheduler,
+ Vulkan::VKDescriptorPool& descriptor_pool,
+ Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue,
+ Vulkan::VKRenderPassCache& renderpass_cache,
+ std::vector<VkDescriptorSetLayoutBinding> bindings,
+ Vulkan::SPIRVProgram program,
+ Vulkan::GraphicsPipelineCacheKey key) {
+ WorkerParams params{
+ .backend = Backend::Vulkan,
+ .pp_cache = pp_cache,
+ .vk_device = &device,
+ .scheduler = &scheduler,
+ .descriptor_pool = &descriptor_pool,
+ .update_descriptor_queue = &update_descriptor_queue,
+ .renderpass_cache = &renderpass_cache,
+ .bindings = bindings,
+ .program = program,
+ .key = key,
+ };
+
+ std::unique_lock lock(queue_mutex);
+ pending_queue.push(std::move(params));
cv.notify_one();
}
void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context) {
- using namespace std::chrono_literals;
while (!is_thread_exiting.load(std::memory_order_relaxed)) {
std::unique_lock lock{queue_mutex};
cv.wait(lock, [this] { return HasWorkQueued() || is_thread_exiting; });
@@ -144,18 +178,17 @@ void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context
if (pending_queue.empty()) {
continue;
}
+
// Pull work from queue
WorkerParams work = std::move(pending_queue.front());
- pending_queue.pop_front();
-
+ pending_queue.pop();
lock.unlock();
- if (work.backend == AsyncShaders::Backend::OpenGL ||
- work.backend == AsyncShaders::Backend::GLASM) {
- const ShaderIR ir(work.code, work.main_offset, work.compiler_settings, work.registry);
+ if (work.backend == Backend::OpenGL || work.backend == Backend::GLASM) {
+ const ShaderIR ir(work.code, work.main_offset, work.compiler_settings, *work.registry);
const auto scope = context->Acquire();
auto program =
- OpenGL::BuildShader(work.device, work.shader_type, work.uid, ir, work.registry);
+ OpenGL::BuildShader(*work.device, work.shader_type, work.uid, ir, *work.registry);
Result result{};
result.backend = work.backend;
result.cpu_address = work.cpu_address;
@@ -164,9 +197,9 @@ void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context
result.code_b = std::move(work.code_b);
result.shader_type = work.shader_type;
- if (work.backend == AsyncShaders::Backend::OpenGL) {
+ if (work.backend == Backend::OpenGL) {
result.program.opengl = std::move(program->source_program);
- } else if (work.backend == AsyncShaders::Backend::GLASM) {
+ } else if (work.backend == Backend::GLASM) {
result.program.glasm = std::move(program->assembly_program);
}
@@ -174,6 +207,13 @@ void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context
std::unique_lock complete_lock(completed_mutex);
finished_work.push_back(std::move(result));
}
+ } else if (work.backend == Backend::Vulkan) {
+ auto pipeline = std::make_unique<Vulkan::VKGraphicsPipeline>(
+ *work.vk_device, *work.scheduler, *work.descriptor_pool,
+ *work.update_descriptor_queue, *work.renderpass_cache, work.key, work.bindings,
+ work.program);
+
+ work.pp_cache->EmplacePipeline(std::move(pipeline));
}
}
}
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 @@
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
+#include "video_core/renderer_vulkan/vk_device.h"
+#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
+#include "video_core/renderer_vulkan/vk_scheduler.h"
+#include "video_core/renderer_vulkan/vk_update_descriptor.h"
namespace Core::Frontend {
class EmuWindow;
@@ -24,6 +28,10 @@ namespace Tegra {
class GPU;
}
+namespace Vulkan {
+class VKPipelineCache;
+}
+
namespace VideoCommon::Shader {
class AsyncShaders {
@@ -31,6 +39,7 @@ public:
enum class Backend {
OpenGL,
GLASM,
+ Vulkan,
};
struct ResultPrograms {
@@ -52,7 +61,7 @@ public:
~AsyncShaders();
/// Start up shader worker threads
- void AllocateWorkers(std::size_t num_workers);
+ void AllocateWorkers();
/// Clear the shader queue and kill all worker threads
void FreeWorkers();
@@ -76,6 +85,14 @@ public:
VideoCommon::Shader::CompilerSettings compiler_settings,
const VideoCommon::Shader::Registry& registry, VAddr cpu_addr);
+ void QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, const Vulkan::VKDevice& device,
+ Vulkan::VKScheduler& scheduler,
+ Vulkan::VKDescriptorPool& descriptor_pool,
+ Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue,
+ Vulkan::VKRenderPassCache& renderpass_cache,
+ std::vector<VkDescriptorSetLayoutBinding> bindings,
+ Vulkan::SPIRVProgram program, Vulkan::GraphicsPipelineCacheKey key);
+
private:
void ShaderCompilerThread(Core::Frontend::GraphicsContext* context);
@@ -83,16 +100,28 @@ private:
bool HasWorkQueued();
struct WorkerParams {
- AsyncShaders::Backend backend;
- OpenGL::Device device;
+ Backend backend;
+ // For OGL
+ const OpenGL::Device* device;
Tegra::Engines::ShaderType shader_type;
u64 uid;
std::vector<u64> code;
std::vector<u64> code_b;
u32 main_offset;
VideoCommon::Shader::CompilerSettings compiler_settings;
- VideoCommon::Shader::Registry registry;
+ std::optional<VideoCommon::Shader::Registry> registry;
VAddr cpu_address;
+
+ // For Vulkan
+ Vulkan::VKPipelineCache* pp_cache;
+ const Vulkan::VKDevice* vk_device;
+ Vulkan::VKScheduler* scheduler;
+ Vulkan::VKDescriptorPool* descriptor_pool;
+ Vulkan::VKUpdateDescriptorQueue* update_descriptor_queue;
+ Vulkan::VKRenderPassCache* renderpass_cache;
+ std::vector<VkDescriptorSetLayoutBinding> bindings;
+ Vulkan::SPIRVProgram program;
+ Vulkan::GraphicsPipelineCacheKey key;
};
std::condition_variable cv;
@@ -101,7 +130,7 @@ private:
std::atomic<bool> is_thread_exiting{};
std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> context_list;
std::vector<std::thread> worker_threads;
- std::deque<WorkerParams> pending_queue;
+ std::queue<WorkerParams> pending_queue;
std::vector<AsyncShaders::Result> finished_work;
Core::Frontend::EmuWindow& emu_window;
};
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,
std::optional<u64> TrackSHLRegister(const CFGRebuildState& state, u32& pos,
u64 ldc_tracked_register) {
- return TrackInstruction<u64>(state, pos,
- [ldc_tracked_register](auto instr, const auto& opcode) {
- return opcode.GetId() == OpCode::Id::SHL_IMM &&
- instr.gpr0.Value() == ldc_tracked_register;
- },
- [](auto instr, const auto&) { return instr.gpr8.Value(); });
+ return TrackInstruction<u64>(
+ state, pos,
+ [ldc_tracked_register](auto instr, const auto& opcode) {
+ return opcode.GetId() == OpCode::Id::SHL_IMM &&
+ instr.gpr0.Value() == ldc_tracked_register;
+ },
+ [](auto instr, const auto&) { return instr.gpr8.Value(); });
}
std::optional<u32> TrackIMNMXValue(const CFGRebuildState& state, u32& pos,
u64 shl_tracked_register) {
- return TrackInstruction<u32>(state, pos,
- [shl_tracked_register](auto instr, const auto& opcode) {
- return opcode.GetId() == OpCode::Id::IMNMX_IMM &&
- instr.gpr0.Value() == shl_tracked_register;
- },
- [](auto instr, const auto&) {
- return static_cast<u32>(instr.alu.GetSignedImm20_20() + 1);
- });
+ return TrackInstruction<u32>(
+ state, pos,
+ [shl_tracked_register](auto instr, const auto& opcode) {
+ return opcode.GetId() == OpCode::Id::IMNMX_IMM &&
+ instr.gpr0.Value() == shl_tracked_register;
+ },
+ [](auto instr, const auto&) {
+ return static_cast<u32>(instr.alu.GetSignedImm20_20() + 1);
+ });
}
std::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..e2bba88dd 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -386,7 +386,8 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
break;
}
case OpCode::Id::RED: {
- UNIMPLEMENTED_IF_MSG(instr.red.type != GlobalAtomicType::U32);
+ UNIMPLEMENTED_IF_MSG(instr.red.type != GlobalAtomicType::U32, "type={}",
+ static_cast<int>(instr.red.type.Value()));
const auto [real_address, base_address, descriptor] =
TrackGlobalMemory(bb, instr, true, true);
if (!real_address || !base_address) {
@@ -471,9 +472,9 @@ std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackGlobalMemory(NodeBlock&
const auto [base_address, index, offset] =
TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()));
- ASSERT_OR_EXECUTE_MSG(base_address != nullptr,
- { return std::make_tuple(nullptr, nullptr, GlobalMemoryBase{}); },
- "Global memory tracking failed");
+ ASSERT_OR_EXECUTE_MSG(
+ base_address != nullptr, { return std::make_tuple(nullptr, nullptr, GlobalMemoryBase{}); },
+ "Global memory tracking failed");
bb.push_back(Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", index, offset)));
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
}
params.type = GetFormatType(params.pixel_format);
}
- params.type = GetFormatType(params.pixel_format);
// TODO: on 1DBuffer we should use the tic info.
if (tic.IsBuffer()) {
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
}
}
-void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
- u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
- u32 block_height_bit, u32 offset_x, u32 offset_y) {
- const u32 block_height = 1U << block_height_bit;
- for (u32 line = 0; line < subrect_height; ++line) {
- const u32 y2 = line + offset_y;
- const u32 gob_address_y = (y2 / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height +
- ((y2 % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE;
- const auto& table = LEGACY_SWIZZLE_TABLE[y2 % GOB_SIZE_Y];
- for (u32 x = 0; x < subrect_width; ++x) {
- const u32 x2 = (x + offset_x) * bytes_per_pixel;
- const u32 gob_address = gob_address_y + (x2 / GOB_SIZE_X) * GOB_SIZE * block_height;
- const u32 swizzled_offset = gob_address + table[x2 % GOB_SIZE_X];
- const u32 unswizzled_offset = line * dest_pitch + x * bytes_per_pixel;
- u8* dest_line = unswizzled_data + unswizzled_offset;
- u8* source_addr = swizzled_data + swizzled_offset;
+void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel,
+ u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) {
+ const u32 stride = width * bytes_per_pixel;
+ const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
+ const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height);
+
+ const u32 block_height_mask = (1U << block_height) - 1;
+ const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height;
+
+ for (u32 line = 0; line < line_count; ++line) {
+ const u32 src_y = line + origin_y;
+ const auto& table = LEGACY_SWIZZLE_TABLE[src_y % GOB_SIZE_Y];
+
+ const u32 block_y = src_y >> GOB_SIZE_Y_SHIFT;
+ const u32 src_offset_y = (block_y >> block_height) * block_size +
+ ((block_y & block_height_mask) << GOB_SIZE_SHIFT);
+ for (u32 column = 0; column < line_length_in; ++column) {
+ const u32 src_x = (column + origin_x) * bytes_per_pixel;
+ const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift;
+
+ const u32 swizzled_offset = src_offset_y + src_offset_x + table[src_x % GOB_SIZE_X];
+ const u32 unswizzled_offset = line * pitch + column * bytes_per_pixel;
- std::memcpy(dest_line, source_addr, bytes_per_pixel);
+ std::memcpy(output + unswizzled_offset, input + swizzled_offset, bytes_per_pixel);
}
}
}
@@ -261,7 +267,7 @@ void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 widt
const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
const u32 block_height_mask = (1U << block_height) - 1;
- const u32 x_shift = Common::CountTrailingZeroes32(GOB_SIZE << (block_height + block_depth));
+ const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height + block_depth;
for (u32 line = 0; line < line_count; ++line) {
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
u32 block_height_bit, u32 offset_x, u32 offset_y);
/// Copies a tiled subrectangle into a linear surface.
-void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
- u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height,
- u32 offset_x, u32 offset_y);
+void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel,
+ u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input);
/// @brief Swizzles a 2D array of pixels into a 3D texture
/// @param line_length_in Number of pixels per line
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 45f360bdd..4e3a092c7 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <memory>
+
#include "common/logging/log.h"
#include "core/core.h"
#include "core/settings.h"
@@ -16,37 +17,46 @@
#include "video_core/video_core.h"
namespace {
-std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
- Core::System& system,
- Core::Frontend::GraphicsContext& context) {
+
+std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
+ Core::System& system, Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
+ std::unique_ptr<Core::Frontend::GraphicsContext> context) {
switch (Settings::values.renderer_backend.GetValue()) {
case Settings::RendererBackend::OpenGL:
- return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context);
+ return std::make_unique<OpenGL::RendererOpenGL>(system, emu_window, gpu,
+ std::move(context));
#ifdef HAS_VULKAN
case Settings::RendererBackend::Vulkan:
- return std::make_unique<Vulkan::RendererVulkan>(emu_window, system);
+ return std::make_unique<Vulkan::RendererVulkan>(system, emu_window, gpu,
+ std::move(context));
#endif
default:
return nullptr;
}
}
+
} // Anonymous namespace
namespace VideoCore {
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
+ std::unique_ptr<Tegra::GPU> gpu;
+ if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
+ gpu = std::make_unique<VideoCommon::GPUAsynch>(system);
+ } else {
+ gpu = std::make_unique<VideoCommon::GPUSynch>(system);
+ }
+
auto context = emu_window.CreateSharedContext();
const auto scope = context->Acquire();
- auto renderer = CreateRenderer(emu_window, system, *context);
+
+ auto renderer = CreateRenderer(system, emu_window, *gpu, std::move(context));
if (!renderer->Init()) {
return nullptr;
}
- if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
- return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer),
- std::move(context));
- }
- return std::make_unique<VideoCommon::GPUSynch>(system, std::move(renderer), std::move(context));
+ gpu->BindRenderer(std::move(renderer));
+ return gpu;
}
u16 GetResolutionScaleFactor(const RendererBase& renderer) {
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt
index 06ab7c59d..7e484b906 100644
--- a/src/web_service/CMakeLists.txt
+++ b/src/web_service/CMakeLists.txt
@@ -5,6 +5,7 @@ add_library(web_service STATIC
verify_login.h
web_backend.cpp
web_backend.h
+ web_result.h
)
create_target_directory_groups(web_service)
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index 7a480e33c..6215c914f 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -4,12 +4,14 @@
#include <nlohmann/json.hpp>
#include "common/detached_tasks.h"
-#include "common/web_result.h"
#include "web_service/telemetry_json.h"
#include "web_service/web_backend.h"
+#include "web_service/web_result.h"
namespace WebService {
+namespace Telemetry = Common::Telemetry;
+
struct TelemetryJson::Impl {
Impl(std::string host, std::string username, std::string token)
: host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {}
@@ -123,7 +125,7 @@ bool TelemetryJson::SubmitTestcase() {
Client client(impl->host, impl->username, impl->token);
auto value = client.PostJson("/gamedb/testcase", content, false);
- return value.result_code == Common::WebResult::Code::Success;
+ return value.result_code == WebResult::Code::Success;
}
} // namespace WebService
diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h
index dfd202829..df51e00f8 100644
--- a/src/web_service/telemetry_json.h
+++ b/src/web_service/telemetry_json.h
@@ -14,25 +14,25 @@ namespace WebService {
* Implementation of VisitorInterface that serialized telemetry into JSON, and submits it to the
* yuzu web service
*/
-class TelemetryJson : public Telemetry::VisitorInterface {
+class TelemetryJson : public Common::Telemetry::VisitorInterface {
public:
TelemetryJson(std::string host, std::string username, std::string token);
~TelemetryJson() override;
- void Visit(const Telemetry::Field<bool>& field) override;
- void Visit(const Telemetry::Field<double>& field) override;
- void Visit(const Telemetry::Field<float>& field) override;
- void Visit(const Telemetry::Field<u8>& field) override;
- void Visit(const Telemetry::Field<u16>& field) override;
- void Visit(const Telemetry::Field<u32>& field) override;
- void Visit(const Telemetry::Field<u64>& field) override;
- void Visit(const Telemetry::Field<s8>& field) override;
- void Visit(const Telemetry::Field<s16>& field) override;
- void Visit(const Telemetry::Field<s32>& field) override;
- void Visit(const Telemetry::Field<s64>& field) override;
- void Visit(const Telemetry::Field<std::string>& field) override;
- void Visit(const Telemetry::Field<const char*>& field) override;
- void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override;
+ void Visit(const Common::Telemetry::Field<bool>& field) override;
+ void Visit(const Common::Telemetry::Field<double>& field) override;
+ void Visit(const Common::Telemetry::Field<float>& field) override;
+ void Visit(const Common::Telemetry::Field<u8>& field) override;
+ void Visit(const Common::Telemetry::Field<u16>& field) override;
+ void Visit(const Common::Telemetry::Field<u32>& field) override;
+ void Visit(const Common::Telemetry::Field<u64>& field) override;
+ void Visit(const Common::Telemetry::Field<s8>& field) override;
+ void Visit(const Common::Telemetry::Field<s16>& field) override;
+ void Visit(const Common::Telemetry::Field<s32>& field) override;
+ void Visit(const Common::Telemetry::Field<s64>& field) override;
+ void Visit(const Common::Telemetry::Field<std::string>& field) override;
+ void Visit(const Common::Telemetry::Field<const char*>& field) override;
+ void Visit(const Common::Telemetry::Field<std::chrono::microseconds>& field) override;
void Complete() override;
bool SubmitTestcase() override;
diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp
index bfaa5b70a..ceb55ca6b 100644
--- a/src/web_service/verify_login.cpp
+++ b/src/web_service/verify_login.cpp
@@ -3,9 +3,9 @@
// Refer to the license.txt file included.
#include <nlohmann/json.hpp>
-#include "common/web_result.h"
#include "web_service/verify_login.h"
#include "web_service/web_backend.h"
+#include "web_service/web_result.h"
namespace WebService {
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 09d1651ac..74e287045 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -6,13 +6,14 @@
#include <cstdlib>
#include <mutex>
#include <string>
+
#include <LUrlParser.h>
#include <fmt/format.h>
#include <httplib.h>
-#include "common/common_types.h"
+
#include "common/logging/log.h"
-#include "common/web_result.h"
#include "web_service/web_backend.h"
+#include "web_service/web_result.h"
namespace WebService {
@@ -33,17 +34,16 @@ struct Client::Impl {
}
/// A generic function handles POST, GET and DELETE request together
- Common::WebResult GenericRequest(const std::string& method, const std::string& path,
- const std::string& data, bool allow_anonymous,
- const std::string& accept) {
+ WebResult GenericRequest(const std::string& method, const std::string& path,
+ const std::string& data, bool allow_anonymous,
+ const std::string& accept) {
if (jwt.empty()) {
UpdateJWT();
}
if (jwt.empty() && !allow_anonymous) {
LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
- return Common::WebResult{Common::WebResult::Code::CredentialsMissing,
- "Credentials needed", ""};
+ return WebResult{WebResult::Code::CredentialsMissing, "Credentials needed", ""};
}
auto result = GenericRequest(method, path, data, accept, jwt);
@@ -62,10 +62,10 @@ struct Client::Impl {
* username + token is used if jwt is empty but username and token are
* not empty anonymous if all of jwt, username and token are empty
*/
- Common::WebResult GenericRequest(const std::string& method, const std::string& path,
- const std::string& data, const std::string& accept,
- const std::string& jwt = "", const std::string& username = "",
- const std::string& token = "") {
+ WebResult GenericRequest(const std::string& method, const std::string& path,
+ const std::string& data, const std::string& accept,
+ const std::string& jwt = "", const std::string& username = "",
+ const std::string& token = "") {
if (cli == nullptr) {
auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
int port;
@@ -81,12 +81,12 @@ struct Client::Impl {
cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port);
} else {
LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
- return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme", ""};
+ return WebResult{WebResult::Code::InvalidURL, "Bad URL scheme", ""};
}
}
if (cli == nullptr) {
LOG_ERROR(WebService, "Invalid URL {}", host + path);
- return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL", ""};
+ return WebResult{WebResult::Code::InvalidURL, "Invalid URL", ""};
}
cli->set_timeout_sec(TIMEOUT_SECONDS);
@@ -106,7 +106,7 @@ struct Client::Impl {
std::string(API_VERSION.begin(), API_VERSION.end()));
if (method != "GET") {
params.emplace(std::string("Content-Type"), std::string("application/json"));
- };
+ }
httplib::Request request;
request.method = method;
@@ -118,29 +118,28 @@ struct Client::Impl {
if (!cli->send(request, response)) {
LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
- return Common::WebResult{Common::WebResult::Code::LibError, "Null response", ""};
+ return WebResult{WebResult::Code::LibError, "Null response", ""};
}
if (response.status >= 400) {
LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
response.status);
- return Common::WebResult{Common::WebResult::Code::HttpError,
- std::to_string(response.status), ""};
+ return WebResult{WebResult::Code::HttpError, std::to_string(response.status), ""};
}
auto content_type = response.headers.find("content-type");
if (content_type == response.headers.end()) {
LOG_ERROR(WebService, "{} to {} returned no content", method, host + path);
- return Common::WebResult{Common::WebResult::Code::WrongContent, "", ""};
+ return WebResult{WebResult::Code::WrongContent, "", ""};
}
if (content_type->second.find(accept) == std::string::npos) {
LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path,
content_type->second);
- return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content", ""};
+ return WebResult{WebResult::Code::WrongContent, "Wrong content", ""};
}
- return Common::WebResult{Common::WebResult::Code::Success, "", response.body};
+ return WebResult{WebResult::Code::Success, "", response.body};
}
// Retrieve a new JWT from given username and token
@@ -150,7 +149,7 @@ struct Client::Impl {
}
auto result = GenericRequest("POST", "/jwt/internal", "", "text/html", "", username, token);
- if (result.result_code != Common::WebResult::Code::Success) {
+ if (result.result_code != WebResult::Code::Success) {
LOG_ERROR(WebService, "UpdateJWT failed");
} else {
std::lock_guard lock{jwt_cache.mutex};
@@ -180,29 +179,28 @@ Client::Client(std::string host, std::string username, std::string token)
Client::~Client() = default;
-Common::WebResult Client::PostJson(const std::string& path, const std::string& data,
- bool allow_anonymous) {
+WebResult Client::PostJson(const std::string& path, const std::string& data, bool allow_anonymous) {
return impl->GenericRequest("POST", path, data, allow_anonymous, "application/json");
}
-Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
+WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
return impl->GenericRequest("GET", path, "", allow_anonymous, "application/json");
}
-Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data,
- bool allow_anonymous) {
+WebResult Client::DeleteJson(const std::string& path, const std::string& data,
+ bool allow_anonymous) {
return impl->GenericRequest("DELETE", path, data, allow_anonymous, "application/json");
}
-Common::WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) {
+WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) {
return impl->GenericRequest("GET", path, "", allow_anonymous, "text/plain");
}
-Common::WebResult Client::GetImage(const std::string& path, bool allow_anonymous) {
+WebResult Client::GetImage(const std::string& path, bool allow_anonymous) {
return impl->GenericRequest("GET", path, "", allow_anonymous, "image/png");
}
-Common::WebResult Client::GetExternalJWT(const std::string& audience) {
+WebResult Client::GetExternalJWT(const std::string& audience) {
return impl->GenericRequest("POST", fmt::format("/jwt/external/{}", audience), "", false,
"text/html");
}
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h
index 04121f17e..81f58583c 100644
--- a/src/web_service/web_backend.h
+++ b/src/web_service/web_backend.h
@@ -7,12 +7,10 @@
#include <memory>
#include <string>
-namespace Common {
-struct WebResult;
-}
-
namespace WebService {
+struct WebResult;
+
class Client {
public:
Client(std::string host, std::string username, std::string token);
@@ -25,8 +23,7 @@ public:
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
* @return the result of the request.
*/
- Common::WebResult PostJson(const std::string& path, const std::string& data,
- bool allow_anonymous);
+ WebResult PostJson(const std::string& path, const std::string& data, bool allow_anonymous);
/**
* Gets JSON from the specified path.
@@ -34,7 +31,7 @@ public:
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
* @return the result of the request.
*/
- Common::WebResult GetJson(const std::string& path, bool allow_anonymous);
+ WebResult GetJson(const std::string& path, bool allow_anonymous);
/**
* Deletes JSON to the specified path.
@@ -43,8 +40,7 @@ public:
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
* @return the result of the request.
*/
- Common::WebResult DeleteJson(const std::string& path, const std::string& data,
- bool allow_anonymous);
+ WebResult DeleteJson(const std::string& path, const std::string& data, bool allow_anonymous);
/**
* Gets a plain string from the specified path.
@@ -52,7 +48,7 @@ public:
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
* @return the result of the request.
*/
- Common::WebResult GetPlain(const std::string& path, bool allow_anonymous);
+ WebResult GetPlain(const std::string& path, bool allow_anonymous);
/**
* Gets an PNG image from the specified path.
@@ -60,14 +56,14 @@ public:
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
* @return the result of the request.
*/
- Common::WebResult GetImage(const std::string& path, bool allow_anonymous);
+ WebResult GetImage(const std::string& path, bool allow_anonymous);
/**
* Requests an external JWT for the specific audience provided.
* @param audience the audience of the JWT requested.
* @return the result of the request.
*/
- Common::WebResult GetExternalJWT(const std::string& audience);
+ WebResult GetExternalJWT(const std::string& audience);
private:
struct Impl;
diff --git a/src/common/web_result.h b/src/web_service/web_result.h
index 8bfa2141d..3aeeb5288 100644
--- a/src/common/web_result.h
+++ b/src/web_service/web_result.h
@@ -7,7 +7,7 @@
#include <string>
#include "common/common_types.h"
-namespace Common {
+namespace WebService {
struct WebResult {
enum class Code : u32 {
Success,
@@ -22,4 +22,4 @@ struct WebResult {
std::string result_string;
std::string returned_data;
};
-} // namespace Common
+} // namespace WebService
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 656096c9f..6987e85e1 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -39,6 +39,9 @@ add_executable(yuzu
configuration/configure_debug.cpp
configuration/configure_debug.h
configuration/configure_debug.ui
+ configuration/configure_debug_controller.cpp
+ configuration/configure_debug_controller.h
+ configuration/configure_debug_controller.ui
configuration/configure_dialog.cpp
configuration/configure_dialog.h
configuration/configure_filesystem.cpp
@@ -62,9 +65,9 @@ add_executable(yuzu
configuration/configure_input_player.cpp
configuration/configure_input_player.h
configuration/configure_input_player.ui
- configuration/configure_input_simple.cpp
- configuration/configure_input_simple.h
- configuration/configure_input_simple.ui
+ configuration/configure_input_advanced.cpp
+ configuration/configure_input_advanced.h
+ configuration/configure_input_advanced.ui
configuration/configure_mouse_advanced.cpp
configuration/configure_mouse_advanced.h
configuration/configure_mouse_advanced.ui
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) {
}
QString GetImagePath(Common::UUID uuid) {
- const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
+ const auto path = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
return QString::fromStdString(path);
}
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 8fc322b30..21707e451 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -304,8 +304,9 @@ static Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow*
return wsi;
}
-GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_)
- : QWidget(parent_), emu_thread(emu_thread_) {
+GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
+ std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_)
+ : QWidget(parent), emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)} {
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
.arg(QString::fromUtf8(Common::g_build_name),
QString::fromUtf8(Common::g_scm_branch),
@@ -314,15 +315,15 @@ GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_)
auto layout = new QHBoxLayout(this);
layout->setMargin(0);
setLayout(layout);
- InputCommon::Init();
+ input_subsystem->Initialize();
this->setMouseTracking(true);
- connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete);
+ connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
}
GRenderWindow::~GRenderWindow() {
- InputCommon::Shutdown();
+ input_subsystem->Shutdown();
}
void GRenderWindow::PollEvents() {
@@ -391,11 +392,11 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
}
void GRenderWindow::keyPressEvent(QKeyEvent* event) {
- InputCommon::GetKeyboard()->PressKey(event->key());
+ input_subsystem->GetKeyboard()->PressKey(event->key());
}
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
- InputCommon::GetKeyboard()->ReleaseKey(event->key());
+ input_subsystem->GetKeyboard()->ReleaseKey(event->key());
}
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
@@ -409,7 +410,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
const auto [x, y] = ScaleTouch(pos);
this->TouchPressed(x, y);
} else if (event->button() == Qt::RightButton) {
- InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
+ input_subsystem->GetMotionEmu()->BeginTilt(pos.x(), pos.y());
}
QWidget::mousePressEvent(event);
}
@@ -423,7 +424,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
auto pos = event->pos();
const auto [x, y] = ScaleTouch(pos);
this->TouchMoved(x, y);
- InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
+ input_subsystem->GetMotionEmu()->Tilt(pos.x(), pos.y());
QWidget::mouseMoveEvent(event);
}
@@ -436,7 +437,7 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
if (event->button() == Qt::LeftButton) {
this->TouchReleased();
} else if (event->button() == Qt::RightButton) {
- InputCommon::GetMotionEmu()->EndTilt();
+ input_subsystem->GetMotionEmu()->EndTilt();
}
}
@@ -451,7 +452,7 @@ void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
int active_points = 0;
// average all active touch points
- for (const auto tp : event->touchPoints()) {
+ for (const auto& tp : event->touchPoints()) {
if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) {
active_points++;
pos += tp.pos();
@@ -485,7 +486,7 @@ bool GRenderWindow::event(QEvent* event) {
void GRenderWindow::focusOutEvent(QFocusEvent* event) {
QWidget::focusOutEvent(event);
- InputCommon::GetKeyboard()->ReleaseAllKeys();
+ input_subsystem->GetKeyboard()->ReleaseAllKeys();
}
void GRenderWindow::resizeEvent(QResizeEvent* event) {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 6c59b4d5c..ca35cf831 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -6,6 +6,7 @@
#include <atomic>
#include <condition_variable>
+#include <memory>
#include <mutex>
#include <QImage>
@@ -23,6 +24,10 @@ class QKeyEvent;
class QTouchEvent;
class QStringList;
+namespace InputCommon {
+class InputSubsystem;
+}
+
namespace VideoCore {
enum class LoadCallbackStage;
}
@@ -121,7 +126,8 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
Q_OBJECT
public:
- GRenderWindow(GMainWindow* parent, EmuThread* emu_thread);
+ explicit GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
+ std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_);
~GRenderWindow() override;
// EmuWindow implementation.
@@ -183,6 +189,7 @@ private:
QStringList GetUnsupportedGLExtensions() const;
EmuThread* emu_thread;
+ std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
// Main context that will be shared with all other contexts that are requested.
// If this is used in a shared context setting, then this should not be used directly, but
diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp
index 5477f050c..649912557 100644
--- a/src/yuzu/compatdb.cpp
+++ b/src/yuzu/compatdb.cpp
@@ -54,7 +54,8 @@ void CompatDB::Submit() {
back();
LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
Core::System::GetInstance().TelemetrySession().AddField(
- Telemetry::FieldType::UserFeedback, "Compatibility", compatibility->checkedId());
+ Common::Telemetry::FieldType::UserFeedback, "Compatibility",
+ compatibility->checkedId());
button(NextButton)->setEnabled(false);
button(NextButton)->setText(tr("Submitting"));
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index cb71b8d11..588bbd677 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -6,17 +6,18 @@
#include <QKeySequence>
#include <QSettings>
#include "common/file_util.h"
-#include "configure_input_simple.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "input_common/main.h"
#include "input_common/udp/client.h"
#include "yuzu/configuration/config.h"
+namespace FS = Common::FS;
+
Config::Config(const std::string& config_file, bool is_global) {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
- qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + config_file;
- FileUtil::CreateFullPath(qt_config_loc);
+ qt_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + config_file;
+ FS::CreateFullPath(qt_config_loc);
qt_config =
std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
global = is_global;
@@ -30,29 +31,31 @@ Config::~Config() {
}
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
- Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q,
- Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T,
- Qt::Key_H, Qt::Key_G, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, Qt::Key_Down, Qt::Key_J,
- Qt::Key_I, Qt::Key_L, Qt::Key_K, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
+ Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q,
+ Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T,
+ Qt::Key_H, Qt::Key_G, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
};
-const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
+const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
{
Qt::Key_Up,
Qt::Key_Down,
Qt::Key_Left,
Qt::Key_Right,
- Qt::Key_E,
},
{
Qt::Key_I,
Qt::Key_K,
Qt::Key_J,
Qt::Key_L,
- Qt::Key_R,
},
}};
+const std::array<int, 2> Config::default_stick_mod = {
+ Qt::Key_E,
+ Qt::Key_R,
+};
+
const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons =
{
Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal,
@@ -241,10 +244,10 @@ void Config::ReadPlayerValues() {
player.connected =
ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool();
- player.type = static_cast<Settings::ControllerType>(
+ player.controller_type = static_cast<Settings::ControllerType>(
qt_config
->value(QStringLiteral("player_%1_type").arg(p),
- static_cast<u8>(Settings::ControllerType::DualJoycon))
+ static_cast<u8>(Settings::ControllerType::ProController))
.toUInt());
player.body_color_left = qt_config
@@ -284,7 +287,7 @@ void Config::ReadPlayerValues() {
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
- default_analogs[i][3], default_analogs[i][4], 0.5f);
+ default_analogs[i][3], default_stick_mod[i], 0.5f);
auto& player_analogs = player.analogs[i];
player_analogs = qt_config
@@ -298,12 +301,6 @@ void Config::ReadPlayerValues() {
}
}
}
-
- std::stable_partition(
- Settings::values.players.begin(),
- Settings::values.players.begin() +
- Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
- [](const auto& player) { return player.connected; });
}
void Config::ReadDebugValues() {
@@ -328,7 +325,7 @@ void Config::ReadDebugValues() {
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
- default_analogs[i][3], default_analogs[i][4], 0.5f);
+ default_analogs[i][3], default_stick_mod[i], 0.5f);
auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
debug_pad_analogs = qt_config
@@ -395,13 +392,6 @@ void Config::ReadTouchscreenValues() {
ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt();
}
-void Config::ApplyDefaultProfileIfInputInvalid() {
- if (!std::any_of(Settings::values.players.begin(), Settings::values.players.end(),
- [](const Settings::PlayerInput& in) { return in.connected; })) {
- ApplyInputProfileConfiguration(UISettings::values.profile_index);
- }
-}
-
void Config::ReadAudioValues() {
qt_config->beginGroup(QStringLiteral("Audio"));
@@ -431,6 +421,8 @@ void Config::ReadControlValues() {
ReadMouseValues();
ReadTouchscreenValues();
+ Settings::values.vibration_enabled =
+ ReadSetting(QStringLiteral("vibration_enabled"), true).toBool();
Settings::values.motion_device =
ReadSetting(QStringLiteral("motion_device"),
QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"))
@@ -464,47 +456,42 @@ void Config::ReadDataStorageValues() {
qt_config->beginGroup(QStringLiteral("Data Storage"));
Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool();
- FileUtil::GetUserPath(
- FileUtil::UserPath::NANDDir,
- qt_config
- ->value(QStringLiteral("nand_directory"),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)))
- .toString()
- .toStdString());
- FileUtil::GetUserPath(
- FileUtil::UserPath::SDMCDir,
- qt_config
- ->value(QStringLiteral("sdmc_directory"),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)))
- .toString()
- .toStdString());
- FileUtil::GetUserPath(
- FileUtil::UserPath::LoadDir,
- qt_config
- ->value(QStringLiteral("load_directory"),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)))
- .toString()
- .toStdString());
- FileUtil::GetUserPath(
- FileUtil::UserPath::DumpDir,
- qt_config
- ->value(QStringLiteral("dump_directory"),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)))
- .toString()
- .toStdString());
- FileUtil::GetUserPath(
- FileUtil::UserPath::CacheDir,
- qt_config
- ->value(QStringLiteral("cache_directory"),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)))
- .toString()
- .toStdString());
+ FS::GetUserPath(FS::UserPath::NANDDir,
+ qt_config
+ ->value(QStringLiteral("nand_directory"),
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir)))
+ .toString()
+ .toStdString());
+ FS::GetUserPath(FS::UserPath::SDMCDir,
+ qt_config
+ ->value(QStringLiteral("sdmc_directory"),
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir)))
+ .toString()
+ .toStdString());
+ FS::GetUserPath(FS::UserPath::LoadDir,
+ qt_config
+ ->value(QStringLiteral("load_directory"),
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir)))
+ .toString()
+ .toStdString());
+ FS::GetUserPath(FS::UserPath::DumpDir,
+ qt_config
+ ->value(QStringLiteral("dump_directory"),
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)))
+ .toString()
+ .toStdString());
+ FS::GetUserPath(FS::UserPath::CacheDir,
+ qt_config
+ ->value(QStringLiteral("cache_directory"),
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::CacheDir)))
+ .toString()
+ .toStdString());
Settings::values.gamecard_inserted =
ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool();
Settings::values.gamecard_current_game =
ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool();
Settings::values.gamecard_path =
- ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString();
+ ReadSetting(QStringLiteral("gamecard_path"), QString{}).toString().toStdString();
qt_config->endGroup();
}
@@ -518,7 +505,7 @@ void Config::ReadDebuggingValues() {
Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
Settings::values.program_args =
- ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString();
+ ReadSetting(QStringLiteral("program_args"), QString{}).toString().toStdString();
Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool();
Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool();
Settings::values.reporting_services =
@@ -551,8 +538,7 @@ void Config::ReadDisabledAddOnValues() {
const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled"));
for (int j = 0; j < d_size; ++j) {
qt_config->setArrayIndex(j);
- out.push_back(
- ReadSetting(QStringLiteral("d"), QStringLiteral("")).toString().toStdString());
+ out.push_back(ReadSetting(QStringLiteral("d"), QString{}).toString().toStdString());
}
qt_config->endArray();
Settings::values.disabled_addons.insert_or_assign(title_id, out);
@@ -638,6 +624,11 @@ void Config::ReadCpuValues() {
ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool();
Settings::values.cpuopt_reduce_misalign_checks =
ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool();
+
+ Settings::values.cpuopt_unsafe_unfuse_fma =
+ ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool();
+ Settings::values.cpuopt_unsafe_reduce_fp_error =
+ ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool();
}
qt_config->endGroup();
@@ -677,11 +668,11 @@ void Config::ReadScreenshotValues() {
UISettings::values.enable_screenshot_save_as =
ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool();
- FileUtil::GetUserPath(
- FileUtil::UserPath::ScreenshotsDir,
+ FS::GetUserPath(
+ FS::UserPath::ScreenshotsDir,
qt_config
- ->value(QStringLiteral("screenshot_path"), QString::fromStdString(FileUtil::GetUserPath(
- FileUtil::UserPath::ScreenshotsDir)))
+ ->value(QStringLiteral("screenshot_path"),
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::ScreenshotsDir)))
.toString()
.toStdString());
@@ -786,14 +777,11 @@ void Config::ReadUIValues() {
UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool();
UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt();
UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool();
- UISettings::values.profile_index = ReadSetting(QStringLiteral("profileIndex"), 0).toUInt();
UISettings::values.pause_when_in_background =
ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool();
UISettings::values.hide_mouse =
ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool();
- ApplyDefaultProfileIfInputInvalid();
-
qt_config->endGroup();
}
@@ -867,8 +855,9 @@ void Config::SavePlayerValues() {
const auto& player = Settings::values.players[p];
WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false);
- WriteSetting(QStringLiteral("player_%1_type").arg(p), static_cast<u8>(player.type),
- static_cast<u8>(Settings::ControllerType::DualJoycon));
+ WriteSetting(QStringLiteral("player_%1_type").arg(p),
+ static_cast<u8>(player.controller_type),
+ static_cast<u8>(Settings::ControllerType::ProController));
WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left,
Settings::JOYCON_BODY_NEON_BLUE);
@@ -890,7 +879,7 @@ void Config::SavePlayerValues() {
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
- default_analogs[i][3], default_analogs[i][4], 0.5f);
+ default_analogs[i][3], default_stick_mod[i], 0.5f);
WriteSetting(QStringLiteral("player_%1_").arg(p) +
QString::fromStdString(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(player.analogs[i]),
@@ -911,7 +900,7 @@ void Config::SaveDebugValues() {
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
- default_analogs[i][3], default_analogs[i][4], 0.5f);
+ default_analogs[i][3], default_stick_mod[i], 0.5f);
WriteSetting(QStringLiteral("debug_pad_") +
QString::fromStdString(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(Settings::values.debug_pad_analogs[i]),
@@ -988,6 +977,7 @@ void Config::SaveControlValues() {
SaveMouseValues();
SaveTouchscreenValues();
+ WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true);
WriteSetting(QStringLiteral("motion_device"),
QString::fromStdString(Settings::values.motion_device),
QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
@@ -1016,25 +1006,25 @@ void Config::SaveDataStorageValues() {
WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true);
WriteSetting(QStringLiteral("nand_directory"),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir)),
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir)));
WriteSetting(QStringLiteral("sdmc_directory"),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir)),
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir)));
WriteSetting(QStringLiteral("load_directory"),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir)),
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir)));
WriteSetting(QStringLiteral("dump_directory"),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)),
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)));
WriteSetting(QStringLiteral("cache_directory"),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::CacheDir)),
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::CacheDir)));
WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false);
WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game,
false);
WriteSetting(QStringLiteral("gamecard_path"),
- QString::fromStdString(Settings::values.gamecard_path), QStringLiteral(""));
+ QString::fromStdString(Settings::values.gamecard_path), QString{});
qt_config->endGroup();
}
@@ -1047,7 +1037,7 @@ void Config::SaveDebuggingValues() {
WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
WriteSetting(QStringLiteral("program_args"),
- QString::fromStdString(Settings::values.program_args), QStringLiteral(""));
+ QString::fromStdString(Settings::values.program_args), QString{});
WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false);
WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
@@ -1074,8 +1064,7 @@ void Config::SaveDisabledAddOnValues() {
qt_config->beginWriteArray(QStringLiteral("disabled"));
for (std::size_t j = 0; j < elem.second.size(); ++j) {
qt_config->setArrayIndex(static_cast<int>(j));
- WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]),
- QStringLiteral(""));
+ WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), QString{});
}
qt_config->endArray();
++i;
@@ -1135,6 +1124,11 @@ void Config::SaveCpuValues() {
WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true);
WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"),
Settings::values.cpuopt_reduce_misalign_checks, true);
+
+ WriteSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"),
+ Settings::values.cpuopt_unsafe_unfuse_fma, true);
+ WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"),
+ Settings::values.cpuopt_unsafe_reduce_fp_error, true);
}
qt_config->endGroup();
@@ -1180,7 +1174,7 @@ void Config::SaveScreenshotValues() {
WriteSetting(QStringLiteral("enable_screenshot_save_as"),
UISettings::values.enable_screenshot_save_as);
WriteSetting(QStringLiteral("screenshot_path"),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir)));
+ QString::fromStdString(FS::GetUserPath(FS::UserPath::ScreenshotsDir)));
qt_config->endGroup();
}
@@ -1259,7 +1253,6 @@ void Config::SaveUIValues() {
WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true);
WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0);
WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false);
- WriteSetting(QStringLiteral("profileIndex"), UISettings::values.profile_index, 0);
WriteSetting(QStringLiteral("pauseWhenInBackground"),
UISettings::values.pause_when_in_background, false);
WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false);
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index e5f39b040..aa929d134 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -23,7 +23,8 @@ public:
void Save();
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
- static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
+ static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
+ static const std::array<int, 2> default_stick_mod;
static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
default_mouse_buttons;
static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
@@ -37,7 +38,6 @@ private:
void ReadKeyboardValues();
void ReadMouseValues();
void ReadTouchscreenValues();
- void ApplyDefaultProfileIfInputInvalid();
// Read functions bases off the respective config section names.
void ReadAudioValues();
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(
ConfigurationShared::USE_GLOBAL_OFFSET);
}
-void ConfigurationShared::SetHighlight(QWidget* widget, const std::string& name, bool highlighted) {
+void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) {
if (highlighted) {
widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }")
- .arg(QString::fromStdString(name)));
+ .arg(widget->objectName()));
} else {
widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,0,0,0) }")
- .arg(QString::fromStdString(name)));
+ .arg(widget->objectName()));
}
widget->show();
}
-void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, const std::string& name,
+void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox,
const Settings::Setting<bool>& setting,
CheckState& tracker) {
if (setting.UsingGlobal()) {
@@ -91,45 +91,39 @@ void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, const std::str
} else {
tracker = (setting.GetValue() == setting.GetValue(true)) ? CheckState::On : CheckState::Off;
}
- SetHighlight(checkbox, name, tracker != CheckState::Global);
- QObject::connect(checkbox, &QCheckBox::clicked, checkbox,
- [checkbox, name, setting, &tracker]() {
- tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
- static_cast<int>(CheckState::Count));
- if (tracker == CheckState::Global) {
- checkbox->setChecked(setting.GetValue(true));
- }
- SetHighlight(checkbox, name, tracker != CheckState::Global);
- });
+ SetHighlight(checkbox, tracker != CheckState::Global);
+ QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, setting, &tracker] {
+ tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
+ static_cast<int>(CheckState::Count));
+ if (tracker == CheckState::Global) {
+ checkbox->setChecked(setting.GetValue(true));
+ }
+ SetHighlight(checkbox, tracker != CheckState::Global);
+ });
}
-void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, const std::string& name,
- bool global, bool state, bool global_state,
- CheckState& tracker) {
+void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, bool global, bool state,
+ bool global_state, CheckState& tracker) {
if (global) {
tracker = CheckState::Global;
} else {
tracker = (state == global_state) ? CheckState::On : CheckState::Off;
}
- SetHighlight(checkbox, name, tracker != CheckState::Global);
- QObject::connect(checkbox, &QCheckBox::clicked, checkbox,
- [checkbox, name, global_state, &tracker]() {
- tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
- static_cast<int>(CheckState::Count));
- if (tracker == CheckState::Global) {
- checkbox->setChecked(global_state);
- }
- SetHighlight(checkbox, name, tracker != CheckState::Global);
- });
+ SetHighlight(checkbox, tracker != CheckState::Global);
+ QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, global_state, &tracker] {
+ tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
+ static_cast<int>(CheckState::Count));
+ if (tracker == CheckState::Global) {
+ checkbox->setChecked(global_state);
+ }
+ SetHighlight(checkbox, tracker != CheckState::Global);
+ });
}
-void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target,
- const std::string& target_name, int global) {
+void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target, int global) {
InsertGlobalItem(combobox, global);
- QObject::connect(combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), target,
- [target, target_name](int index) {
- ConfigurationShared::SetHighlight(target, target_name, index != 0);
- });
+ QObject::connect(combobox, qOverload<int>(&QComboBox::activated), target,
+ [target](int index) { SetHighlight(target, index != 0); });
}
void 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,
void SetPerGameSetting(QComboBox* combobox,
const Settings::Setting<Settings::GPUAccuracy>* setting);
-void SetHighlight(QWidget* widget, const std::string& name, bool highlighted);
-void SetColoredTristate(QCheckBox* checkbox, const std::string& name,
- const Settings::Setting<bool>& setting, CheckState& tracker);
-void SetColoredTristate(QCheckBox* checkbox, const std::string& name, bool global, bool state,
- bool global_state, CheckState& tracker);
-void SetColoredComboBox(QComboBox* combobox, QWidget* target, const std::string& target_name,
- int global);
+void SetHighlight(QWidget* widget, bool highlighted);
+void SetColoredTristate(QCheckBox* checkbox, const Settings::Setting<bool>& setting,
+ CheckState& tracker);
+void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state,
+ CheckState& tracker);
+void SetColoredComboBox(QComboBox* combobox, QWidget* target, int global);
void InsertGlobalItem(QComboBox* combobox, int global_index);
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 5f5d8e571..fcf42cdcb 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
- <width>382</width>
+ <width>650</width>
<height>650</height>
</rect>
</property>
@@ -26,13 +26,13 @@
<widget class="QListWidget" name="selectorList">
<property name="minimumSize">
<size>
- <width>150</width>
+ <width>120</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>150</width>
+ <width>120</width>
<height>16777215</height>
</size>
</property>
@@ -44,76 +44,121 @@
<number>0</number>
</property>
<widget class="ConfigureGeneral" name="generalTab">
+ <property name="accessibleName">
+ <string>General</string>
+ </property>
<attribute name="title">
<string>General</string>
</attribute>
</widget>
<widget class="ConfigureUi" name="uiTab">
+ <property name="accessibleName">
+ <string>UI</string>
+ </property>
<attribute name="title">
<string>Game List</string>
</attribute>
</widget>
<widget class="ConfigureSystem" name="systemTab">
+ <property name="accessibleName">
+ <string>System</string>
+ </property>
<attribute name="title">
<string>System</string>
</attribute>
</widget>
<widget class="ConfigureProfileManager" name="profileManagerTab">
+ <property name="accessibleName">
+ <string>Profiles</string>
+ </property>
<attribute name="title">
<string>Profiles</string>
</attribute>
</widget>
<widget class="ConfigureFilesystem" name="filesystemTab">
+ <property name="accessibleName">
+ <string>Filesystem</string>
+ </property>
<attribute name="title">
<string>Filesystem</string>
</attribute>
</widget>
- <widget class="ConfigureInputSimple" name="inputTab">
+ <widget class="ConfigureInput" name="inputTab">
+ <property name="accessibleName">
+ <string>Controls</string>
+ </property>
<attribute name="title">
- <string>Input</string>
+ <string>Controls</string>
</attribute>
</widget>
<widget class="ConfigureHotkeys" name="hotkeysTab">
+ <property name="accessibleName">
+ <string>Hotkeys</string>
+ </property>
<attribute name="title">
<string>Hotkeys</string>
</attribute>
</widget>
<widget class="ConfigureCpu" name="cpuTab">
+ <property name="accessibleName">
+ <string>CPU</string>
+ </property>
<attribute name="title">
<string>CPU</string>
</attribute>
</widget>
<widget class="ConfigureCpuDebug" name="cpuDebugTab">
+ <property name="accessibleName">
+ <string>Debug</string>
+ </property>
<attribute name="title">
<string>Debug</string>
</attribute>
</widget>
<widget class="ConfigureGraphics" name="graphicsTab">
+ <property name="accessibleName">
+ <string>Graphics</string>
+ </property>
<attribute name="title">
<string>Graphics</string>
</attribute>
</widget>
<widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab">
+ <property name="accessibleName">
+ <string>Advanced</string>
+ </property>
<attribute name="title">
<string>GraphicsAdvanced</string>
</attribute>
</widget>
<widget class="ConfigureAudio" name="audioTab">
+ <property name="accessibleName">
+ <string>Audio</string>
+ </property>
<attribute name="title">
<string>Audio</string>
</attribute>
</widget>
<widget class="ConfigureDebug" name="debugTab">
+ <property name="accessibleName">
+ <string>Debug</string>
+ </property>
<attribute name="title">
<string>Debug</string>
</attribute>
</widget>
<widget class="ConfigureWeb" name="webTab">
+ <property name="accessibleName">
+ <string>Web</string>
+ </property>
<attribute name="title">
<string>Web</string>
</attribute>
</widget>
<widget class="ConfigureService" name="serviceTab">
+ <property name="accessibleName">
+ <string>Services</string>
+ </property>
<attribute name="title">
<string>Services</string>
</attribute>
@@ -205,9 +250,9 @@
<container>1</container>
</customwidget>
<customwidget>
- <class>ConfigureInputSimple</class>
+ <class>ConfigureInput</class>
<extends>QWidget</extends>
- <header>configuration/configure_input_simple.h</header>
+ <header>configuration/configure_input.h</header>
<container>1</container>
</customwidget>
<customwidget>
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() {
ui->volume_combo_box->setCurrentIndex(1);
ui->volume_slider->setEnabled(true);
}
- ConfigurationShared::SetHighlight(ui->volume_layout, "volume_layout",
+ ConfigurationShared::SetHighlight(ui->volume_layout,
!Settings::values.volume.UsingGlobal());
}
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
@@ -173,14 +173,13 @@ void ConfigureAudio::SetupPerGameUI() {
return;
}
- ConfigurationShared::SetColoredTristate(ui->toggle_audio_stretching, "toggle_audio_stretching",
+ ConfigurationShared::SetColoredTristate(ui->toggle_audio_stretching,
Settings::values.enable_audio_stretching,
enable_audio_stretching);
- connect(ui->volume_combo_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
- this, [this](int index) {
- ui->volume_slider->setEnabled(index == 1);
- ConfigurationShared::SetHighlight(ui->volume_layout, "volume_layout", index == 1);
- });
+ connect(ui->volume_combo_box, qOverload<int>(&QComboBox::activated), this, [this](int index) {
+ ui->volume_slider->setEnabled(index == 1);
+ ConfigurationShared::SetHighlight(ui->volume_layout, index == 1);
+ });
ui->output_sink_combo_box->setVisible(false);
ui->output_sink_label->setVisible(false);
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index 7493e5ffb..37fcd6adc 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -19,6 +19,8 @@ ConfigureCpu::ConfigureCpu(QWidget* parent) : QWidget(parent), ui(new Ui::Config
connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this,
&ConfigureCpu::AccuracyUpdated);
+ connect(ui->accuracy, qOverload<int>(&QComboBox::currentIndexChanged), this,
+ &ConfigureCpu::UpdateGroup);
}
ConfigureCpu::~ConfigureCpu() = default;
@@ -28,6 +30,12 @@ void ConfigureCpu::SetConfiguration() {
ui->accuracy->setEnabled(runtime_lock);
ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy));
+ UpdateGroup(static_cast<int>(Settings::values.cpu_accuracy));
+
+ ui->cpuopt_unsafe_unfuse_fma->setEnabled(runtime_lock);
+ ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma);
+ ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock);
+ ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error);
}
void ConfigureCpu::AccuracyUpdated(int index) {
@@ -38,14 +46,21 @@ void ConfigureCpu::AccuracyUpdated(int index) {
QMessageBox::Yes | QMessageBox::No);
if (result == QMessageBox::No) {
ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate));
- return;
+ UpdateGroup(static_cast<int>(Settings::CPUAccuracy::Accurate));
}
}
}
+void ConfigureCpu::UpdateGroup(int index) {
+ ui->unsafe_group->setVisible(static_cast<Settings::CPUAccuracy>(index) ==
+ Settings::CPUAccuracy::Unsafe);
+}
+
void ConfigureCpu::ApplyConfiguration() {
Settings::values.cpu_accuracy =
static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex());
+ Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked();
+ Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked();
}
void ConfigureCpu::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h
index e4741d3a4..3c5683d81 100644
--- a/src/yuzu/configuration/configure_cpu.h
+++ b/src/yuzu/configuration/configure_cpu.h
@@ -26,6 +26,7 @@ private:
void RetranslateUI();
void AccuracyUpdated(int index);
+ void UpdateGroup(int index);
void SetConfiguration();
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
index bf6ea79bb..ebdd2e6e9 100644
--- a/src/yuzu/configuration/configure_cpu.ui
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -40,6 +40,11 @@
</item>
<item>
<property name="text">
+ <string>Unsafe</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
<string>Enable Debug Mode</string>
</property>
</item>
@@ -63,6 +68,53 @@
</layout>
</item>
<item>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QGroupBox" name="unsafe_group">
+ <property name="title">
+ <string>Unsafe CPU Optimization Settings</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QLabel">
+ <property name="wordWrap">
+ <bool>1</bool>
+ </property>
+ <property name="text">
+ <string>These settings reduce accuracy for speed.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="cpuopt_unsafe_unfuse_fma">
+ <property name="text">
+ <string>Unfuse FMA (improve performance on CPUs without FMA)</string>
+ </property>
+ <property name="toolTip">
+ <string>
+ &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
+ </string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="cpuopt_unsafe_reduce_fp_error">
+ <property name="text">
+ <string>Faster FRSQRTE and FRECPE</string>
+ </property>
+ <property name="toolTip">
+ <string>
+ &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
+ </string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index d0e71dd60..2bfe2c306 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -19,7 +19,8 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
SetConfiguration();
connect(ui->open_log_button, &QPushButton::clicked, []() {
- QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
+ const auto path =
+ QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::LogDir));
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
});
}
diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp
new file mode 100644
index 000000000..0097c9a29
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_controller.cpp
@@ -0,0 +1,40 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "ui_configure_debug_controller.h"
+#include "yuzu/configuration/configure_debug_controller.h"
+
+ConfigureDebugController::ConfigureDebugController(QWidget* parent,
+ InputCommon::InputSubsystem* input_subsystem)
+ : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()),
+ debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, true)) {
+ ui->setupUi(this);
+
+ ui->controllerLayout->addWidget(debug_controller);
+
+ connect(ui->clear_all_button, &QPushButton::clicked, this,
+ [this] { debug_controller->ClearAll(); });
+ connect(ui->restore_defaults_button, &QPushButton::clicked, this,
+ [this] { debug_controller->RestoreDefaults(); });
+
+ RetranslateUI();
+}
+
+ConfigureDebugController::~ConfigureDebugController() = default;
+
+void ConfigureDebugController::ApplyConfiguration() {
+ debug_controller->ApplyConfiguration();
+}
+
+void ConfigureDebugController::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QDialog::changeEvent(event);
+}
+
+void ConfigureDebugController::RetranslateUI() {
+ ui->retranslateUi(this);
+}
diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h
new file mode 100644
index 000000000..34dcf705f
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_controller.h
@@ -0,0 +1,38 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QDialog>
+#include "yuzu/configuration/configure_input_player.h"
+
+class QPushButton;
+
+namespace InputCommon {
+class InputSubsystem;
+}
+
+namespace Ui {
+class ConfigureDebugController;
+}
+
+class ConfigureDebugController : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit ConfigureDebugController(QWidget* parent,
+ InputCommon::InputSubsystem* input_subsystem);
+ ~ConfigureDebugController() override;
+
+ void ApplyConfiguration();
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+
+ std::unique_ptr<Ui::ConfigureDebugController> ui;
+
+ ConfigureInputPlayer* debug_controller;
+};
diff --git a/src/yuzu/configuration/configure_debug_controller.ui b/src/yuzu/configuration/configure_debug_controller.ui
new file mode 100644
index 000000000..a95ed50ff
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_controller.ui
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureDebugController</class>
+ <widget class="QDialog" name="ConfigureDebugController">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>780</width>
+ <height>500</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Debug Controller</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ <property name="leftMargin">
+ <number>9</number>
+ </property>
+ <property name="topMargin">
+ <number>9</number>
+ </property>
+ <property name="rightMargin">
+ <number>9</number>
+ </property>
+ <property name="bottomMargin">
+ <number>9</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="controllerLayout"/>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="clear_all_button">
+ <property name="text">
+ <string>Clear</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="restore_defaults_button">
+ <property name="text">
+ <string>Defaults</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigureDebugController</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>140</x>
+ <y>318</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>140</x>
+ <y>169</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ConfigureDebugController</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>140</x>
+ <y>318</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>140</x>
+ <y>169</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 4e30dc51e..8186929a6 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -12,7 +12,8 @@
#include "yuzu/configuration/configure_input_player.h"
#include "yuzu/hotkeys.h"
-ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
+ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
+ InputCommon::InputSubsystem* input_subsystem)
: QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
Settings::configuring_global = true;
@@ -20,6 +21,8 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
ui->hotkeysTab->Populate(registry);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ ui->inputTab->Initialize(input_subsystem);
+
SetConfiguration();
PopulateSelectionList();
@@ -80,12 +83,12 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
void ConfigureDialog::PopulateSelectionList() {
const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
- {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}},
+ {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}},
{tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}},
{tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}},
{tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
{tr("Audio"), {ui->audioTab}},
- {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}},
+ {tr("Controls"), ui->inputTab->GetSubTabs()}},
};
[[maybe_unused]] const QSignalBlocker blocker(ui->selectorList);
@@ -117,7 +120,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
{ui->generalTab, tr("General")},
{ui->systemTab, tr("System")},
{ui->profileManagerTab, tr("Profiles")},
- {ui->inputTab, tr("Input")},
+ {ui->inputTab, tr("Controls")},
{ui->hotkeysTab, tr("Hotkeys")},
{ui->cpuTab, tr("CPU")},
{ui->cpuDebugTab, tr("Debug")},
@@ -138,6 +141,6 @@ void ConfigureDialog::UpdateVisibleTabs() {
const QList<QWidget*> tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole));
for (const auto tab : tabs) {
- ui->tabWidget->addTab(tab, widgets.at(tab));
+ ui->tabWidget->addTab(tab, tab->accessibleName());
}
}
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 4289bc225..570c3b941 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -9,6 +9,10 @@
class HotkeyRegistry;
+namespace InputCommon {
+class InputSubsystem;
+}
+
namespace Ui {
class ConfigureDialog;
}
@@ -17,7 +21,8 @@ class ConfigureDialog : public QDialog {
Q_OBJECT
public:
- explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry);
+ explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
+ InputCommon::InputSubsystem* input_subsystem);
~ConfigureDialog() override;
void ApplyConfiguration();
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;
void ConfigureFilesystem::setConfiguration() {
ui->nand_directory_edit->setText(
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
+ QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)));
ui->sdmc_directory_edit->setText(
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
+ QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)));
ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path));
ui->dump_path_edit->setText(
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
+ QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir)));
ui->load_path_edit->setText(
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
+ QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir)));
ui->cache_directory_edit->setText(
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
+ QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)));
ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted);
ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game);
@@ -64,14 +64,16 @@ void ConfigureFilesystem::setConfiguration() {
}
void ConfigureFilesystem::applyConfiguration() {
- FileUtil::GetUserPath(FileUtil::UserPath::NANDDir,
- ui->nand_directory_edit->text().toStdString());
- FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
- ui->sdmc_directory_edit->text().toStdString());
- FileUtil::GetUserPath(FileUtil::UserPath::DumpDir, ui->dump_path_edit->text().toStdString());
- FileUtil::GetUserPath(FileUtil::UserPath::LoadDir, ui->load_path_edit->text().toStdString());
- FileUtil::GetUserPath(FileUtil::UserPath::CacheDir,
- ui->cache_directory_edit->text().toStdString());
+ Common::FS::GetUserPath(Common::FS::UserPath::NANDDir,
+ ui->nand_directory_edit->text().toStdString());
+ Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir,
+ ui->sdmc_directory_edit->text().toStdString());
+ Common::FS::GetUserPath(Common::FS::UserPath::DumpDir,
+ ui->dump_path_edit->text().toStdString());
+ Common::FS::GetUserPath(Common::FS::UserPath::LoadDir,
+ ui->load_path_edit->text().toStdString());
+ Common::FS::GetUserPath(Common::FS::UserPath::CacheDir,
+ ui->cache_directory_edit->text().toStdString());
Settings::values.gamecard_path = ui->gamecard_path_edit->text().toStdString();
Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked();
@@ -121,12 +123,13 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit)
}
void ConfigureFilesystem::ResetMetadata() {
- if (!FileUtil::Exists(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
- "game_list")) {
+ if (!Common::FS::Exists(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
+ "game_list")) {
QMessageBox::information(this, tr("Reset Metadata Cache"),
tr("The metadata cache is already empty."));
- } else if (FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
- DIR_SEP + "game_list")) {
+ } else if (Common::FS::DeleteDirRecursively(
+ Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
+ "game_list")) {
QMessageBox::information(this, tr("Reset Metadata Cache"),
tr("The operation completed successfully."));
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() {
ui->toggle_background_pause->setVisible(false);
ui->toggle_hide_mouse->setVisible(false);
- ConfigurationShared::SetColoredTristate(ui->toggle_frame_limit, "toggle_frame_limit",
+ ConfigurationShared::SetColoredTristate(ui->toggle_frame_limit,
Settings::values.use_frame_limit, use_frame_limit);
- ConfigurationShared::SetColoredTristate(ui->use_multi_core, "use_multi_core",
- Settings::values.use_multi_core, use_multi_core);
+ ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core,
+ use_multi_core);
connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, [this]() {
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)
connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
UpdateDeviceComboBox();
if (!Settings::configuring_global) {
- ConfigurationShared::SetHighlight(ui->api_layout, "api_layout",
- ui->api->currentIndex() !=
- ConfigurationShared::USE_GLOBAL_INDEX);
+ ConfigurationShared::SetHighlight(
+ ui->api_layout, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX);
}
});
connect(ui->device, qOverload<int>(&QComboBox::activated), this,
@@ -80,17 +79,16 @@ void ConfigureGraphics::SetConfiguration() {
ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
} else {
ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
- ConfigurationShared::SetHighlight(ui->api_layout, "api_layout",
+ ConfigurationShared::SetHighlight(ui->api_layout,
!Settings::values.renderer_backend.UsingGlobal());
ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox,
&Settings::values.aspect_ratio);
ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
- ConfigurationShared::SetHighlight(ui->ar_label, "ar_label",
+ ConfigurationShared::SetHighlight(ui->ar_label,
!Settings::values.aspect_ratio.UsingGlobal());
- ConfigurationShared::SetHighlight(ui->bg_layout, "bg_layout",
- !Settings::values.bg_red.UsingGlobal());
+ ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal());
}
UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(),
@@ -248,20 +246,18 @@ void ConfigureGraphics::SetupPerGameUI() {
return;
}
- connect(ui->bg_combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
- [this](int index) {
- ui->bg_button->setEnabled(index == 1);
- ConfigurationShared::SetHighlight(ui->bg_layout, "bg_layout", index == 1);
- });
+ connect(ui->bg_combobox, qOverload<int>(&QComboBox::activated), this, [this](int index) {
+ ui->bg_button->setEnabled(index == 1);
+ ConfigurationShared::SetHighlight(ui->bg_layout, index == 1);
+ });
- ConfigurationShared::SetColoredTristate(ui->use_disk_shader_cache, "use_disk_shader_cache",
- Settings::values.use_disk_shader_cache,
- use_disk_shader_cache);
ConfigurationShared::SetColoredTristate(
- ui->use_asynchronous_gpu_emulation, "use_asynchronous_gpu_emulation",
- Settings::values.use_asynchronous_gpu_emulation, use_asynchronous_gpu_emulation);
+ ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache);
+ ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation,
+ Settings::values.use_asynchronous_gpu_emulation,
+ use_asynchronous_gpu_emulation);
- ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label, "ar_label",
+ ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label,
Settings::values.aspect_ratio.GetValue(true));
ConfigurationShared::InsertGlobalItem(
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() {
ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy);
ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox,
&Settings::values.max_anisotropy);
- ConfigurationShared::SetHighlight(ui->label_gpu_accuracy, "label_gpu_accuracy",
+ ConfigurationShared::SetHighlight(ui->label_gpu_accuracy,
!Settings::values.gpu_accuracy.UsingGlobal());
- ConfigurationShared::SetHighlight(ui->af_label, "af_label",
+ ConfigurationShared::SetHighlight(ui->af_label,
!Settings::values.max_anisotropy.UsingGlobal());
}
}
@@ -131,20 +131,18 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
return;
}
- ConfigurationShared::SetColoredTristate(ui->use_vsync, "use_vsync", Settings::values.use_vsync,
- use_vsync);
- ConfigurationShared::SetColoredTristate(ui->use_assembly_shaders, "use_assembly_shaders",
- Settings::values.use_assembly_shaders,
- use_assembly_shaders);
+ ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync);
ConfigurationShared::SetColoredTristate(
- ui->use_asynchronous_shaders, "use_asynchronous_shaders",
- Settings::values.use_asynchronous_shaders, use_asynchronous_shaders);
- ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, "use_fast_gpu_time",
+ ui->use_assembly_shaders, Settings::values.use_assembly_shaders, use_assembly_shaders);
+ ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders,
+ Settings::values.use_asynchronous_shaders,
+ use_asynchronous_shaders);
+ ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time,
Settings::values.use_fast_gpu_time, use_fast_gpu_time);
ConfigurationShared::SetColoredComboBox(
- ui->gpu_accuracy, ui->label_gpu_accuracy, "label_gpu_accuracy",
+ ui->gpu_accuracy, ui->label_gpu_accuracy,
static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
ConfigurationShared::SetColoredComboBox(
- ui->anisotropic_filtering_combobox, ui->af_label, "af_label",
+ ui->anisotropic_filtering_combobox, ui->af_label,
static_cast<int>(Settings::values.max_anisotropy.GetValue(true)));
}
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 @@
<string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
</property>
<property name="text">
- <string>Use asynchronous shader building (experimental, OpenGL or Assembly shaders only)</string>
+ <string>Use asynchronous shader building (experimental)</string>
</property>
</widget>
</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() {
const QStandardItem* parent = model->item(r, 0);
for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
- model->item(r, 0)->child(r2, 1)->setText(tr(""));
+ model->item(r, 0)->child(r2, 1)->setText(QString{});
}
}
}
@@ -186,7 +186,7 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) {
model->setData(selected, default_key_sequence.toString(QKeySequence::NativeText));
}
});
- connect(clear, &QAction::triggered, [this, selected] { model->setData(selected, tr("")); });
+ connect(clear, &QAction::triggered, [this, selected] { model->setData(selected, QString{}); });
context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location));
}
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index f2977719c..5223eed1d 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -8,18 +8,32 @@
#include <QSignalBlocker>
#include <QTimer>
-#include "configuration/configure_touchscreen_advanced.h"
#include "core/core.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
-#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/sm/sm.h"
#include "ui_configure_input.h"
+#include "ui_configure_input_advanced.h"
#include "ui_configure_input_player.h"
+#include "yuzu/configuration/configure_debug_controller.h"
#include "yuzu/configuration/configure_input.h"
+#include "yuzu/configuration/configure_input_advanced.h"
#include "yuzu/configuration/configure_input_player.h"
#include "yuzu/configuration/configure_mouse_advanced.h"
+#include "yuzu/configuration/configure_touchscreen_advanced.h"
+
+namespace {
+template <typename Dialog, typename... Args>
+void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
+ Dialog dialog(&parent, std::forward<Args>(args)...);
+
+ const auto res = dialog.exec();
+ if (res == QDialog::Accepted) {
+ dialog.ApplyConfiguration();
+ }
+}
+} // Anonymous namespace
void OnDockedModeChanged(bool last_state, bool new_state) {
if (last_state == new_state) {
@@ -48,97 +62,98 @@ void OnDockedModeChanged(bool last_state, bool new_state) {
}
}
-namespace {
-template <typename Dialog, typename... Args>
-void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
- parent.ApplyConfiguration();
- Dialog dialog(&parent, std::forward<Args>(args)...);
-
- const auto res = dialog.exec();
- if (res == QDialog::Accepted) {
- dialog.ApplyConfiguration();
- }
-}
-} // Anonymous namespace
-
ConfigureInput::ConfigureInput(QWidget* parent)
- : QDialog(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
+ : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
ui->setupUi(this);
+}
- players_controller = {
- ui->player1_combobox, ui->player2_combobox, ui->player3_combobox, ui->player4_combobox,
- ui->player5_combobox, ui->player6_combobox, ui->player7_combobox, ui->player8_combobox,
- };
+ConfigureInput::~ConfigureInput() = default;
- players_configure = {
- ui->player1_configure, ui->player2_configure, ui->player3_configure, ui->player4_configure,
- ui->player5_configure, ui->player6_configure, ui->player7_configure, ui->player8_configure,
+void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
+ player_controllers = {
+ new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem),
+ new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem),
+ new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem),
+ new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem),
+ new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem),
+ new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem),
+ new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem),
+ new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem),
};
- RetranslateUI();
- LoadConfiguration();
- UpdateUIEnabled();
+ player_tabs = {
+ ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4,
+ ui->tabPlayer5, ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8,
+ };
- connect(ui->restore_defaults_button, &QPushButton::clicked, this,
- &ConfigureInput::RestoreDefaults);
+ player_connected = {
+ ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
+ ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
+ ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
+ };
- for (auto* enabled : players_controller) {
- connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
- &ConfigureInput::UpdateUIEnabled);
- }
- connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
- connect(ui->handheld_connected, &QCheckBox::stateChanged, this,
- &ConfigureInput::UpdateUIEnabled);
- connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
- connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
- connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
- connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
- &ConfigureInput::UpdateUIEnabled);
-
- for (std::size_t i = 0; i < players_configure.size(); ++i) {
- connect(players_configure[i], &QPushButton::clicked, this,
- [this, i] { CallConfigureDialog<ConfigureInputPlayer>(*this, i, false); });
+ for (std::size_t i = 0; i < player_tabs.size(); ++i) {
+ player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
+ player_tabs[i]->layout()->addWidget(player_controllers[i]);
+ connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) {
+ if (is_connected) {
+ for (std::size_t index = 0; index <= i; ++index) {
+ player_connected[index]->setChecked(is_connected);
+ }
+ } else {
+ for (std::size_t index = i; index < player_tabs.size(); ++index) {
+ player_connected[index]->setChecked(is_connected);
+ }
+ }
+ });
+ connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices,
+ [this] { UpdateAllInputDevices(); });
+ connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) {
+ player_controllers[i]->ConnectPlayer(state == Qt::Checked);
+ });
}
+ // Only the first player can choose handheld mode so connect the signal just to player 1
+ connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged,
+ [this](bool is_handheld) { UpdateDockedState(is_handheld); });
+
+ advanced = new ConfigureInputAdvanced(this);
+ ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
+ ui->tabAdvanced->layout()->addWidget(advanced);
+ connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] {
+ CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem);
+ });
+ connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] {
+ CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem);
+ });
+ connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog,
+ [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); });
- connect(ui->handheld_configure, &QPushButton::clicked, this,
- [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 8, false); });
-
- connect(ui->debug_configure, &QPushButton::clicked, this,
- [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 9, true); });
-
- connect(ui->mouse_advanced, &QPushButton::clicked, this,
- [this] { CallConfigureDialog<ConfigureMouseAdvanced>(*this); });
+ connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
+ connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
- connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
- [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); });
+ RetranslateUI();
+ LoadConfiguration();
}
-ConfigureInput::~ConfigureInput() = default;
+QList<QWidget*> ConfigureInput::GetSubTabs() const {
+ return {
+ ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5,
+ ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8, ui->tabAdvanced,
+ };
+}
void ConfigureInput::ApplyConfiguration() {
- for (std::size_t i = 0; i < players_controller.size(); ++i) {
- const auto controller_type_index = players_controller[i]->currentIndex();
-
- Settings::values.players[i].connected = controller_type_index != 0;
-
- if (controller_type_index > 0) {
- Settings::values.players[i].type =
- static_cast<Settings::ControllerType>(controller_type_index - 1);
- } else {
- Settings::values.players[i].type = Settings::ControllerType::DualJoycon;
- }
+ for (auto controller : player_controllers) {
+ controller->ApplyConfiguration();
}
+ advanced->ApplyConfiguration();
+
const bool pre_docked_mode = Settings::values.use_docked_mode;
- Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
+ Settings::values.use_docked_mode = ui->radioDocked->isChecked();
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
- Settings::values
- .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
- .connected = ui->handheld_connected->isChecked();
- Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
- Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
- Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
- Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
+
+ Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
}
void ConfigureInput::changeEvent(QEvent* event) {
@@ -146,94 +161,63 @@ void ConfigureInput::changeEvent(QEvent* event) {
RetranslateUI();
}
- QDialog::changeEvent(event);
+ QWidget::changeEvent(event);
}
void ConfigureInput::RetranslateUI() {
ui->retranslateUi(this);
- RetranslateControllerComboBoxes();
}
-void ConfigureInput::RetranslateControllerComboBoxes() {
- for (auto* controller_box : players_controller) {
- [[maybe_unused]] const QSignalBlocker blocker(controller_box);
-
- controller_box->clear();
- controller_box->addItems({tr("None"), tr("Pro Controller"), tr("Dual Joycons"),
- tr("Single Right Joycon"), tr("Single Left Joycon")});
- }
-
+void ConfigureInput::LoadConfiguration() {
LoadPlayerControllerIndices();
-}
+ UpdateDockedState(Settings::values.players[0].controller_type ==
+ Settings::ControllerType::Handheld);
-void ConfigureInput::UpdateUIEnabled() {
- bool hit_disabled = false;
- for (auto* player : players_controller) {
- player->setDisabled(hit_disabled);
- if (hit_disabled) {
- player->setCurrentIndex(0);
- }
- if (!hit_disabled && player->currentIndex() == 0) {
- hit_disabled = true;
- }
- }
+ ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
+}
- for (std::size_t i = 0; i < players_controller.size(); ++i) {
- players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0);
+void ConfigureInput::LoadPlayerControllerIndices() {
+ for (std::size_t i = 0; i < player_connected.size(); ++i) {
+ const auto connected = Settings::values.players[i].connected ||
+ (i == 0 && Settings::values.players[8].connected);
+ player_connected[i]->setChecked(connected);
}
+}
- ui->handheld_connected->setChecked(ui->handheld_connected->isChecked() &&
- !ui->use_docked_mode->isChecked());
- ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked());
- ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() &&
- !ui->use_docked_mode->isChecked());
- ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
- ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
- ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
+void ConfigureInput::ClearAll() {
+ // We don't have a good way to know what tab is active, but we can find out by getting the
+ // parent of the consoleInputSettings
+ auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent());
+ player_tab->ClearAll();
}
-void ConfigureInput::LoadConfiguration() {
- std::stable_partition(
- Settings::values.players.begin(),
- Settings::values.players.begin() +
- Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
- [](const auto& player) { return player.connected; });
+void ConfigureInput::RestoreDefaults() {
+ // We don't have a good way to know what tab is active, but we can find out by getting the
+ // parent of the consoleInputSettings
+ auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent());
+ player_tab->RestoreDefaults();
+
+ ui->radioDocked->setChecked(true);
+ ui->radioUndocked->setChecked(false);
+ ui->vibrationGroup->setChecked(true);
+}
- LoadPlayerControllerIndices();
+void ConfigureInput::UpdateDockedState(bool is_handheld) {
+ // If the controller type is handheld only, disallow changing docked mode
+ ui->radioDocked->setEnabled(!is_handheld);
+ ui->radioUndocked->setEnabled(!is_handheld);
- ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
- ui->handheld_connected->setChecked(
- Settings::values
- .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
- .connected);
- ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
- ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
- ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
- ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
-
- UpdateUIEnabled();
-}
+ ui->radioDocked->setChecked(Settings::values.use_docked_mode);
+ ui->radioUndocked->setChecked(!Settings::values.use_docked_mode);
-void ConfigureInput::LoadPlayerControllerIndices() {
- for (std::size_t i = 0; i < players_controller.size(); ++i) {
- const auto connected = Settings::values.players[i].connected;
- players_controller[i]->setCurrentIndex(
- connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0);
+ // If its handheld only, force docked mode off (since you can't play handheld in a dock)
+ if (is_handheld) {
+ ui->radioUndocked->setChecked(true);
}
}
-void ConfigureInput::RestoreDefaults() {
- players_controller[0]->setCurrentIndex(2);
-
- for (std::size_t i = 1; i < players_controller.size(); ++i) {
- players_controller[i]->setCurrentIndex(0);
+void ConfigureInput::UpdateAllInputDevices() {
+ for (const auto& player : player_controllers) {
+ player->UpdateInputDevices();
}
-
- ui->use_docked_mode->setCheckState(Qt::Unchecked);
- ui->handheld_connected->setCheckState(Qt::Unchecked);
- ui->mouse_enabled->setCheckState(Qt::Unchecked);
- ui->keyboard_enabled->setCheckState(Qt::Unchecked);
- ui->debug_enabled->setCheckState(Qt::Unchecked);
- ui->touchscreen_enabled->setCheckState(Qt::Checked);
- UpdateUIEnabled();
}
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 2f70cb3ca..d08a24f96 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -7,37 +7,50 @@
#include <array>
#include <memory>
-#include <QDialog>
#include <QKeyEvent>
+#include <QWidget>
+
+#include "yuzu/configuration/configure_input_advanced.h"
+#include "yuzu/configuration/configure_input_player.h"
#include "ui_configure_input.h"
-class QPushButton;
+class QCheckBox;
class QString;
class QTimer;
+namespace InputCommon {
+class InputSubsystem;
+}
+
namespace Ui {
class ConfigureInput;
}
void OnDockedModeChanged(bool last_state, bool new_state);
-class ConfigureInput : public QDialog {
+class ConfigureInput : public QWidget {
Q_OBJECT
public:
explicit ConfigureInput(QWidget* parent = nullptr);
~ConfigureInput() override;
- /// Save all button configurations to settings file
+ /// Initializes the input dialog with the given input subsystem.
+ void Initialize(InputCommon::InputSubsystem* input_subsystem_);
+
+ /// Save all button configurations to settings file.
void ApplyConfiguration();
+ QList<QWidget*> GetSubTabs() const;
+
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
- void RetranslateControllerComboBoxes();
+ void ClearAll();
- void UpdateUIEnabled();
+ void UpdateDockedState(bool is_handheld);
+ void UpdateAllInputDevices();
/// Load configuration settings.
void LoadConfiguration();
@@ -48,6 +61,8 @@ private:
std::unique_ptr<Ui::ConfigureInput> ui;
- std::array<QComboBox*, 8> players_controller;
- std::array<QPushButton*, 8> players_configure;
+ std::array<ConfigureInputPlayer*, 8> player_controllers;
+ std::array<QWidget*, 8> player_tabs;
+ std::array<QCheckBox*, 8> player_connected;
+ ConfigureInputAdvanced* advanced;
};
diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui
index efffd8487..136955224 100644
--- a/src/yuzu/configuration/configure_input.ui
+++ b/src/yuzu/configuration/configure_input.ui
@@ -1,529 +1,554 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureInput</class>
- <widget class="QDialog" name="ConfigureInput">
+ <widget class="QWidget" name="ConfigureInput">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>384</width>
- <height>576</height>
+ <width>700</width>
+ <height>540</height>
</rect>
</property>
<property name="windowTitle">
- <string>Custom Input Settings</string>
+ <string>ConfigureInput</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
<item>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QGroupBox" name="gridGroupBox_1">
- <property name="title">
- <string>Players</string>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="1" column="2">
- <widget class="QComboBox" name="player1_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="1" column="3">
- <widget class="QPushButton" name="player1_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Controller Type</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item row="2" column="2">
- <widget class="QComboBox" name="player2_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="3" column="2">
- <widget class="QComboBox" name="player3_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="4" column="2">
- <widget class="QComboBox" name="player4_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="5" column="2">
- <widget class="QComboBox" name="player5_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="6" column="2">
- <widget class="QComboBox" name="player6_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="7" column="2">
- <widget class="QComboBox" name="player7_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="8" column="2">
- <widget class="QComboBox" name="player8_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="2" column="3">
- <widget class="QPushButton" name="player2_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="3" column="3">
- <widget class="QPushButton" name="player3_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="4" column="3">
- <widget class="QPushButton" name="player4_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="5" column="3">
- <widget class="QPushButton" name="player5_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="6" column="3">
- <widget class="QPushButton" name="player6_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="7" column="3">
- <widget class="QPushButton" name="player7_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="8" column="3">
- <widget class="QPushButton" name="player8_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="4">
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="label_3">
- <property name="minimumSize">
- <size>
- <width>55</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Player 1</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QLabel" name="label_4">
- <property name="text">
- <string>Player 2</string>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QLabel" name="label_5">
- <property name="text">
- <string>Player 3</string>
- </property>
- </widget>
- </item>
- <item row="4" column="1">
- <widget class="QLabel" name="label_6">
- <property name="text">
- <string>Player 4</string>
- </property>
- </widget>
- </item>
- <item row="5" column="1">
- <widget class="QLabel" name="label_7">
- <property name="text">
- <string>Player 5</string>
- </property>
- </widget>
- </item>
- <item row="6" column="1">
- <widget class="QLabel" name="label_8">
- <property name="text">
- <string>Player 6</string>
- </property>
- </widget>
- </item>
- <item row="7" column="1">
- <widget class="QLabel" name="label_9">
- <property name="text">
- <string>Player 7</string>
- </property>
- </widget>
- </item>
- <item row="8" column="1">
- <widget class="QLabel" name="label_10">
- <property name="text">
- <string>Player 8</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="gridGroupBox_2">
- <property name="title">
- <string>Handheld</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="1" column="2">
- <spacer name="horizontalSpacer_5">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>72</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="4">
- <spacer name="horizontalSpacer_4">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="3">
- <widget class="QPushButton" name="handheld_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <spacer name="horizontalSpacer_3">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="1">
- <widget class="QCheckBox" name="handheld_connected">
- <property name="text">
- <string>Joycons Docked</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QCheckBox" name="use_docked_mode">
- <property name="text">
- <string>Use Docked Mode</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="gridGroupBox_3">
- <property name="title">
- <string>Other</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="1" column="1">
- <widget class="QCheckBox" name="keyboard_enabled">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>23</height>
- </size>
- </property>
- <property name="text">
- <string>Keyboard</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QCheckBox" name="debug_enabled">
- <property name="text">
- <string>Debug Controller</string>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QCheckBox" name="touchscreen_enabled">
- <property name="text">
- <string>Touchscreen</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QCheckBox" name="mouse_enabled">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>23</height>
- </size>
- </property>
- <property name="text">
- <string>Mouse</string>
- </property>
- </widget>
- </item>
- <item row="0" column="4">
- <spacer name="horizontalSpacer_7">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="2">
- <spacer name="horizontalSpacer_8">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>76</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="0">
- <spacer name="horizontalSpacer_6">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="3" column="3">
- <widget class="QPushButton" name="touchscreen_advanced">
- <property name="text">
- <string>Advanced</string>
- </property>
- </widget>
- </item>
- <item row="2" column="3">
- <widget class="QPushButton" name="debug_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="0" column="3">
- <widget class="QPushButton" name="mouse_advanced">
- <property name="text">
- <string>Advanced</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <widget class="QPushButton" name="restore_defaults_button">
- <property name="text">
- <string>Restore Defaults</string>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tabPlayer1">
+ <property name="accessibleName">
+ <string>Player 1</string>
+ </property>
+ <attribute name="title">
+ <string>Player 1</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabPlayer2">
+ <property name="accessibleName">
+ <string>Player 2</string>
+ </property>
+ <attribute name="title">
+ <string>Player 2</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabPlayer3">
+ <property name="accessibleName">
+ <string>Player 3</string>
+ </property>
+ <attribute name="title">
+ <string>Player 3</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabPlayer4">
+ <property name="accessibleName">
+ <string>Player 4</string>
+ </property>
+ <attribute name="title">
+ <string>Player 4</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabPlayer5">
+ <property name="accessibleName">
+ <string>Player 5</string>
+ </property>
+ <attribute name="title">
+ <string>Player 5</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabPlayer6">
+ <property name="accessibleName">
+ <string>Player 6</string>
+ </property>
+ <attribute name="title">
+ <string>Player 6</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabPlayer7">
+ <property name="accessibleName">
+ <string>Player 7</string>
+ </property>
+ <attribute name="title">
+ <string>Player 7</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabPlayer8">
+ <property name="accessibleName">
+ <string>Player 8</string>
+ </property>
+ <attribute name="title">
+ <string>Player 8</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabAdvanced">
+ <property name="accessibleName">
+ <string>Advanced</string>
+ </property>
+ <attribute name="title">
+ <string>Advanced</string>
+ </attribute>
+ </widget>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignVCenter">
+ <widget class="QWidget" name="consoleInputSettings" native="true">
+ <layout class="QHBoxLayout" name="buttonsBottomRightHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignVCenter">
+ <widget class="QGroupBox" name="handheldGroup">
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Console Mode</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QRadioButton" name="radioDocked">
+ <property name="text">
+ <string>Docked</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="radioUndocked">
+ <property name="text">
+ <string>Undocked</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroup">
+ <property name="title">
+ <string>Vibration</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpin">
+ <property name="minimumSize">
+ <size>
+ <width>65</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>200</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="motionGroup">
+ <property name="title">
+ <string>Motion</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="motionButton">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignVCenter">
+ <widget class="QWidget" name="widget" native="true">
+ <layout class="QGridLayout" name="gridLayout_2">
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer_9">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <property name="rightMargin">
+ <number>0</number>
</property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
+ <property name="bottomMargin">
+ <number>0</number>
</property>
- </spacer>
- </item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ <property name="spacing">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
+ <item row="1" column="2">
+ <widget class="QCheckBox" name="checkboxPlayer2Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Controllers</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QCheckBox" name="checkboxPlayer4Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QCheckBox" name="checkboxPlayer3Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="5">
+ <widget class="QCheckBox" name="checkboxPlayer5Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>1</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="7">
+ <widget class="QCheckBox" name="checkboxPlayer7Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="6">
+ <widget class="QCheckBox" name="checkboxPlayer6Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="checkboxPlayer1Connected">
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="8">
+ <widget class="QCheckBox" name="checkboxPlayer8Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>2</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>3</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>4</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="5">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>5</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="6">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>6</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="7">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>7</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="8">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>8</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>Connected</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignBottom">
+ <widget class="QPushButton" name="buttonRestoreDefaults">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="sizeIncrement">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Defaults</string>
+ </property>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignBottom">
+ <widget class="QPushButton" name="buttonClearAll">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="sizeIncrement">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Clear</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
</item>
</layout>
</widget>
<resources/>
- <connections>
- <connection>
- <sender>buttonBox</sender>
- <signal>accepted()</signal>
- <receiver>ConfigureInput</receiver>
- <slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>294</x>
- <y>553</y>
- </hint>
- <hint type="destinationlabel">
- <x>191</x>
- <y>287</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>buttonBox</sender>
- <signal>rejected()</signal>
- <receiver>ConfigureInput</receiver>
- <slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>294</x>
- <y>553</y>
- </hint>
- <hint type="destinationlabel">
- <x>191</x>
- <y>287</y>
- </hint>
- </hints>
- </connection>
- </connections>
+ <connections/>
</ui>
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
new file mode 100644
index 000000000..db42b826b
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -0,0 +1,169 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QColorDialog>
+#include "core/core.h"
+#include "core/settings.h"
+#include "ui_configure_input_advanced.h"
+#include "yuzu/configuration/configure_input_advanced.h"
+
+ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
+ : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()) {
+ ui->setupUi(this);
+
+ controllers_color_buttons = {{
+ {
+ ui->player1_left_body_button,
+ ui->player1_left_buttons_button,
+ ui->player1_right_body_button,
+ ui->player1_right_buttons_button,
+ },
+ {
+ ui->player2_left_body_button,
+ ui->player2_left_buttons_button,
+ ui->player2_right_body_button,
+ ui->player2_right_buttons_button,
+ },
+ {
+ ui->player3_left_body_button,
+ ui->player3_left_buttons_button,
+ ui->player3_right_body_button,
+ ui->player3_right_buttons_button,
+ },
+ {
+ ui->player4_left_body_button,
+ ui->player4_left_buttons_button,
+ ui->player4_right_body_button,
+ ui->player4_right_buttons_button,
+ },
+ {
+ ui->player5_left_body_button,
+ ui->player5_left_buttons_button,
+ ui->player5_right_body_button,
+ ui->player5_right_buttons_button,
+ },
+ {
+ ui->player6_left_body_button,
+ ui->player6_left_buttons_button,
+ ui->player6_right_body_button,
+ ui->player6_right_buttons_button,
+ },
+ {
+ ui->player7_left_body_button,
+ ui->player7_left_buttons_button,
+ ui->player7_right_body_button,
+ ui->player7_right_buttons_button,
+ },
+ {
+ ui->player8_left_body_button,
+ ui->player8_left_buttons_button,
+ ui->player8_right_body_button,
+ ui->player8_right_buttons_button,
+ },
+ }};
+
+ for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
+ auto& color_buttons = controllers_color_buttons[player_idx];
+ for (std::size_t button_idx = 0; button_idx < color_buttons.size(); ++button_idx) {
+ connect(color_buttons[button_idx], &QPushButton::clicked, this,
+ [this, player_idx, button_idx] {
+ OnControllerButtonClick(static_cast<int>(player_idx),
+ static_cast<int>(button_idx));
+ });
+ }
+ }
+
+ connect(ui->mouse_enabled, &QCheckBox::stateChanged, this,
+ &ConfigureInputAdvanced::UpdateUIEnabled);
+ connect(ui->debug_enabled, &QCheckBox::stateChanged, this,
+ &ConfigureInputAdvanced::UpdateUIEnabled);
+ connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
+ &ConfigureInputAdvanced::UpdateUIEnabled);
+
+ connect(ui->debug_configure, &QPushButton::clicked, this,
+ [this] { CallDebugControllerDialog(); });
+ connect(ui->mouse_advanced, &QPushButton::clicked, this, [this] { CallMouseConfigDialog(); });
+ connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
+ [this] { CallTouchscreenConfigDialog(); });
+
+ LoadConfiguration();
+}
+
+ConfigureInputAdvanced::~ConfigureInputAdvanced() = default;
+
+void ConfigureInputAdvanced::OnControllerButtonClick(int player_idx, int button_idx) {
+ const QColor new_bg_color = QColorDialog::getColor(controllers_colors[player_idx][button_idx]);
+ if (!new_bg_color.isValid()) {
+ return;
+ }
+ controllers_colors[player_idx][button_idx] = new_bg_color;
+ controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
+ QStringLiteral("background-color: %1; min-width: 55px;")
+ .arg(controllers_colors[player_idx][button_idx].name()));
+}
+
+void ConfigureInputAdvanced::ApplyConfiguration() {
+ for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
+ auto& player = Settings::values.players[player_idx];
+ std::array<u32, 4> colors{};
+ std::transform(controllers_colors[player_idx].begin(), controllers_colors[player_idx].end(),
+ colors.begin(), [](QColor color) { return color.rgb(); });
+
+ player.body_color_left = colors[0];
+ player.button_color_left = colors[1];
+ player.body_color_right = colors[2];
+ player.button_color_right = colors[3];
+ }
+
+ Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
+ Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
+ Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
+ Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
+}
+
+void ConfigureInputAdvanced::LoadConfiguration() {
+ for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
+ auto& player = Settings::values.players[player_idx];
+ std::array<u32, 4> colors = {
+ player.body_color_left,
+ player.button_color_left,
+ player.body_color_right,
+ player.button_color_right,
+ };
+
+ std::transform(colors.begin(), colors.end(), controllers_colors[player_idx].begin(),
+ [](u32 rgb) { return QColor::fromRgb(rgb); });
+
+ for (std::size_t button_idx = 0; button_idx < colors.size(); ++button_idx) {
+ controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
+ QStringLiteral("background-color: %1; min-width: 55px;")
+ .arg(controllers_colors[player_idx][button_idx].name()));
+ }
+ }
+
+ ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
+ ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
+ ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
+ ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
+
+ UpdateUIEnabled();
+}
+
+void ConfigureInputAdvanced::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QWidget::changeEvent(event);
+}
+
+void ConfigureInputAdvanced::RetranslateUI() {
+ ui->retranslateUi(this);
+}
+
+void ConfigureInputAdvanced::UpdateUIEnabled() {
+ ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
+ ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
+ ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
+}
diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h
new file mode 100644
index 000000000..d8fcec52d
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_advanced.h
@@ -0,0 +1,45 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <QWidget>
+
+class QColor;
+class QPushButton;
+
+namespace Ui {
+class ConfigureInputAdvanced;
+}
+
+class ConfigureInputAdvanced : public QWidget {
+ Q_OBJECT
+
+public:
+ explicit ConfigureInputAdvanced(QWidget* parent = nullptr);
+ ~ConfigureInputAdvanced() override;
+
+ void ApplyConfiguration();
+
+signals:
+ void CallDebugControllerDialog();
+ void CallMouseConfigDialog();
+ void CallTouchscreenConfigDialog();
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+ void UpdateUIEnabled();
+
+ void OnControllerButtonClick(int player_idx, int button_idx);
+
+ void LoadConfiguration();
+
+ std::unique_ptr<Ui::ConfigureInputAdvanced> ui;
+
+ std::array<std::array<QColor, 4>, 8> controllers_colors;
+ std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons;
+};
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
new file mode 100644
index 000000000..5958435fc
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -0,0 +1,2688 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureInputAdvanced</class>
+ <widget class="QWidget" name="ConfigureInputAdvanced">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>710</width>
+ <height>580</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Input</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="mainInputAdvanced" native="true">
+ <layout class="QHBoxLayout" name="main" stretch="1,1">
+ <property name="spacing">
+ <number>9</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="leftInputAdvanced" native="true">
+ <layout class="QVBoxLayout" name="leftLayout" stretch="0">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="joyconColorsGroup">
+ <property name="title">
+ <string>Joycon Colors</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3" stretch="1,1">
+ <property name="leftMargin">
+ <number>9</number>
+ </property>
+ <property name="topMargin">
+ <number>9</number>
+ </property>
+ <property name="rightMargin">
+ <number>9</number>
+ </property>
+ <property name="bottomMargin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="topLeftInputAdvanced" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player12Widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="player1Group">
+ <property name="title">
+ <string>Player 1</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player1LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_14">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player1LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_66">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player1_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player1LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_67">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player1_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player1RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_14">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player1RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_64">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player1_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player1RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_65">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player1_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="player2Group">
+ <property name="title">
+ <string>Player 2</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_14">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player2LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_15">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player2LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_70">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player2_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player2LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_71">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player2_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player2RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_15">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player2RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_68">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player2_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player2RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_69">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player2_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player34Widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="player3Group">
+ <property name="title">
+ <string>Player 3</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_15">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player3LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_16">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player3LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_74">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player3_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player3LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_75">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player3_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player3RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_16">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player3RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_72">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player3_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player3RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_73">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player3_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="player4Group">
+ <property name="title">
+ <string>Player 4</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_16">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player4LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_17">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player4LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_78">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player4_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player4LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_79">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player4_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player4RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_17">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player4RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_76">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player4_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player4RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_77">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player4_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="bottomLeftInputAdvanced" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player56Widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="player5Group">
+ <property name="title">
+ <string>Player 5</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_10">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player5LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_10">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player5LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_50">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player5_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player5LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_51">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player5_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player5RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_10">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player5RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_48">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player5_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player5RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_49">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player5_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="player6Group">
+ <property name="title">
+ <string>Player 6</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player6LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_11">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player6LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_54">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player6_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player6LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_55">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player6_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player6RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_11">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player6RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_52">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player6_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player6RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_53">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player6_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player78Widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="player7Group">
+ <property name="title">
+ <string>Player 7</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_12">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player7LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_12">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player7LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_58">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player7_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player7LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_59">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player7_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player7RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_12">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player7RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_56">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player7_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player7RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_57">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player7_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="player8Group">
+ <property name="title">
+ <string>Player 8</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_13">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player8LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_13">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player8LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_62">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player8_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player8LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_63">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player8_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player8RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_13">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player8RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_60">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player8_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player8RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_61">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player8_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="rightInputAdvanced" native="true">
+ <layout class="QVBoxLayout" name="rightLayout" stretch="1,1">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="topRightInputAdvanced" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="gridGroupBox_3">
+ <property name="title">
+ <string>Other</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="keyboard_enabled">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>23</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Keyboard</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QPushButton" name="touchscreen_advanced">
+ <property name="text">
+ <string>Advanced</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <spacer name="horizontalSpacer_8">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>76</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="2">
+ <widget class="QPushButton" name="mouse_advanced">
+ <property name="text">
+ <string>Advanced</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QCheckBox" name="touchscreen_enabled">
+ <property name="text">
+ <string>Touchscreen</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="mouse_enabled">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>23</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Mouse</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="motion_touch">
+ <property name="text">
+ <string>Motion / Touch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="2">
+ <widget class="QPushButton" name="buttonMotionTouch">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QCheckBox" name="debug_enabled">
+ <property name="text">
+ <string>Debug Controller</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="2">
+ <widget class="QPushButton" name="debug_configure">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="bottomRightInputAdvanced" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="mainVerticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index b1850bc95..13ecb3dc5 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -5,39 +5,97 @@
#include <algorithm>
#include <memory>
#include <utility>
-#include <QColorDialog>
#include <QGridLayout>
+#include <QInputDialog>
#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
#include <QTimer>
-#include "common/assert.h"
#include "common/param_package.h"
+#include "core/core.h"
+#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/sm/sm.h"
+#include "input_common/gcadapter/gc_poller.h"
#include "input_common/main.h"
#include "ui_configure_input_player.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_input_player.h"
+constexpr std::size_t HANDHELD_INDEX = 8;
+
const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
ConfigureInputPlayer::analog_sub_buttons{{
"up",
"down",
"left",
"right",
- "modifier",
}};
-static void LayerGridElements(QGridLayout* grid, QWidget* item, QWidget* onTopOf) {
- const int index1 = grid->indexOf(item);
- const int index2 = grid->indexOf(onTopOf);
- int row, column, rowSpan, columnSpan;
- grid->getItemPosition(index2, &row, &column, &rowSpan, &columnSpan);
- grid->takeAt(index1);
- grid->addWidget(item, row, column, rowSpan, columnSpan);
+namespace {
+
+void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
+ bool connected) {
+ Core::System& system{Core::System::GetInstance()};
+ if (!system.IsPoweredOn()) {
+ return;
+ }
+ Service::SM::ServiceManager& sm = system.ServiceManager();
+
+ auto& npad =
+ sm.GetService<Service::HID::Hid>("hid")
+ ->GetAppletResource()
+ ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
+
+ npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
}
-static QString GetKeyName(int key_code) {
+/// Maps the controller type combobox index to Controller Type enum
+constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
+ switch (index) {
+ case 0:
+ default:
+ return Settings::ControllerType::ProController;
+ case 1:
+ return Settings::ControllerType::DualJoyconDetached;
+ case 2:
+ return Settings::ControllerType::LeftJoycon;
+ case 3:
+ return Settings::ControllerType::RightJoycon;
+ case 4:
+ return Settings::ControllerType::Handheld;
+ }
+}
+
+/// Maps the Controller Type enum to controller type combobox index
+constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
+ switch (type) {
+ case Settings::ControllerType::ProController:
+ default:
+ return 0;
+ case Settings::ControllerType::DualJoyconDetached:
+ return 1;
+ case Settings::ControllerType::LeftJoycon:
+ return 2;
+ case Settings::ControllerType::RightJoycon:
+ return 3;
+ case Settings::ControllerType::Handheld:
+ return 4;
+ }
+}
+
+QString GetKeyName(int key_code) {
switch (key_code) {
+ case Qt::LeftButton:
+ return QObject::tr("Click 0");
+ case Qt::RightButton:
+ return QObject::tr("Click 1");
+ case Qt::MiddleButton:
+ return QObject::tr("Click 2");
+ case Qt::BackButton:
+ return QObject::tr("Click 3");
+ case Qt::ForwardButton:
+ return QObject::tr("Click 4");
case Qt::Key_Shift:
return QObject::tr("Shift");
case Qt::Key_Control:
@@ -51,9 +109,16 @@ static QString GetKeyName(int key_code) {
}
}
-static void SetAnalogButton(const Common::ParamPackage& input_param,
- Common::ParamPackage& analog_param, const std::string& button_name) {
- if (analog_param.Get("engine", "") != "analog_from_button") {
+void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
+ const std::string& button_name) {
+ // The poller returned a complete axis, so set all the buttons
+ if (input_param.Has("axis_x") && input_param.Has("axis_y")) {
+ analog_param = input_param;
+ return;
+ }
+ // Check if the current configuration has either no engine or an axis binding.
+ // Clears out the old binding and adds one with analog_from_button.
+ if (!analog_param.Has("engine") || analog_param.Has("axis_x") || analog_param.Has("axis_y")) {
analog_param = {
{"engine", "analog_from_button"},
};
@@ -61,7 +126,7 @@ static void SetAnalogButton(const Common::ParamPackage& input_param,
analog_param.Set(button_name, input_param.Serialize());
}
-static QString ButtonToText(const Common::ParamPackage& param) {
+QString ButtonToText(const Common::ParamPackage& param) {
if (!param.Has("engine")) {
return QObject::tr("[not set]");
}
@@ -111,7 +176,7 @@ static QString ButtonToText(const Common::ParamPackage& param) {
return QObject::tr("[unknown]");
}
-static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
+QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
if (!param.Has("engine")) {
return QObject::tr("[not set]");
}
@@ -161,22 +226,25 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string
}
return QObject::tr("[unknown]");
}
-
-ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug)
- : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
- debug(debug), timeout_timer(std::make_unique<QTimer>()),
- poll_timer(std::make_unique<QTimer>()) {
+} // namespace
+
+ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index,
+ QWidget* bottom_row,
+ InputCommon::InputSubsystem* input_subsystem_,
+ bool debug)
+ : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
+ debug(debug), input_subsystem{input_subsystem_}, timeout_timer(std::make_unique<QTimer>()),
+ poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row) {
ui->setupUi(this);
+
setFocusPolicy(Qt::ClickFocus);
button_map = {
- ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
- ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
- ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
- ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
- ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown,
- ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
- ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
+ ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
+ ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
+ ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
+ ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
+ ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
};
analog_map_buttons = {{
@@ -185,220 +253,165 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
ui->buttonLStickDown,
ui->buttonLStickLeft,
ui->buttonLStickRight,
- ui->buttonLStickMod,
},
{
ui->buttonRStickUp,
ui->buttonRStickDown,
ui->buttonRStickLeft,
ui->buttonRStickRight,
- ui->buttonRStickMod,
},
}};
- debug_hidden = {
- ui->buttonSL, ui->labelSL,
- ui->buttonSR, ui->labelSR,
- ui->buttonLStick, ui->labelLStickPressed,
- ui->buttonRStick, ui->labelRStickPressed,
- ui->buttonHome, ui->labelHome,
- ui->buttonScreenshot, ui->labelScreenshot,
- };
-
- auto layout = Settings::values.players[player_index].type;
- if (debug)
- layout = Settings::ControllerType::DualJoycon;
-
- switch (layout) {
- case Settings::ControllerType::ProController:
- case Settings::ControllerType::DualJoycon:
- layout_hidden = {
- ui->buttonSL,
- ui->labelSL,
- ui->buttonSR,
- ui->labelSR,
- };
- break;
- case Settings::ControllerType::LeftJoycon:
- layout_hidden = {
- ui->right_body_button,
- ui->right_buttons_button,
- ui->right_body_label,
- ui->right_buttons_label,
- ui->buttonR,
- ui->labelR,
- ui->buttonZR,
- ui->labelZR,
- ui->labelHome,
- ui->buttonHome,
- ui->buttonPlus,
- ui->labelPlus,
- ui->RStick,
- ui->faceButtons,
- };
- break;
- case Settings::ControllerType::RightJoycon:
- layout_hidden = {
- ui->left_body_button, ui->left_buttons_button,
- ui->left_body_label, ui->left_buttons_label,
- ui->buttonL, ui->labelL,
- ui->buttonZL, ui->labelZL,
- ui->labelScreenshot, ui->buttonScreenshot,
- ui->buttonMinus, ui->labelMinus,
- ui->LStick, ui->Dpad,
- };
- break;
- }
-
- if (debug || layout == Settings::ControllerType::ProController) {
- ui->controller_color->hide();
- } else {
- if (layout == Settings::ControllerType::LeftJoycon ||
- layout == Settings::ControllerType::RightJoycon) {
- ui->horizontalSpacer_4->setGeometry({0, 0, 0, 0});
-
- LayerGridElements(ui->buttons, ui->shoulderButtons, ui->Dpad);
- LayerGridElements(ui->buttons, ui->misc, ui->RStick);
- LayerGridElements(ui->buttons, ui->Dpad, ui->faceButtons);
- LayerGridElements(ui->buttons, ui->RStick, ui->LStick);
- }
- }
+ analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone};
+ analog_map_deadzone_slider = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone};
+ analog_map_modifier_groupbox = {ui->buttonLStickModGroup, ui->buttonRStickModGroup};
+ analog_map_modifier_label = {ui->labelLStickModifierRange, ui->labelRStickModifierRange};
+ analog_map_modifier_slider = {ui->sliderLStickModifierRange, ui->sliderRStickModifierRange};
+ analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup};
+ analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange};
- for (auto* widget : layout_hidden)
- widget->setVisible(false);
-
- analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
- analog_map_deadzone_and_modifier_slider = {ui->sliderLStickDeadzoneAndModifier,
- ui->sliderRStickDeadzoneAndModifier};
- analog_map_deadzone_and_modifier_slider_label = {ui->labelLStickDeadzoneAndModifier,
- ui->labelRStickDeadzoneAndModifier};
+ const auto ConfigureButtonClick = [&](QPushButton* button, Common::ParamPackage* param,
+ int default_val) {
+ connect(button, &QPushButton::clicked, [=, this] {
+ HandleClick(
+ button,
+ [=, this](Common::ParamPackage params) {
+ // Workaround for ZL & ZR for analog triggers like on XBOX
+ // controllers. Analog triggers (from controllers like the XBOX
+ // controller) would not work due to a different range of their
+ // signals (from 0 to 255 on analog triggers instead of -32768 to
+ // 32768 on analog joysticks). The SDL driver misinterprets analog
+ // triggers as analog joysticks.
+ // TODO: reinterpret the signal range for analog triggers to map the
+ // values correctly. This is required for the correct emulation of
+ // the analog triggers of the GameCube controller.
+ if (button == ui->buttonZL || button == ui->buttonZR) {
+ params.Set("direction", "+");
+ params.Set("threshold", "0.5");
+ }
+ *param = std::move(params);
+ },
+ InputCommon::Polling::DeviceType::Button);
+ });
+ };
- for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
auto* const button = button_map[button_id];
if (button == nullptr) {
continue;
}
-
- button->setContextMenuPolicy(Qt::CustomContextMenu);
- connect(button, &QPushButton::clicked, [=, this] {
- HandleClick(button_map[button_id],
- [=, this](Common::ParamPackage params) {
- // Workaround for ZL & ZR for analog triggers like on XBOX controllors.
- // Analog triggers (from controllers like the XBOX controller) would not
- // work due to a different range of their signals (from 0 to 255 on
- // analog triggers instead of -32768 to 32768 on analog joysticks). The
- // SDL driver misinterprets analog triggers as analog joysticks.
- // TODO: reinterpret the signal range for analog triggers to map the
- // values correctly. This is required for the correct emulation of the
- // analog triggers of the GameCube controller.
- if (button_id == Settings::NativeButton::ZL ||
- button_id == Settings::NativeButton::ZR) {
- params.Set("direction", "+");
- params.Set("threshold", "0.5");
- }
- buttons_param[button_id] = std::move(params);
- },
- InputCommon::Polling::DeviceType::Button);
- });
- connect(button, &QPushButton::customContextMenuRequested,
- [=, this](const QPoint& menu_location) {
- QMenu context_menu;
- context_menu.addAction(tr("Clear"), [&] {
- buttons_param[button_id].Clear();
- button_map[button_id]->setText(tr("[not set]"));
- });
- context_menu.addAction(tr("Restore Default"), [&] {
- buttons_param[button_id] = Common::ParamPackage{
- InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
- button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
- });
- context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
- });
+ ConfigureButtonClick(button_map[button_id], &buttons_param[button_id],
+ Config::default_buttons[button_id]);
}
- for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
- for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ // Handle clicks for the modifier buttons as well.
+ ConfigureButtonClick(ui->buttonLStickMod, &lstick_mod, Config::default_stick_mod[0]);
+ ConfigureButtonClick(ui->buttonRStickMod, &rstick_mod, Config::default_stick_mod[1]);
+
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
+
if (analog_button == nullptr) {
continue;
}
- analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(analog_button, &QPushButton::clicked, [=, this] {
- HandleClick(analog_map_buttons[analog_id][sub_button_id],
- [=, this](const Common::ParamPackage& params) {
- SetAnalogButton(params, analogs_param[analog_id],
- analog_sub_buttons[sub_button_id]);
- },
- InputCommon::Polling::DeviceType::Button);
+ HandleClick(
+ analog_map_buttons[analog_id][sub_button_id],
+ [=, this](const Common::ParamPackage& params) {
+ SetAnalogParam(params, analogs_param[analog_id],
+ analog_sub_buttons[sub_button_id]);
+ },
+ InputCommon::Polling::DeviceType::AnalogPreferred);
});
- connect(analog_button, &QPushButton::customContextMenuRequested,
- [=, this](const QPoint& menu_location) {
- QMenu context_menu;
- context_menu.addAction(tr("Clear"), [&] {
- analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
- analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
- });
- context_menu.addAction(tr("Restore Default"), [&] {
- Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
- Config::default_analogs[analog_id][sub_button_id])};
- SetAnalogButton(params, analogs_param[analog_id],
- analog_sub_buttons[sub_button_id]);
- analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
- analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
- });
- context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
- menu_location));
- });
}
- connect(analog_map_stick[analog_id], &QPushButton::clicked, [=, this] {
- if (QMessageBox::information(
- this, tr("Information"),
- tr("After pressing OK, first move your joystick horizontally, "
- "and then vertically."),
- QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
- HandleClick(analog_map_stick[analog_id],
- [=, this](const Common::ParamPackage& params) {
- analogs_param[analog_id] = params;
- },
- InputCommon::Polling::DeviceType::Analog);
- }
- });
- connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged,
+ connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged),
[=, this] {
- const float slider_value =
- analog_map_deadzone_and_modifier_slider[analog_id]->value();
- if (analogs_param[analog_id].Get("engine", "") == "sdl" ||
- analogs_param[analog_id].Get("engine", "") == "gcpad") {
- analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
- tr("Deadzone: %1%").arg(slider_value));
- analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
- } else {
- analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
- tr("Modifier Scale: %1%").arg(slider_value));
- analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
- }
+ const auto spinbox_value = analog_map_range_spinbox[analog_id]->value();
+ analogs_param[analog_id].Set("range", spinbox_value / 100.0f);
});
+
+ connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] {
+ const auto slider_value = analog_map_deadzone_slider[analog_id]->value();
+ analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value));
+ analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
+ });
+
+ connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] {
+ const auto slider_value = analog_map_modifier_slider[analog_id]->value();
+ analog_map_modifier_label[analog_id]->setText(
+ tr("Modifier Range: %1%").arg(slider_value));
+ analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
+ });
}
- connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
- connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
+ // Player Connected checkbox
+ connect(ui->groupConnectedController, &QGroupBox::toggled,
+ [this](bool checked) { emit Connected(checked); });
+
+ // Set up controller type. Only Player 1 can choose Handheld.
+ ui->comboControllerType->clear();
+
+ QStringList controller_types = {
+ tr("Pro Controller"),
+ tr("Dual Joycons"),
+ tr("Left Joycon"),
+ tr("Right Joycon"),
+ };
+
+ if (player_index == 0) {
+ controller_types.append(tr("Handheld"));
+ connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
+ [this](int index) {
+ emit HandheldStateChanged(GetControllerTypeFromIndex(index) ==
+ Settings::ControllerType::Handheld);
+ });
+ }
+
+ // The Debug Controller can only choose the Pro Controller.
+ if (debug) {
+ ui->buttonScreenshot->setEnabled(false);
+ ui->buttonHome->setEnabled(false);
+ ui->groupConnectedController->setCheckable(false);
+ QStringList debug_controller_types = {
+ tr("Pro Controller"),
+ };
+ ui->comboControllerType->addItems(debug_controller_types);
+ } else {
+ ui->comboControllerType->addItems(controller_types);
+ }
+
+ UpdateControllerIcon();
+ UpdateControllerAvailableButtons();
+ connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
+ UpdateControllerIcon();
+ UpdateControllerAvailableButtons();
+ });
+
+ connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this,
+ &ConfigureInputPlayer::UpdateMappingWithDefaults);
+
+ ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
+ UpdateInputDevices();
+ connect(ui->buttonRefreshDevices, &QPushButton::clicked,
+ [this] { emit RefreshInputDevices(); });
timeout_timer->setSingleShot(true);
connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
connect(poll_timer.get(), &QTimer::timeout, [this] {
Common::ParamPackage params;
- if (InputCommon::GetGCButtons()->IsPolling()) {
- params = InputCommon::GetGCButtons()->GetNextInput();
+ if (input_subsystem->GetGCButtons()->IsPolling()) {
+ params = input_subsystem->GetGCButtons()->GetNextInput();
if (params.Has("engine")) {
SetPollingResult(params, false);
return;
}
}
- if (InputCommon::GetGCAnalogs()->IsPolling()) {
- params = InputCommon::GetGCAnalogs()->GetNextInput();
+ if (input_subsystem->GetGCAnalogs()->IsPolling()) {
+ params = input_subsystem->GetGCAnalogs()->GetNextInput();
if (params.Has("engine")) {
SetPollingResult(params, false);
return;
@@ -413,20 +426,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
}
});
- controller_color_buttons = {
- ui->left_body_button,
- ui->left_buttons_button,
- ui->right_body_button,
- ui->right_buttons_button,
- };
-
- for (std::size_t i = 0; i < controller_color_buttons.size(); ++i) {
- connect(controller_color_buttons[i], &QPushButton::clicked, this,
- [this, i] { OnControllerButtonClick(static_cast<int>(i)); });
- }
-
LoadConfiguration();
- resize(0, 0);
// TODO(wwylele): enable this when we actually emulate it
ui->buttonHome->setEnabled(false);
@@ -435,27 +435,43 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
ConfigureInputPlayer::~ConfigureInputPlayer() = default;
void ConfigureInputPlayer::ApplyConfiguration() {
- auto& buttons =
- debug ? Settings::values.debug_pad_buttons : Settings::values.players[player_index].buttons;
- auto& analogs =
- debug ? Settings::values.debug_pad_analogs : Settings::values.players[player_index].analogs;
+ auto& player = Settings::values.players[player_index];
+ auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons;
+ auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs;
std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(),
[](const Common::ParamPackage& param) { return param.Serialize(); });
std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
[](const Common::ParamPackage& param) { return param.Serialize(); });
- if (debug)
+ if (debug) {
+ return;
+ }
+
+ player.controller_type =
+ static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
+ player.connected = ui->groupConnectedController->isChecked();
+
+ // Player 2-8
+ if (player_index != 0) {
+ UpdateController(player.controller_type, player_index, player.connected);
return;
+ }
- std::array<u32, 4> colors{};
- std::transform(controller_colors.begin(), controller_colors.end(), colors.begin(),
- [](QColor color) { return color.rgb(); });
+ // Player 1 and Handheld
+ auto& handheld = Settings::values.players[HANDHELD_INDEX];
+ // If Handheld is selected, copy all the settings from Player 1 to Handheld.
+ if (player.controller_type == Settings::ControllerType::Handheld) {
+ handheld = player;
+ handheld.connected = ui->groupConnectedController->isChecked();
+ player.connected = false; // Disconnect Player 1
+ } else {
+ player.connected = ui->groupConnectedController->isChecked();
+ handheld.connected = false; // Disconnect Handheld
+ }
- Settings::values.players[player_index].body_color_left = colors[0];
- Settings::values.players[player_index].button_color_left = colors[1];
- Settings::values.players[player_index].body_color_right = colors[2];
- Settings::values.players[player_index].button_color_right = colors[3];
+ UpdateController(player.controller_type, player_index, player.connected);
+ UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected);
}
void ConfigureInputPlayer::changeEvent(QEvent* event) {
@@ -463,24 +479,16 @@ void ConfigureInputPlayer::changeEvent(QEvent* event) {
RetranslateUI();
}
- QDialog::changeEvent(event);
+ QWidget::changeEvent(event);
}
void ConfigureInputPlayer::RetranslateUI() {
ui->retranslateUi(this);
- UpdateButtonLabels();
-}
-
-void ConfigureInputPlayer::OnControllerButtonClick(int i) {
- const QColor new_bg_color = QColorDialog::getColor(controller_colors[i]);
- if (!new_bg_color.isValid())
- return;
- controller_colors[i] = new_bg_color;
- controller_color_buttons[i]->setStyleSheet(
- QStringLiteral("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
+ UpdateUI();
}
void ConfigureInputPlayer::LoadConfiguration() {
+ auto& player = Settings::values.players[player_index];
if (debug) {
std::transform(Settings::values.debug_pad_buttons.begin(),
Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
@@ -489,56 +497,60 @@ void ConfigureInputPlayer::LoadConfiguration() {
Settings::values.debug_pad_analogs.end(), analogs_param.begin(),
[](const std::string& str) { return Common::ParamPackage(str); });
} else {
- std::transform(Settings::values.players[player_index].buttons.begin(),
- Settings::values.players[player_index].buttons.end(), buttons_param.begin(),
+ std::transform(player.buttons.begin(), player.buttons.end(), buttons_param.begin(),
[](const std::string& str) { return Common::ParamPackage(str); });
- std::transform(Settings::values.players[player_index].analogs.begin(),
- Settings::values.players[player_index].analogs.end(), analogs_param.begin(),
+ std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(),
[](const std::string& str) { return Common::ParamPackage(str); });
}
- UpdateButtonLabels();
+ UpdateUI();
- if (debug)
+ if (debug) {
return;
+ }
- std::array<u32, 4> colors = {
- Settings::values.players[player_index].body_color_left,
- Settings::values.players[player_index].button_color_left,
- Settings::values.players[player_index].body_color_right,
- Settings::values.players[player_index].button_color_right,
- };
-
- std::transform(colors.begin(), colors.end(), controller_colors.begin(),
- [](u32 rgb) { return QColor::fromRgb(rgb); });
+ ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type));
+ ui->groupConnectedController->setChecked(
+ player.connected ||
+ (player_index == 0 && Settings::values.players[HANDHELD_INDEX].connected));
+}
- for (std::size_t i = 0; i < colors.size(); ++i) {
- controller_color_buttons[i]->setStyleSheet(
- QStringLiteral("QPushButton { background-color: %1 }")
- .arg(controller_colors[i].name()));
+void ConfigureInputPlayer::UpdateInputDevices() {
+ input_devices = input_subsystem->GetInputDevices();
+ ui->comboDevices->clear();
+ for (auto device : input_devices) {
+ ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
}
}
void ConfigureInputPlayer::RestoreDefaults() {
- for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
+ // Reset Buttons
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
buttons_param[button_id] = Common::ParamPackage{
InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
}
- for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
- for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ // Reset Modifier Buttons
+ lstick_mod =
+ Common::ParamPackage(InputCommon::GenerateKeyboardParam(Config::default_stick_mod[0]));
+ rstick_mod =
+ Common::ParamPackage(InputCommon::GenerateKeyboardParam(Config::default_stick_mod[1]));
+
+ // Reset Analogs
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
Config::default_analogs[analog_id][sub_button_id])};
- SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
+ SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
}
}
-
- UpdateButtonLabels();
- ApplyConfiguration();
+ UpdateUI();
+ UpdateInputDevices();
+ ui->comboControllerType->setCurrentIndex(0);
}
void ConfigureInputPlayer::ClearAll() {
- for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
const auto* const button = button_map[button_id];
if (button == nullptr || !button->isEnabled()) {
continue;
@@ -547,8 +559,11 @@ void ConfigureInputPlayer::ClearAll() {
buttons_param[button_id].Clear();
}
- for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
- for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ lstick_mod.Clear();
+ rstick_mod.Clear();
+
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
if (analog_button == nullptr || !analog_button->isEnabled()) {
continue;
@@ -558,17 +573,20 @@ void ConfigureInputPlayer::ClearAll() {
}
}
- UpdateButtonLabels();
- ApplyConfiguration();
+ UpdateUI();
+ UpdateInputDevices();
}
-void ConfigureInputPlayer::UpdateButtonLabels() {
- for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
+void ConfigureInputPlayer::UpdateUI() {
+ for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) {
button_map[button]->setText(ButtonToText(buttons_param[button]));
}
- for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
- for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ ui->buttonLStickMod->setText(ButtonToText(lstick_mod));
+ ui->buttonRStickMod->setText(ButtonToText(rstick_mod));
+
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
if (analog_button == nullptr) {
@@ -578,99 +596,141 @@ void ConfigureInputPlayer::UpdateButtonLabels() {
analog_button->setText(
AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
}
- analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
+ const auto deadzone_label = analog_map_deadzone_label[analog_id];
+ const auto deadzone_slider = analog_map_deadzone_slider[analog_id];
+ const auto modifier_groupbox = analog_map_modifier_groupbox[analog_id];
+ const auto modifier_label = analog_map_modifier_label[analog_id];
+ const auto modifier_slider = analog_map_modifier_slider[analog_id];
+ const auto range_groupbox = analog_map_range_groupbox[analog_id];
+ const auto range_spinbox = analog_map_range_spinbox[analog_id];
+
+ int slider_value;
auto& param = analogs_param[analog_id];
- auto* const analog_stick_slider = analog_map_deadzone_and_modifier_slider[analog_id];
- auto* const analog_stick_slider_label =
- analog_map_deadzone_and_modifier_slider_label[analog_id];
-
- if (param.Has("engine")) {
- if (param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad") {
- if (!param.Has("deadzone")) {
- param.Set("deadzone", 0.1f);
- }
-
- analog_stick_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100));
- if (analog_stick_slider->value() == 0) {
- analog_stick_slider_label->setText(tr("Deadzone: 0%"));
- }
- } else {
- if (!param.Has("modifier_scale")) {
- param.Set("modifier_scale", 0.5f);
- }
-
- analog_stick_slider->setValue(
- static_cast<int>(param.Get("modifier_scale", 0.5f) * 100));
- if (analog_stick_slider->value() == 0) {
- analog_stick_slider_label->setText(tr("Modifier Scale: 0%"));
- }
+ const bool is_controller =
+ param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad";
+
+ if (is_controller) {
+ if (!param.Has("deadzone")) {
+ param.Set("deadzone", 0.1f);
+ }
+ slider_value = static_cast<int>(param.Get("deadzone", 0.1f) * 100);
+ deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value));
+ deadzone_slider->setValue(slider_value);
+ if (!param.Has("range")) {
+ param.Set("range", 1.0f);
+ }
+ range_spinbox->setValue(static_cast<int>(param.Get("range", 1.0f) * 100));
+ } else {
+ if (!param.Has("modifier_scale")) {
+ param.Set("modifier_scale", 0.5f);
}
+ slider_value = static_cast<int>(param.Get("modifier_scale", 0.5f) * 100);
+ modifier_label->setText(tr("Modifier Range: %1%").arg(slider_value));
+ modifier_slider->setValue(slider_value);
}
+
+ deadzone_label->setVisible(is_controller);
+ deadzone_slider->setVisible(is_controller);
+ modifier_groupbox->setVisible(!is_controller);
+ modifier_label->setVisible(!is_controller);
+ modifier_slider->setVisible(!is_controller);
+ range_groupbox->setVisible(is_controller);
}
}
+void ConfigureInputPlayer::UpdateMappingWithDefaults() {
+ if (ui->comboDevices->currentIndex() < 2) {
+ return;
+ }
+ const auto& device = input_devices[ui->comboDevices->currentIndex()];
+ auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
+ auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
+ for (int i = 0; i < buttons_param.size(); ++i) {
+ buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)];
+ }
+ for (int i = 0; i < analogs_param.size(); ++i) {
+ analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)];
+ }
+
+ UpdateUI();
+}
+
void ConfigureInputPlayer::HandleClick(
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type) {
- button->setText(tr("[press key]"));
+ button->setText(tr("[waiting]"));
button->setFocus();
- // Keyboard keys can only be used as button devices
- want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
- if (want_keyboard_keys) {
- const auto iter = std::find(button_map.begin(), button_map.end(), button);
- ASSERT(iter != button_map.end());
- const auto index = std::distance(button_map.begin(), iter);
- ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
- }
+ // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
+ // controller, then they don't want keyboard/mouse input
+ want_keyboard_mouse = ui->comboDevices->currentIndex() < 2;
input_setter = new_input_setter;
- device_pollers = InputCommon::Polling::GetPollers(type);
+ device_pollers = input_subsystem->GetPollers(type);
for (auto& poller : device_pollers) {
poller->Start();
}
- grabKeyboard();
- grabMouse();
+ QWidget::grabMouse();
+ QWidget::grabKeyboard();
+
if (type == InputCommon::Polling::DeviceType::Button) {
- InputCommon::GetGCButtons()->BeginConfiguration();
+ input_subsystem->GetGCButtons()->BeginConfiguration();
} else {
- InputCommon::GetGCAnalogs()->BeginConfiguration();
+ input_subsystem->GetGCAnalogs()->BeginConfiguration();
}
- timeout_timer->start(5000); // Cancel after 5 seconds
- poll_timer->start(200); // Check for new inputs every 200ms
+
+ timeout_timer->start(2500); // Cancel after 2.5 seconds
+ poll_timer->start(50); // Check for new inputs every 50ms
}
void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) {
- releaseKeyboard();
- releaseMouse();
timeout_timer->stop();
poll_timer->stop();
for (auto& poller : device_pollers) {
poller->Stop();
}
- InputCommon::GetGCButtons()->EndConfiguration();
- InputCommon::GetGCAnalogs()->EndConfiguration();
+ QWidget::releaseMouse();
+ QWidget::releaseKeyboard();
+
+ input_subsystem->GetGCButtons()->EndConfiguration();
+ input_subsystem->GetGCAnalogs()->EndConfiguration();
if (!abort) {
(*input_setter)(params);
}
- UpdateButtonLabels();
+ UpdateUI();
input_setter = std::nullopt;
}
+void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
+ if (!input_setter || !event) {
+ return;
+ }
+
+ if (want_keyboard_mouse) {
+ SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
+ false);
+ } else {
+ // We don't want any mouse buttons, so don't stop polling
+ return;
+ }
+
+ SetPollingResult({}, true);
+}
+
void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
if (!input_setter || !event) {
return;
}
if (event->key() != Qt::Key_Escape) {
- if (want_keyboard_keys) {
+ if (want_keyboard_mouse) {
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
false);
} else {
@@ -678,5 +738,108 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
return;
}
}
+
SetPollingResult({}, true);
}
+
+void ConfigureInputPlayer::UpdateControllerIcon() {
+ // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its
+ // "nonstandard" to use an image through the icon support)
+ const QString stylesheet = [this] {
+ switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
+ case Settings::ControllerType::ProController:
+ return QStringLiteral("image: url(:/controller/pro_controller%0)");
+ case Settings::ControllerType::DualJoyconDetached:
+ return QStringLiteral("image: url(:/controller/dual_joycon%0)");
+ case Settings::ControllerType::LeftJoycon:
+ return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)");
+ case Settings::ControllerType::RightJoycon:
+ return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)");
+ case Settings::ControllerType::Handheld:
+ return QStringLiteral("image: url(:/controller/handheld%0)");
+ default:
+ return QString{};
+ }
+ }();
+
+ const QString theme = [this] {
+ if (QIcon::themeName().contains(QStringLiteral("dark"))) {
+ return QStringLiteral("_dark");
+ } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
+ return QStringLiteral("_midnight");
+ } else {
+ return QString{};
+ }
+ }();
+
+ ui->controllerFrame->setStyleSheet(stylesheet.arg(theme));
+}
+
+void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
+ auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
+ if (debug) {
+ layout = Settings::ControllerType::ProController;
+ }
+
+ // List of all the widgets that will be hidden by any of the following layouts that need
+ // "unhidden" after the controller type changes
+ const std::array<QWidget*, 9> layout_show = {
+ ui->buttonShoulderButtonsSLSR,
+ ui->horizontalSpacerShoulderButtonsWidget,
+ ui->horizontalSpacerShoulderButtonsWidget2,
+ ui->buttonShoulderButtonsLeft,
+ ui->buttonMiscButtonsMinusScreenshot,
+ ui->bottomLeft,
+ ui->buttonShoulderButtonsRight,
+ ui->buttonMiscButtonsPlusHome,
+ ui->bottomRight,
+ };
+
+ for (auto* widget : layout_show) {
+ widget->show();
+ }
+
+ std::vector<QWidget*> layout_hidden;
+ switch (layout) {
+ case Settings::ControllerType::ProController:
+ case Settings::ControllerType::DualJoyconDetached:
+ case Settings::ControllerType::Handheld:
+ layout_hidden = {
+ ui->buttonShoulderButtonsSLSR,
+ ui->horizontalSpacerShoulderButtonsWidget2,
+ };
+ break;
+ case Settings::ControllerType::LeftJoycon:
+ layout_hidden = {
+ ui->horizontalSpacerShoulderButtonsWidget2,
+ ui->buttonShoulderButtonsRight,
+ ui->buttonMiscButtonsPlusHome,
+ ui->bottomRight,
+ };
+ break;
+ case Settings::ControllerType::RightJoycon:
+ layout_hidden = {
+ ui->horizontalSpacerShoulderButtonsWidget,
+ ui->buttonShoulderButtonsLeft,
+ ui->buttonMiscButtonsMinusScreenshot,
+ ui->bottomLeft,
+ };
+ break;
+ }
+
+ for (auto* widget : layout_hidden) {
+ widget->hide();
+ }
+}
+
+void ConfigureInputPlayer::showEvent(QShowEvent* event) {
+ if (bottom_row == nullptr) {
+ return;
+ }
+ QWidget::showEvent(event);
+ ui->main->addWidget(bottom_row);
+}
+
+void ConfigureInputPlayer::ConnectPlayer(bool connected) {
+ ui->groupConnectedController->setChecked(connected);
+}
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 95afa5375..a25bc3bd9 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -10,16 +10,25 @@
#include <optional>
#include <string>
-#include <QDialog>
+#include <QWidget>
#include "common/param_package.h"
#include "core/settings.h"
#include "ui_configure_input.h"
+class QCheckBox;
class QKeyEvent;
+class QLabel;
class QPushButton;
+class QSlider;
+class QSpinBox;
class QString;
class QTimer;
+class QWidget;
+
+namespace InputCommon {
+class InputSubsystem;
+}
namespace InputCommon::Polling {
class DevicePoller;
@@ -30,77 +39,116 @@ namespace Ui {
class ConfigureInputPlayer;
}
-class ConfigureInputPlayer : public QDialog {
+class ConfigureInputPlayer : public QWidget {
Q_OBJECT
public:
- explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug = false);
+ explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row,
+ InputCommon::InputSubsystem* input_subsystem_,
+ bool debug = false);
~ConfigureInputPlayer() override;
- /// Save all button configurations to settings file
+ /// Save all button configurations to settings file.
void ApplyConfiguration();
+ /// Update the input devices combobox.
+ void UpdateInputDevices();
+
+ /// Restore all buttons to their default values.
+ void RestoreDefaults();
+
+ /// Clear all input configuration.
+ void ClearAll();
+
+ /// Set the connection state checkbox (used to sync state).
+ void ConnectPlayer(bool connected);
+
+signals:
+ /// Emitted when this controller is connected by the user.
+ void Connected(bool connected);
+ /// Emitted when the Handheld mode is selected (undocked with dual joycons attached).
+ void HandheldStateChanged(bool is_handheld);
+ /// Emitted when the input devices combobox is being refreshed.
+ void RefreshInputDevices();
+
+protected:
+ void showEvent(QShowEvent* event) override;
+
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
- void OnControllerButtonClick(int i);
-
/// Load configuration settings.
void LoadConfiguration();
- /// Restore all buttons to their default values.
- void RestoreDefaults();
- /// Clear all input configuration
- void ClearAll();
-
- /// Update UI to reflect current configuration.
- void UpdateButtonLabels();
/// Called when the button was pressed.
void HandleClick(QPushButton* button,
std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type);
- /// Finish polling and configure input using the input_setter
+ /// Finish polling and configure input using the input_setter.
void SetPollingResult(const Common::ParamPackage& params, bool abort);
+ /// Handle mouse button press events.
+ void mousePressEvent(QMouseEvent* event) override;
+
/// Handle key press events.
void keyPressEvent(QKeyEvent* event) override;
+ /// Update UI to reflect current configuration.
+ void UpdateUI();
+
+ /// Update the controller selection combobox
+ void UpdateControllerCombobox();
+
+ /// Update the current controller icon.
+ void UpdateControllerIcon();
+
+ /// Hides and disables controller settings based on the current controller type.
+ void UpdateControllerAvailableButtons();
+
+ /// Gets the default controller mapping for this device and auto configures the input to match.
+ void UpdateMappingWithDefaults();
+
std::unique_ptr<Ui::ConfigureInputPlayer> ui;
std::size_t player_index;
bool debug;
+ InputCommon::InputSubsystem* input_subsystem;
+
std::unique_ptr<QTimer> timeout_timer;
std::unique_ptr<QTimer> poll_timer;
+ static constexpr int PLAYER_COUNT = 8;
+ std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox;
+
/// This will be the the setting function when an input is awaiting configuration.
std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
- static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
+ static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;
/// Each button input is represented by a QPushButton.
std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
+ /// Extra buttons for the modifiers.
+ Common::ParamPackage lstick_mod;
+ Common::ParamPackage rstick_mod;
- std::vector<QWidget*> debug_hidden;
- std::vector<QWidget*> layout_hidden;
-
- /// A group of five QPushButtons represent one analog input. The buttons each represent up,
- /// down, left, right, and modifier, respectively.
+ /// A group of four QPushButtons represent one analog input. The buttons each represent up,
+ /// down, left, right, respectively.
std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
analog_map_buttons;
- /// Analog inputs are also represented each with a single button, used to configure with an
- /// actual analog stick
- std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
- std::array<QSlider*, Settings::NativeAnalog::NumAnalogs>
- analog_map_deadzone_and_modifier_slider;
- std::array<QLabel*, Settings::NativeAnalog::NumAnalogs>
- analog_map_deadzone_and_modifier_slider_label;
+ std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label;
+ std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_slider;
+ std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_groupbox;
+ std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_label;
+ std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_slider;
+ std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_groupbox;
+ std::array<QSpinBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_spinbox;
static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
@@ -108,8 +156,14 @@ private:
/// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
/// keyboard events are ignored.
- bool want_keyboard_keys = false;
+ bool want_keyboard_mouse = false;
+
+ /// List of physical devices users can map with. If a SDL backed device is selected, then you
+ /// can usue this device to get a default mapping.
+ std::vector<Common::ParamPackage> input_devices;
- std::array<QPushButton*, 4> controller_color_buttons;
- std::array<QColor, 4> controller_colors;
+ /// Bottom row is where console wide settings are held, and its "owned" by the parent
+ /// ConfigureInput widget. On show, add this widget to the main layout. This will change the
+ /// parent of the widget to this widget (but thats fine).
+ QWidget* bottom_row;
};
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index f27a77180..9bc681894 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -1,1243 +1,2974 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureInputPlayer</class>
- <widget class="QDialog" name="ConfigureInputPlayer">
+ <widget class="QWidget" name="ConfigureInputPlayer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>408</width>
- <height>731</height>
+ <width>780</width>
+ <height>487</height>
</rect>
</property>
<property name="windowTitle">
<string>Configure Input</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_5">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
<item>
- <layout class="QGridLayout" name="buttons">
- <item row="1" column="1">
- <widget class="QGroupBox" name="RStick">
- <property name="title">
- <string>Right Stick</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
- </property>
- <property name="flat">
- <bool>false</bool>
+ <layout class="QVBoxLayout" name="main">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="top" stretch="0,1,2">
+ <property name="spacing">
+ <number>3</number>
</property>
- <property name="checkable">
- <bool>false</bool>
+ <property name="topMargin">
+ <number>0</number>
</property>
- <layout class="QGridLayout" name="gridLayout_5">
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupConnectedController">
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="title">
+ <string>Connect Controller</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>5</number>
+ </property>
+ <property name="rightMargin">
+ <number>5</number>
+ </property>
+ <property name="bottomMargin">
+ <number>5</number>
+ </property>
<item>
- <layout class="QHBoxLayout" name="buttonRStickDownHorizontalLayout">
+ <widget class="QComboBox" name="comboControllerType">
<item>
- <widget class="QLabel" name="labelRStickDown">
- <property name="text">
- <string>Down:</string>
- </property>
- </widget>
+ <property name="text">
+ <string>Pro Controller</string>
+ </property>
</item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickDown">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonRStickRightHorizontalLayout">
<item>
- <widget class="QLabel" name="labelRStickRight">
- <property name="text">
- <string>Right:</string>
- </property>
- </widget>
+ <property name="text">
+ <string>Dual Joycons</string>
+ </property>
</item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickRight">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="3" column="0" colspan="2">
- <widget class="QPushButton" name="buttonRStickAnalog">
- <property name="text">
- <string>Set Analog Stick</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonRStickLeftHorizontalLayout">
<item>
- <widget class="QLabel" name="labelRStickLeft">
- <property name="text">
- <string>Left:</string>
- </property>
- </widget>
+ <property name="text">
+ <string>Left Joycon</string>
+ </property>
</item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickLeft">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonRStickUpHorizontalLayout">
<item>
- <widget class="QLabel" name="labelRStickUp">
- <property name="text">
- <string>Up:</string>
- </property>
- </widget>
+ <property name="text">
+ <string>Right Joycon</string>
+ </property>
</item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickUp">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="2" column="0">
- <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonRStickPressedHorizontalLayout">
<item>
- <widget class="QLabel" name="labelRStickPressed">
- <property name="text">
- <string>Pressed:</string>
- </property>
- </widget>
+ <property name="text">
+ <string>Handheld</string>
+ </property>
</item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStick">
- <property name="text">
- <string/>
- </property>
</widget>
</item>
</layout>
- </item>
- <item row="2" column="1">
- <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout">
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="devicesGroup">
+ <property name="title">
+ <string>Input Device</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>5</number>
+ </property>
+ <property name="rightMargin">
+ <number>5</number>
+ </property>
+ <property name="bottomMargin">
+ <number>5</number>
+ </property>
<item>
- <layout class="QHBoxLayout" name="buttonRStickModHorizontalLayout">
+ <widget class="QComboBox" name="comboDevices">
<item>
- <widget class="QLabel" name="labelRStickMod">
- <property name="text">
- <string>Modifier:</string>
- </property>
- </widget>
+ <property name="text">
+ <string>Any</string>
+ </property>
</item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickMod">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="4" column="0" colspan="2">
- <layout class="QVBoxLayout" name="sliderRStickDeadzoneAndModifierVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="sliderRStickDeadzoneAndModifierHorizontalLayout">
<item>
- <widget class="QLabel" name="labelRStickDeadzoneAndModifier">
- <property name="text">
- <string>Deadzone: 0</string>
- </property>
- <property name="alignment">
- <enum>Qt::AlignHCenter</enum>
- </property>
- </widget>
+ <property name="text">
+ <string>Keyboard/Mouse</string>
+ </property>
</item>
- </layout>
+ </widget>
</item>
<item>
- <widget class="QSlider" name="sliderRStickDeadzoneAndModifier">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <widget class="QPushButton" name="buttonRefreshDevices">
+ <property name="minimumSize">
+ <size>
+ <width>24</width>
+ <height>22</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>24</width>
+ <height>22</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
</property>
</widget>
</item>
</layout>
- </item>
- <item row="5" column="0">
- <spacer name="RStick_verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="profilesGroup">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Profile</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_4" stretch="2,0,0,0">
+ <property name="spacing">
+ <number>3</number>
</property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>5</number>
+ </property>
+ <property name="rightMargin">
+ <number>5</number>
+ </property>
+ <property name="bottomMargin">
+ <number>5</number>
</property>
- </spacer>
- </item>
- </layout>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QGroupBox" name="Dpad">
- <property name="title">
- <string>Directional Pad</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
<item>
- <layout class="QHBoxLayout" name="buttonDpadUpHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelDpadUp">
- <property name="text">
- <string>Up:</string>
- </property>
- </widget>
- </item>
- </layout>
+ <widget class="QComboBox" name="comboProfiles"/>
</item>
<item>
- <widget class="QPushButton" name="buttonDpadUp">
- <property name="text">
- <string/>
+ <widget class="QPushButton" name="buttonProfilesSave">
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonDpadDownHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelDpadDown">
- <property name="text">
- <string>Down:</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonDpadDown">
<property name="text">
- <string/>
+ <string>Save</string>
</property>
</widget>
</item>
- </layout>
- </item>
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonDpadLeftHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelDpadLeft">
- <property name="minimumSize">
- <size>
- <width>80</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Left:</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
<item>
- <widget class="QPushButton" name="buttonDpadLeft">
+ <widget class="QPushButton" name="buttonProfilesNew">
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
<property name="text">
- <string/>
+ <string>New</string>
</property>
</widget>
</item>
- </layout>
- </item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonDpadRightHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelDpadRight">
- <property name="minimumSize">
- <size>
- <width>80</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Right:</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
<item>
- <widget class="QPushButton" name="buttonDpadRight">
+ <widget class="QPushButton" name="buttonProfilesDelete">
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
<property name="text">
- <string/>
+ <string>Delete</string>
</property>
</widget>
</item>
</layout>
- </item>
- </layout>
- </widget>
+ </widget>
+ </item>
+ </layout>
</item>
- <item row="0" column="0">
- <widget class="QGroupBox" name="faceButtons">
- <property name="title">
- <string>Face Buttons</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
+ <item>
+ <widget class="QFrame" name="bottom">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonFaceButtonsAHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelA">
- <property name="minimumSize">
- <size>
- <width>80</width>
- <height>0</height>
- </size>
+ <layout class="QHBoxLayout" name="_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="bottomLeft" native="true">
+ <layout class="QVBoxLayout" name="bottomLeftLayout" stretch="0,0,0,0">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="LStick">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Left Stick</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>0</number>
</property>
- <property name="text">
- <string>A:</string>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonA">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonFaceButtonsBHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelB">
- <property name="minimumSize">
- <size>
- <width>80</width>
- <height>0</height>
- </size>
+ <property name="leftMargin">
+ <number>3</number>
</property>
- <property name="text">
- <string>B:</string>
+ <property name="topMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonB">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonFaceButtonsXHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelX">
- <property name="text">
- <string>X:</string>
+ <property name="rightMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonX">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonFaceButtonsYHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelY">
- <property name="text">
- <string>Y:</string>
+ <property name="bottomMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonY">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- </item>
- <item row="5" column="0" colspan="2">
- <widget class="QGroupBox" name="controller_color">
- <property name="title">
- <string>Controller Color</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_10" columnstretch="0,0,0,0,0,0,0">
- <item row="0" column="0">
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="left_body_label">
- <property name="text">
- <string>Left Body</string>
- </property>
- </widget>
- </item>
- <item row="0" column="6">
- <spacer name="horizontalSpacer_3">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="left_buttons_label">
- <property name="minimumSize">
- <size>
- <width>90</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Left Buttons</string>
- </property>
- </widget>
- </item>
- <item row="1" column="5">
- <widget class="QPushButton" name="right_buttons_button">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>32</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>40</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="4">
- <widget class="QLabel" name="right_body_label">
- <property name="text">
- <string>Right Body</string>
- </property>
- </widget>
- </item>
- <item row="1" column="4">
- <widget class="QLabel" name="right_buttons_label">
- <property name="minimumSize">
- <size>
- <width>90</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Right Buttons</string>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <widget class="QPushButton" name="left_buttons_button">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>32</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>40</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QPushButton" name="left_body_button">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>32</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>40</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="5">
- <widget class="QPushButton" name="right_body_button">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>32</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>40</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="3">
- <spacer name="horizontalSpacer_4">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QGroupBox" name="LStick">
- <property name="title">
- <string>Left Stick</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_4">
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonLStickUpHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelLStickUp">
- <property name="text">
- <string>Up:</string>
+ <item>
+ <widget class="QWidget" name="buttonLStickUpWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_20">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerLStickUpLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonLStickUpGroup">
+ <property name="title">
+ <string>Up</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonLStickUp">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Up</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerLStickUpRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonLStickLeftRightHorizontaLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonLStickLeftGroup">
+ <property name="title">
+ <string>Left</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonLStickLeft">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Left</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonLStickRightGroup">
+ <property name="title">
+ <string>Right</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonLStickRight">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Right</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonLStickDownWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_22">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerLStickDownLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonLStickDownGroup">
+ <property name="title">
+ <string>Down</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonLStickDown">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Down</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerLStickDownRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonLStickPressedModifierHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonLStickPressedGroup">
+ <property name="title">
+ <string>Pressed</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonLStick">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Pressed</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonLStickModGroup">
+ <property name="title">
+ <string>Modifier</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonLStickMod">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Modifier</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="buttonLStickRangeGroup">
+ <property name="title">
+ <string>Range</string>
+ </property>
+ <layout class="QHBoxLayout" name="buttonLStickRangeGroupHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="spinboxLStickRange">
+ <property name="minimumSize">
+ <size>
+ <width>55</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>50</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="sliderLStickDeadzoneModifierRangeVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="sliderLStickDeadzoneHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelLStickDeadzone">
+ <property name="text">
+ <string>Deadzone: 0%</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QSlider" name="sliderLStickDeadzone">
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="sliderLStickModifierRangeHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelLStickModifierRange">
+ <property name="text">
+ <string>Modifier Range: 0%</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QSlider" name="sliderLStickModifierRange">
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacerBottomLeft">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="Dpad">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>D-Pad</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <property name="spacing">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickUp">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="2">
- <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonLStickRightHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelLStickRight">
- <property name="text">
- <string>Right:</string>
+ <property name="leftMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickRight">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="4" column="1" colspan="2">
- <widget class="QPushButton" name="buttonLStickAnalog">
- <property name="text">
- <string>Set Analog Stick</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonLStickLeftHorizontalLayout_2">
- <item>
- <widget class="QLabel" name="labelLStickLeft">
- <property name="text">
- <string>Left:</string>
+ <property name="topMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickLeft">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="2">
- <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonLStickDownHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelLStickDown">
- <property name="text">
- <string>Down:</string>
+ <property name="rightMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickDown">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="3" column="2">
- <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonLStickModHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelLStickMod">
- <property name="text">
- <string>Modifier:</string>
+ <property name="bottomMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickMod">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item>
+ <widget class="QWidget" name="buttonDpadUpWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_23">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerDpadUpLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonDpadUpGroup">
+ <property name="title">
+ <string>Up</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonDpadUp">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Up</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerDpadUpRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonDpadLeftRightHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonDpadLeftGroup">
+ <property name="title">
+ <string>Left</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonDpadLeft">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Left</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonDpadRightGroup">
+ <property name="title">
+ <string>Right</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonDpadRight">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Right</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonDpadDownWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_24">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerDpadDownLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonDpadDownGroup">
+ <property name="title">
+ <string>Down</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonDpadDown">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Down</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerDpadDownRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacerBottomLeft_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
</item>
- <item row="3" column="1">
- <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0">
- <item>
- <layout class="QHBoxLayout" name="buttonLStickPressedHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelLStickPressed">
- <property name="text">
- <string>Pressed:</string>
+ <item>
+ <widget class="QWidget" name="bottomMiddle" native="true">
+ <layout class="QVBoxLayout" stretch="0,0,0">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="shoulderButtons">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="buttonShoulderButtonsLeft" native="true">
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsLeftVerticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="buttonShoulderButtonsButtonLGroup">
+ <property name="title">
+ <string>L</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonL">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>L</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="buttonShoulderButtonsButtonZLGroup">
+ <property name="title">
+ <string>ZL</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonZL">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>ZL</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidgetLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerShoulderButtons1">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonMiscButtonsMinusScreenshot" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsMinusScreenshotVerticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonMiscButtonsMinusGroup">
+ <property name="title">
+ <string>Minus</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonMinus">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Minus</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonMiscButtonsScreenshotGroup">
+ <property name="title">
+ <string>Capture</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonScreenshot">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Capture</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonMiscButtonsPlusHome" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsPlusHomeVerticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonMiscButtonsPlusGroup">
+ <property name="title">
+ <string>Plus</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonPlus">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Plus</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonMiscButtonsHomeGroup">
+ <property name="title">
+ <string>Home</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonHome">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Home</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget3" native="true">
+ <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget3Layout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerShoulderButtons2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonShoulderButtonsRight" native="true">
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsRightVerticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="buttonShoulderButtonsRGroup">
+ <property name="title">
+ <string>R</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonR">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>R</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="buttonShoulderButtonsZRGroup">
+ <property name="title">
+ <string>ZR</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonZR">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>ZR</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget2" native="true">
+ <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget2Layout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerShoulderButtons3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonShoulderButtonsSLSR" native="true">
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRVerticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonShoulderButtonsSLGroup">
+ <property name="title">
+ <string>SL</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonSL">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>SL</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonShoulderButtonsSRGroup">
+ <property name="title">
+ <string>SR</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonSR">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>SR</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QFrame" name="controllerFrame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">image: url(:/controller/pro);</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <property name="leftMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStick">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="5" column="1" colspan="2">
- <layout class="QVBoxLayout" name="sliderLStickDeadzoneAndModifierVerticalLayout">
- <property name="sizeConstraint">
- <enum>QLayout::SetDefaultConstraint</enum>
- </property>
- <item>
- <layout class="QHBoxLayout" name="sliderLStickDeadzoneAndModifierHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelLStickDeadzoneAndModifier">
- <property name="text">
- <string>Deadzone: 0</string>
+ <property name="topMargin">
+ <number>0</number>
</property>
- <property name="alignment">
- <enum>Qt::AlignHCenter</enum>
+ <property name="rightMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QSlider" name="sliderLStickDeadzoneAndModifier">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="6" column="1">
- <spacer name="LStick_verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QGroupBox" name="shoulderButtons">
- <property name="title">
- <string>Shoulder Buttons</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelL">
- <property name="text">
- <string>L:</string>
+ <property name="bottomMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonL">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="miscButtons">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerMiscButtons1">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerMiscButtons4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
</item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelR">
- <property name="text">
- <string>R:</string>
+ <item>
+ <widget class="QWidget" name="bottomRight" native="true">
+ <layout class="QVBoxLayout" name="bottomRightLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="faceButtons">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Face Buttons</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonR">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelZL">
- <property name="text">
- <string>ZL:</string>
+ <property name="leftMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonZL">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelZR">
- <property name="text">
- <string>ZR:</string>
+ <property name="topMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonZR">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="2">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelSL">
- <property name="text">
- <string>SL:</string>
+ <property name="rightMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonSL">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="2">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelSR">
- <property name="text">
- <string>SR:</string>
+ <property name="bottomMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonSR">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QGroupBox" name="misc">
- <property name="title">
- <string>Misc.</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_6">
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonMiscMinusHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelMinus">
- <property name="text">
- <string>Minus:</string>
+ <item>
+ <widget class="QWidget" name="buttonFaceButtonsBWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerBLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonFaceButtonsXGroup">
+ <property name="title">
+ <string>X</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonX">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerBRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonFaceButtonsYAHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonFaceButtonsYGroup">
+ <property name="title">
+ <string>Y</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonY">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonFaceButtonsAGroup">
+ <property name="title">
+ <string>A</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonA">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>A</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonFaceButtonsXWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_10">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerXLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonFaceButtonsBWidget_2">
+ <property name="title">
+ <string>B</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonB">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>B</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerXRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacerBottomRight">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="RStick">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Right Stick</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="spacing">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonMinus">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="3" column="1">
- <spacer name="verticalSpacer_2">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonMiscPlusHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelPlus">
- <property name="text">
- <string>Plus:</string>
+ <property name="leftMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonPlus">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonMiscHomeHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelHome">
- <property name="text">
- <string>Home:</string>
+ <property name="topMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonHome">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonMiscScrCapHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelScreenshot">
- <property name="text">
- <string>Screen Capture:</string>
+ <property name="rightMargin">
+ <number>3</number>
</property>
- <property name="wordWrap">
- <bool>false</bool>
+ <property name="bottomMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonScreenshot">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="maximumSize">
- <size>
- <width>80</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item>
+ <widget class="QWidget" name="buttonRStickUpWidget" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_9">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerRStickUpLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonRStickUpGroup">
+ <property name="title">
+ <string>Up</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRStickUp">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Up</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerRStickUpRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonRStickLeftRightHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonRStickLeftGroup">
+ <property name="title">
+ <string>Left</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRStickLeft">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Left</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonRStickRightGroup">
+ <property name="title">
+ <string>Right</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRStickRight">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Right</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonRStickDownWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerRStickDownLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="buttonRStickDownGroup">
+ <property name="title">
+ <string>Down</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRStickDown">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Down</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerRStickDownRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonRStickPressedModifierHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="groupRStickPressed">
+ <property name="title">
+ <string>Pressed</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRStick">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Pressed</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonRStickModGroup">
+ <property name="title">
+ <string>Modifier</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRStickMod">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 55px;</string>
+ </property>
+ <property name="text">
+ <string>Modifier</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="buttonRStickRangeGroup">
+ <property name="title">
+ <string>Range</string>
+ </property>
+ <layout class="QHBoxLayout" name="buttonRStickRangeGroupHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="spinboxRStickRange">
+ <property name="minimumSize">
+ <size>
+ <width>55</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>55</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>50</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="sliderRStickDeadzoneModifierRangeVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="sliderRStickDeadzoneHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelRStickDeadzone">
+ <property name="text">
+ <string>Deadzone: 0%</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QSlider" name="sliderRStickDeadzone">
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="sliderRStickModifierRangeHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelRStickModifierRange">
+ <property name="text">
+ <string>Modifier Range: 0%</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QSlider" name="sliderRStickModifierRange">
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacerBottomRight_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout"/>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <item>
- <widget class="QPushButton" name="buttonClearAll">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="sizeIncrement">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="baseSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="layoutDirection">
- <enum>Qt::LeftToRight</enum>
- </property>
- <property name="text">
- <string>Clear All</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRestoreDefaults">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="sizeIncrement">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="baseSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="layoutDirection">
- <enum>Qt::LeftToRight</enum>
- </property>
- <property name="text">
- <string>Restore Defaults</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- </widget>
- </item>
- </layout>
- </item>
</layout>
</widget>
- <resources/>
- <connections>
- <connection>
- <sender>buttonBox</sender>
- <signal>accepted()</signal>
- <receiver>ConfigureInputPlayer</receiver>
- <slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>371</x>
- <y>730</y>
- </hint>
- <hint type="destinationlabel">
- <x>229</x>
- <y>375</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>buttonBox</sender>
- <signal>rejected()</signal>
- <receiver>ConfigureInputPlayer</receiver>
- <slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>371</x>
- <y>730</y>
- </hint>
- <hint type="destinationlabel">
- <x>229</x>
- <y>375</y>
- </hint>
- </hints>
- </connection>
- </connections>
+ <resources>
+ <include location="../../../dist/icons/controller/controller.qrc"/>
+ </resources>
+ <connections/>
</ui>
diff --git a/src/yuzu/configuration/configure_input_simple.cpp b/src/yuzu/configuration/configure_input_simple.cpp
deleted file mode 100644
index 0e0e8f113..000000000
--- a/src/yuzu/configuration/configure_input_simple.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <array>
-#include <tuple>
-
-#include "ui_configure_input_simple.h"
-#include "yuzu/configuration/configure_input.h"
-#include "yuzu/configuration/configure_input_player.h"
-#include "yuzu/configuration/configure_input_simple.h"
-#include "yuzu/uisettings.h"
-
-namespace {
-
-template <typename Dialog, typename... Args>
-void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) {
- caller->ApplyConfiguration();
- Dialog dialog(caller, std::forward<Args>(args)...);
-
- const auto res = dialog.exec();
- if (res == QDialog::Accepted) {
- dialog.ApplyConfiguration();
- }
-}
-
-// OnProfileSelect functions should (when applicable):
-// - Set controller types
-// - Set controller enabled
-// - Set docked mode
-// - Set advanced controller config/enabled (i.e. debug, kbd, mouse, touch)
-//
-// OnProfileSelect function should NOT however:
-// - Reset any button mappings
-// - Open any dialogs
-// - Block in any way
-
-constexpr std::size_t PLAYER_0_INDEX = 0;
-constexpr std::size_t HANDHELD_INDEX = 8;
-
-void HandheldOnProfileSelect() {
- Settings::values.players[HANDHELD_INDEX].connected = true;
- Settings::values.players[HANDHELD_INDEX].type = Settings::ControllerType::DualJoycon;
-
- for (std::size_t player = 0; player < HANDHELD_INDEX; ++player) {
- Settings::values.players[player].connected = false;
- }
-
- Settings::values.use_docked_mode = false;
- Settings::values.keyboard_enabled = false;
- Settings::values.mouse_enabled = false;
- Settings::values.debug_pad_enabled = false;
- Settings::values.touchscreen.enabled = true;
-}
-
-void DualJoyconsDockedOnProfileSelect() {
- Settings::values.players[PLAYER_0_INDEX].connected = true;
- Settings::values.players[PLAYER_0_INDEX].type = Settings::ControllerType::DualJoycon;
-
- for (std::size_t player = 1; player <= HANDHELD_INDEX; ++player) {
- Settings::values.players[player].connected = false;
- }
-
- Settings::values.use_docked_mode = true;
- Settings::values.keyboard_enabled = false;
- Settings::values.mouse_enabled = false;
- Settings::values.debug_pad_enabled = false;
- Settings::values.touchscreen.enabled = true;
-}
-
-// Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure
-// is clicked)
-using InputProfile = std::tuple<const char*, void (*)(), void (*)(ConfigureInputSimple*)>;
-
-constexpr std::array<InputProfile, 3> INPUT_PROFILES{{
- {QT_TR_NOOP("Single Player - Handheld - Undocked"), HandheldOnProfileSelect,
- [](ConfigureInputSimple* caller) {
- CallConfigureDialog<ConfigureInputPlayer>(caller, HANDHELD_INDEX, false);
- }},
- {QT_TR_NOOP("Single Player - Dual Joycons - Docked"), DualJoyconsDockedOnProfileSelect,
- [](ConfigureInputSimple* caller) {
- CallConfigureDialog<ConfigureInputPlayer>(caller, PLAYER_0_INDEX, false);
- }},
- {QT_TR_NOOP("Custom"), [] {}, CallConfigureDialog<ConfigureInput>},
-}};
-
-} // namespace
-
-void ApplyInputProfileConfiguration(int profile_index) {
- std::get<1>(
- INPUT_PROFILES.at(std::min(profile_index, static_cast<int>(INPUT_PROFILES.size() - 1))))();
-}
-
-ConfigureInputSimple::ConfigureInputSimple(QWidget* parent)
- : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputSimple>()) {
- ui->setupUi(this);
-
- for (const auto& profile : INPUT_PROFILES) {
- const QString label = tr(std::get<0>(profile));
- ui->profile_combobox->addItem(label, label);
- }
-
- connect(ui->profile_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
- &ConfigureInputSimple::OnSelectProfile);
- connect(ui->profile_configure, &QPushButton::clicked, this, &ConfigureInputSimple::OnConfigure);
-
- LoadConfiguration();
-}
-
-ConfigureInputSimple::~ConfigureInputSimple() = default;
-
-void ConfigureInputSimple::ApplyConfiguration() {
- auto index = ui->profile_combobox->currentIndex();
- // Make the stored index for "Custom" very large so that if new profiles are added it
- // doesn't change.
- if (index >= static_cast<int>(INPUT_PROFILES.size() - 1)) {
- index = std::numeric_limits<int>::max();
- }
-
- UISettings::values.profile_index = index;
-}
-
-void ConfigureInputSimple::changeEvent(QEvent* event) {
- if (event->type() == QEvent::LanguageChange) {
- RetranslateUI();
- }
-
- QWidget::changeEvent(event);
-}
-
-void ConfigureInputSimple::RetranslateUI() {
- ui->retranslateUi(this);
-}
-
-void ConfigureInputSimple::LoadConfiguration() {
- const auto index = UISettings::values.profile_index;
- if (index >= static_cast<int>(INPUT_PROFILES.size()) || index < 0) {
- ui->profile_combobox->setCurrentIndex(static_cast<int>(INPUT_PROFILES.size() - 1));
- } else {
- ui->profile_combobox->setCurrentIndex(index);
- }
-}
-
-void ConfigureInputSimple::OnSelectProfile(int index) {
- const auto old_docked = Settings::values.use_docked_mode;
- ApplyInputProfileConfiguration(index);
- OnDockedModeChanged(old_docked, Settings::values.use_docked_mode);
-}
-
-void ConfigureInputSimple::OnConfigure() {
- std::get<2>(INPUT_PROFILES.at(ui->profile_combobox->currentIndex()))(this);
-}
diff --git a/src/yuzu/configuration/configure_input_simple.h b/src/yuzu/configuration/configure_input_simple.h
deleted file mode 100644
index bb5050224..000000000
--- a/src/yuzu/configuration/configure_input_simple.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-
-#include <QWidget>
-
-class QPushButton;
-class QString;
-class QTimer;
-
-namespace Ui {
-class ConfigureInputSimple;
-}
-
-// Used by configuration loader to apply a profile if the input is invalid.
-void ApplyInputProfileConfiguration(int profile_index);
-
-class ConfigureInputSimple : public QWidget {
- Q_OBJECT
-
-public:
- explicit ConfigureInputSimple(QWidget* parent = nullptr);
- ~ConfigureInputSimple() override;
-
- /// Save all button configurations to settings file
- void ApplyConfiguration();
-
-private:
- void changeEvent(QEvent* event) override;
- void RetranslateUI();
-
- /// Load configuration settings.
- void LoadConfiguration();
-
- void OnSelectProfile(int index);
- void OnConfigure();
-
- std::unique_ptr<Ui::ConfigureInputSimple> ui;
-};
diff --git a/src/yuzu/configuration/configure_input_simple.ui b/src/yuzu/configuration/configure_input_simple.ui
deleted file mode 100644
index c4889caa9..000000000
--- a/src/yuzu/configuration/configure_input_simple.ui
+++ /dev/null
@@ -1,97 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>ConfigureInputSimple</class>
- <widget class="QWidget" name="ConfigureInputSimple">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>473</width>
- <height>685</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>ConfigureInputSimple</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_5">
- <item>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QGroupBox" name="gridGroupBox">
- <property name="title">
- <string>Profile</string>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="1" column="2">
- <widget class="QPushButton" name="profile_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="3">
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="1">
- <widget class="QComboBox" name="profile_combobox">
- <property name="minimumSize">
- <size>
- <width>250</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="0" column="1" colspan="2">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Choose a controller configuration:</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp
index ea2549363..2af3afda8 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.cpp
+++ b/src/yuzu/configuration/configure_mouse_advanced.cpp
@@ -18,6 +18,16 @@
static QString GetKeyName(int key_code) {
switch (key_code) {
+ case Qt::LeftButton:
+ return QObject::tr("Click 0");
+ case Qt::RightButton:
+ return QObject::tr("Click 1");
+ case Qt::MiddleButton:
+ return QObject::tr("Click 2");
+ case Qt::BackButton:
+ return QObject::tr("Click 3");
+ case Qt::ForwardButton:
+ return QObject::tr("Click 4");
case Qt::Key_Shift:
return QObject::tr("Shift");
case Qt::Key_Control:
@@ -66,8 +76,10 @@ static QString ButtonToText(const Common::ParamPackage& param) {
return QObject::tr("[unknown]");
}
-ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
- : QDialog(parent), ui(std::make_unique<Ui::ConfigureMouseAdvanced>()),
+ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent,
+ InputCommon::InputSubsystem* input_subsystem_)
+ : QDialog(parent),
+ ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), input_subsystem{input_subsystem_},
timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
ui->setupUi(this);
setFocusPolicy(Qt::ClickFocus);
@@ -84,11 +96,12 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(button, &QPushButton::clicked, [=, this] {
- HandleClick(button_map[button_id],
- [=, this](const Common::ParamPackage& params) {
- buttons_param[button_id] = params;
- },
- InputCommon::Polling::DeviceType::Button);
+ HandleClick(
+ button_map[button_id],
+ [=, this](const Common::ParamPackage& params) {
+ buttons_param[button_id] = params;
+ },
+ InputCommon::Polling::DeviceType::Button);
});
connect(button, &QPushButton::customContextMenuRequested,
[=, this](const QPoint& menu_location) {
@@ -187,9 +200,9 @@ void ConfigureMouseAdvanced::HandleClick(
button->setText(tr("[press key]"));
button->setFocus();
- // Keyboard keys can only be used as button devices
- want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
- if (want_keyboard_keys) {
+ // Keyboard keys or mouse buttons can only be used as button devices
+ want_keyboard_mouse = type == InputCommon::Polling::DeviceType::Button;
+ if (want_keyboard_mouse) {
const auto iter = std::find(button_map.begin(), button_map.end(), button);
ASSERT(iter != button_map.end());
const auto index = std::distance(button_map.begin(), iter);
@@ -198,27 +211,29 @@ void ConfigureMouseAdvanced::HandleClick(
input_setter = new_input_setter;
- device_pollers = InputCommon::Polling::GetPollers(type);
+ device_pollers = input_subsystem->GetPollers(type);
for (auto& poller : device_pollers) {
poller->Start();
}
- grabKeyboard();
- grabMouse();
- timeout_timer->start(5000); // Cancel after 5 seconds
- poll_timer->start(200); // Check for new inputs every 200ms
+ QWidget::grabMouse();
+ QWidget::grabKeyboard();
+
+ timeout_timer->start(2500); // Cancel after 2.5 seconds
+ poll_timer->start(50); // Check for new inputs every 50ms
}
void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) {
- releaseKeyboard();
- releaseMouse();
timeout_timer->stop();
poll_timer->stop();
for (auto& poller : device_pollers) {
poller->Stop();
}
+ QWidget::releaseMouse();
+ QWidget::releaseKeyboard();
+
if (!abort) {
(*input_setter)(params);
}
@@ -227,13 +242,29 @@ void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params
input_setter = std::nullopt;
}
+void ConfigureMouseAdvanced::mousePressEvent(QMouseEvent* event) {
+ if (!input_setter || !event) {
+ return;
+ }
+
+ if (want_keyboard_mouse) {
+ SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
+ false);
+ } else {
+ // We don't want any mouse buttons, so don't stop polling
+ return;
+ }
+
+ SetPollingResult({}, true);
+}
+
void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
if (!input_setter || !event) {
return;
}
if (event->key() != Qt::Key_Escape) {
- if (want_keyboard_keys) {
+ if (want_keyboard_mouse) {
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
false);
} else {
diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h
index 342b82412..65b6fca9a 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.h
+++ b/src/yuzu/configuration/configure_mouse_advanced.h
@@ -8,12 +8,14 @@
#include <optional>
#include <QDialog>
-#include "core/settings.h"
-
class QCheckBox;
class QPushButton;
class QTimer;
+namespace InputCommon {
+class InputSubsystem;
+}
+
namespace Ui {
class ConfigureMouseAdvanced;
}
@@ -22,7 +24,7 @@ class ConfigureMouseAdvanced : public QDialog {
Q_OBJECT
public:
- explicit ConfigureMouseAdvanced(QWidget* parent);
+ explicit ConfigureMouseAdvanced(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_);
~ConfigureMouseAdvanced() override;
void ApplyConfiguration();
@@ -49,11 +51,16 @@ private:
/// Finish polling and configure input using the input_setter
void SetPollingResult(const Common::ParamPackage& params, bool abort);
+ /// Handle mouse button press events.
+ void mousePressEvent(QMouseEvent* event) override;
+
/// Handle key press events.
void keyPressEvent(QKeyEvent* event) override;
std::unique_ptr<Ui::ConfigureMouseAdvanced> ui;
+ InputCommon::InputSubsystem* input_subsystem;
+
/// This will be the the setting function when an input is awaiting configuration.
std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
@@ -67,5 +74,5 @@ private:
/// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
/// keyboard events are ignored.
- bool want_keyboard_keys = false;
+ bool want_keyboard_mouse = false;
};
diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui
index 08245ecf0..74552fdbd 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.ui
+++ b/src/yuzu/configuration/configure_mouse_advanced.ui
@@ -6,13 +6,18 @@
<rect>
<x>0</x>
<y>0</y>
- <width>250</width>
- <height>261</height>
+ <width>310</width>
+ <height>193</height>
</rect>
</property>
<property name="windowTitle">
<string>Configure Mouse</string>
</property>
+ <property name="styleSheet">
+ <string notr="true">QPushButton {
+ min-width: 55px;
+}</string>
+ </property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="gridGroupBox">
@@ -20,81 +25,33 @@
<string>Mouse Buttons</string>
</property>
<layout class="QGridLayout" name="gridLayout">
- <item row="0" column="4">
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="3">
- <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item row="3" column="5">
+ <layout class="QVBoxLayout" name="verticalLayout_6">
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
- <widget class="QLabel" name="label_3">
+ <widget class="QLabel" name="label_5">
<property name="text">
- <string>Right:</string>
+ <string>Forward:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
- <widget class="QPushButton" name="right_button">
+ <widget class="QPushButton" name="forward_button">
<property name="minimumSize">
<size>
- <width>75</width>
+ <width>57</width>
<height>0</height>
</size>
</property>
- <property name="text">
- <string/>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="0">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="2" column="1">
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <item>
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Middle:</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="middle_button">
<property name="text">
<string/>
</property>
@@ -123,6 +80,12 @@
</item>
<item>
<widget class="QPushButton" name="back_button">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
<property name="text">
<string/>
</property>
@@ -147,7 +110,7 @@
<widget class="QPushButton" name="left_button">
<property name="minimumSize">
<size>
- <width>75</width>
+ <width>57</width>
<height>0</height>
</size>
</property>
@@ -158,21 +121,99 @@
</item>
</layout>
</item>
- <item row="3" column="3">
- <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item row="0" column="3">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
- <widget class="QLabel" name="label_5">
+ <widget class="QLabel" name="label_2">
<property name="text">
- <string>Forward:</string>
+ <string>Middle:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
- <widget class="QPushButton" name="forward_button">
+ <widget class="QPushButton" name="middle_button">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="6">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="0">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="5">
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Right:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="right_button">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
<property name="text">
<string/>
</property>
@@ -180,6 +221,32 @@
</item>
</layout>
</item>
+ <item row="0" column="2">
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="4">
+ <spacer name="horizontalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
</item>
@@ -187,15 +254,39 @@
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QPushButton" name="buttonClearAll">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
<property name="text">
- <string>Clear All</string>
+ <string>Clear</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonRestoreDefaults">
+ <property name="minimumSize">
+ <size>
+ <width>57</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
<property name="text">
- <string>Restore Defaults</string>
+ <string>Defaults</string>
</property>
</widget>
</item>
@@ -206,21 +297,24 @@
</property>
<property name="sizeHint" stdset="0">
<size>
- <width>40</width>
+ <width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- </widget>
- </item>
</layout>
</widget>
<resources/>
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() {
std::sort(disabled_addons.begin(), disabled_addons.end());
std::sort(current.begin(), current.end());
if (disabled_addons != current) {
- FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
- "game_list" + DIR_SEP + fmt::format("{:016X}.pv.txt", title_id));
+ Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
+ "game_list" + DIR_SEP + fmt::format("{:016X}.pv.txt", title_id));
}
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{
};
QString GetImagePath(Common::UUID uuid) {
- const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
+ const auto path = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
return QString::fromStdString(path);
}
@@ -282,7 +282,7 @@ void ConfigureProfileManager::SetUserImage() {
}
const auto raw_path = QString::fromStdString(
- FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010");
+ Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + "/system/save/8000000000000010");
const QFileInfo raw_info{raw_path};
if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) {
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() {
&Settings::values.time_zone_index);
ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index);
- ConfigurationShared::SetHighlight(ui->label_language, "label_language",
+ ConfigurationShared::SetHighlight(ui->label_language,
!Settings::values.language_index.UsingGlobal());
- ConfigurationShared::SetHighlight(ui->label_region, "label_region",
+ ConfigurationShared::SetHighlight(ui->label_region,
!Settings::values.region_index.UsingGlobal());
- ConfigurationShared::SetHighlight(ui->label_timezone, "label_timezone",
+ ConfigurationShared::SetHighlight(ui->label_timezone,
!Settings::values.time_zone_index.UsingGlobal());
- ConfigurationShared::SetHighlight(ui->label_sound, "label_sound",
+ ConfigurationShared::SetHighlight(ui->label_sound,
!Settings::values.sound_index.UsingGlobal());
}
}
@@ -224,22 +224,20 @@ void ConfigureSystem::SetupPerGameUI() {
}
ConfigurationShared::SetColoredComboBox(ui->combo_language, ui->label_language,
- "label_language",
Settings::values.language_index.GetValue(true));
- ConfigurationShared::SetColoredComboBox(ui->combo_region, ui->label_region, "label_region",
+ ConfigurationShared::SetColoredComboBox(ui->combo_region, ui->label_region,
Settings::values.region_index.GetValue(true));
ConfigurationShared::SetColoredComboBox(ui->combo_time_zone, ui->label_timezone,
- "label_timezone",
Settings::values.time_zone_index.GetValue(true));
- ConfigurationShared::SetColoredComboBox(ui->combo_sound, ui->label_sound, "label_sound",
+ ConfigurationShared::SetColoredComboBox(ui->combo_sound, ui->label_sound,
Settings::values.sound_index.GetValue(true));
ConfigurationShared::SetColoredTristate(
- ui->rng_seed_checkbox, "rng_seed_checkbox", Settings::values.rng_seed.UsingGlobal(),
+ ui->rng_seed_checkbox, Settings::values.rng_seed.UsingGlobal(),
Settings::values.rng_seed.GetValue().has_value(),
Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed);
ConfigurationShared::SetColoredTristate(
- ui->custom_rtc_checkbox, "custom_rtc_checkbox", Settings::values.custom_rtc.UsingGlobal(),
+ ui->custom_rtc_checkbox, Settings::values.custom_rtc.UsingGlobal(),
Settings::values.custom_rtc.GetValue().has_value(),
Settings::values.custom_rtc.GetValue(true).has_value(), use_custom_rtc);
}
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
// Set screenshot path to user specification.
connect(ui->screenshot_path_button, &QToolButton::pressed, this, [this] {
const QString& filename =
- QFileDialog::getExistingDirectory(
- this, tr("Select Screenshots Path..."),
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))) +
+ QFileDialog::getExistingDirectory(this, tr("Select Screenshots Path..."),
+ QString::fromStdString(Common::FS::GetUserPath(
+ Common::FS::UserPath::ScreenshotsDir))) +
QDir::separator();
if (!filename.isEmpty()) {
ui->screenshot_path_edit->setText(filename);
@@ -82,8 +82,8 @@ void ConfigureUi::ApplyConfiguration() {
UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked();
- FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir,
- ui->screenshot_path_edit->text().toStdString());
+ Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir,
+ ui->screenshot_path_edit->text().toStdString());
Settings::Apply();
}
@@ -101,7 +101,7 @@ void ConfigureUi::SetConfiguration() {
ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as);
ui->screenshot_path_edit->setText(
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir)));
+ QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir)));
}
void 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) {
MicroProfileSetDisplayMode(1); // Timers screen
MicroProfileInitUI();
- connect(&update_timer, &QTimer::timeout, this,
- static_cast<void (MicroProfileWidget::*)()>(&MicroProfileWidget::update));
+ connect(&update_timer, &QTimer::timeout, this, qOverload<>(&MicroProfileWidget::update));
}
void MicroProfileWidget::paintEvent(QPaintEvent* ev) {
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 62acc3720..6a71d9644 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -406,7 +406,7 @@ bool GameList::isEmpty() const {
type == GameListItemType::SysNandDir)) {
item_model->invisibleRootItem()->removeRow(child->row());
i--;
- };
+ }
}
return !item_model->invisibleRootItem()->hasChildren();
}
@@ -502,10 +502,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string pat
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
- emit OpenFolderRequested(GameListOpenTarget::SaveData, path);
+ emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path);
});
connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
- emit OpenFolderRequested(GameListOpenTarget::ModData, path);
+ emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path);
});
connect(open_transferable_shader_cache, &QAction::triggered,
[this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); });
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 483835cce..78e2ba169 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -84,7 +84,8 @@ public:
signals:
void GameChosen(QString game_path);
void ShouldCancelWorker();
- void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path);
+ void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
+ const std::string& game_path);
void OpenTransferableShaderCacheRequested(u64 program_id);
void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type);
void RemoveFileRequested(u64 program_id, GameListRemoveTarget target);
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&
return generator();
}
- const auto path = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" +
- DIR_SEP + filename + '.' + ext;
+ const auto path = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
+ "game_list" + DIR_SEP + filename + '.' + ext;
- FileUtil::CreateFullPath(path);
+ Common::FS::CreateFullPath(path);
- if (!FileUtil::Exists(path)) {
+ if (!Common::FS::Exists(path)) {
const auto str = generator();
QFile file{QString::fromStdString(path)};
@@ -70,14 +70,14 @@ std::pair<std::vector<u8>, std::string> GetGameListCachedObject(
return generator();
}
- const auto path1 = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" +
- DIR_SEP + filename + ".jpeg";
- const auto path2 = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" +
- DIR_SEP + filename + ".appname.txt";
+ const auto path1 = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
+ "game_list" + DIR_SEP + filename + ".jpeg";
+ const auto path2 = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
+ "game_list" + DIR_SEP + filename + ".appname.txt";
- FileUtil::CreateFullPath(path1);
+ Common::FS::CreateFullPath(path1);
- if (!FileUtil::Exists(path1) || !FileUtil::Exists(path2)) {
+ if (!Common::FS::Exists(path1) || !Common::FS::Exists(path2)) {
const auto [icon, nacp] = generator();
QFile file1{QString::fromStdString(path1)};
@@ -208,7 +208,7 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
file_type_string, program_id),
new GameListItemCompat(compatibility),
new GameListItem(file_type_string),
- new GameListItemSize(FileUtil::GetSize(path)),
+ new GameListItemSize(Common::FS::GetSize(path)),
};
if (UISettings::values.show_add_ons) {
@@ -289,7 +289,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
}
const std::string physical_name = directory + DIR_SEP + virtual_name;
- const bool is_dir = FileUtil::IsDirectory(physical_name);
+ const bool is_dir = Common::FS::IsDirectory(physical_name);
if (!is_dir &&
(HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read);
@@ -345,11 +345,12 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
return true;
};
- FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback);
+ Common::FS::ForeachDirectoryEntry(nullptr, dir_path, callback);
}
void GameListWorker::run() {
stop_processing = false;
+ provider->ClearAllEntries();
for (UISettings::GameDir& game_dir : game_dirs) {
if (game_dir.path == QStringLiteral("SDMC")) {
@@ -368,13 +369,12 @@ void GameListWorker::run() {
watch_list.append(game_dir.path);
auto* const game_list_dir = new GameListDir(game_dir);
emit DirEntryReady(game_list_dir);
- provider->ClearAllEntries();
ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(),
game_dir.deep_scan ? 256 : 0, game_list_dir);
ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(),
game_dir.deep_scan ? 256 : 0, game_list_dir);
}
- };
+ }
emit Finished(watch_list);
}
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 592993c36..a1b61d119 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -94,6 +94,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
+#include "input_common/main.h"
#include "video_core/gpu.h"
#include "video_core/shader_notify.h"
#include "yuzu/about_dialog.h"
@@ -177,8 +178,8 @@ static void InitializeLogging() {
log_filter.ParseFilterString(Settings::values.log_filter);
Log::SetGlobalFilter(log_filter);
- const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
- FileUtil::CreateFullPath(log_dir);
+ const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
+ Common::FS::CreateFullPath(log_dir);
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
#ifdef _WIN32
Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
@@ -186,9 +187,9 @@ static void InitializeLogging() {
}
GMainWindow::GMainWindow()
- : config(new Config()), emu_thread(nullptr),
- vfs(std::make_shared<FileSys::RealVfsFilesystem>()),
- provider(std::make_unique<FileSys::ManualContentProvider>()) {
+ : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
+ config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
+ provider{std::make_unique<FileSys::ManualContentProvider>()} {
InitializeLogging();
LoadTranslation();
@@ -473,7 +474,7 @@ void GMainWindow::InitializeWidgets() {
#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
ui.action_Report_Compatibility->setVisible(true);
#endif
- render_window = new GRenderWindow(this, emu_thread.get());
+ render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem);
render_window->hide();
game_list = new GameList(vfs, provider.get(), this);
@@ -894,6 +895,8 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ);
connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); });
connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
+ connect(ui.action_Configure_Current_Game, &QAction::triggered, this,
+ &GMainWindow::OnConfigurePerGame);
// View
connect(ui.action_Single_Window_Mode, &QAction::triggered, this,
@@ -1039,7 +1042,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
}
game_path = filename;
- system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "Qt");
+ system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "Qt");
return true;
}
@@ -1121,7 +1124,7 @@ void GMainWindow::BootGame(const QString& filename) {
title_name = metadata.first->GetApplicationName();
}
if (res != Loader::ResultStatus::Success || title_name.empty()) {
- title_name = FileUtil::GetFilename(filename.toStdString());
+ title_name = Common::FS::GetFilename(filename.toStdString());
}
LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
UpdateWindowTitle(title_name, title_version);
@@ -1167,6 +1170,7 @@ void GMainWindow::ShutdownGame() {
ui.action_Pause->setEnabled(false);
ui.action_Stop->setEnabled(false);
ui.action_Restart->setEnabled(false);
+ ui.action_Configure_Current_Game->setEnabled(false);
ui.action_Report_Compatibility->setEnabled(false);
ui.action_Load_Amiibo->setEnabled(false);
ui.action_Capture_Screenshot->setEnabled(false);
@@ -1239,27 +1243,36 @@ void GMainWindow::OnGameListLoadFile(QString game_path) {
BootGame(game_path);
}
-void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path) {
+void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
+ const std::string& game_path) {
std::string path;
QString open_target;
- const auto v_file = Core::GetGameFileFromPath(vfs, game_path);
- const auto loader = Loader::GetLoader(v_file);
- FileSys::NACP control{};
- u64 program_id{};
+ const auto [user_save_size, device_save_size] = [this, &program_id, &game_path] {
+ FileSys::PatchManager pm{program_id};
+ const auto control = pm.GetControlMetadata().first;
+ if (control != nullptr) {
+ return std::make_pair(control->GetDefaultNormalSaveSize(),
+ control->GetDeviceSaveDataSize());
+ } else {
+ const auto file = Core::GetGameFileFromPath(vfs, game_path);
+ const auto loader = Loader::GetLoader(file);
- loader->ReadControlData(control);
- loader->ReadProgramId(program_id);
+ FileSys::NACP nacp{};
+ loader->ReadControlData(nacp);
+ return std::make_pair(nacp.GetDefaultNormalSaveSize(), nacp.GetDeviceSaveDataSize());
+ }
+ }();
- const bool has_user_save{control.GetDefaultNormalSaveSize() > 0};
- const bool has_device_save{control.GetDeviceSaveDataSize() > 0};
+ const bool has_user_save{user_save_size > 0};
+ const bool has_device_save{device_save_size > 0};
ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?");
switch (target) {
case GameListOpenTarget::SaveData: {
open_target = tr("Save Data");
- const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
+ const std::string nand_dir = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
if (has_user_save) {
// User save data
@@ -1294,16 +1307,16 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str
FileSys::SaveDataType::SaveData, program_id, {}, 0);
}
- if (!FileUtil::Exists(path)) {
- FileUtil::CreateFullPath(path);
- FileUtil::CreateDir(path);
+ if (!Common::FS::Exists(path)) {
+ Common::FS::CreateFullPath(path);
+ Common::FS::CreateDir(path);
}
break;
}
case GameListOpenTarget::ModData: {
open_target = tr("Mod Data");
- const auto load_dir = FileUtil::GetUserPath(FileUtil::UserPath::LoadDir);
+ const auto load_dir = Common::FS::GetUserPath(Common::FS::UserPath::LoadDir);
path = fmt::format("{}{:016X}", load_dir, program_id);
break;
}
@@ -1325,7 +1338,7 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str
void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
const QString shader_dir =
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir));
+ QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir));
const QString transferable_shader_cache_folder_path =
shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
const QString transferable_shader_cache_file_path =
@@ -1428,8 +1441,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
RemoveAddOnContent(program_id, entry_type);
break;
}
- FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
- "game_list");
+ Common::FS::DeleteDirRecursively(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
+ DIR_SEP + "game_list");
game_list->PopulateAsync(UISettings::values.game_dirs);
}
@@ -1519,7 +1532,7 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ
void GMainWindow::RemoveTransferableShaderCache(u64 program_id) {
const QString shader_dir =
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir));
+ QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir));
const QString transferable_shader_cache_folder_path =
shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
const QString transferable_shader_cache_file_path =
@@ -1543,7 +1556,7 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id) {
void GMainWindow::RemoveCustomConfiguration(u64 program_id) {
const QString config_dir =
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir));
+ QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir));
const QString custom_config_file_path =
config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id));
@@ -1590,7 +1603,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
}
const auto path = fmt::format(
- "{}{:016X}/romfs", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), *romfs_title_id);
+ "{}{:016X}/romfs", Common::FS::GetUserPath(Common::FS::UserPath::DumpDir), *romfs_title_id);
FileSys::VirtualFile romfs;
@@ -1670,13 +1683,13 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
QString path;
if (directory == QStringLiteral("SDMC")) {
- path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) +
+ path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
"Nintendo/Contents/registered");
} else if (directory == QStringLiteral("UserNAND")) {
- path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
+ path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
"user/Contents/registered");
} else if (directory == QStringLiteral("SysNAND")) {
- path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
+ path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
"system/Contents/registered");
} else {
path = directory;
@@ -1690,8 +1703,10 @@ void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
void GMainWindow::OnGameListAddDirectory() {
const QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
- if (dir_path.isEmpty())
+ if (dir_path.isEmpty()) {
return;
+ }
+
UISettings::GameDir game_dir{dir_path, false, true};
if (!UISettings::values.game_dirs.contains(game_dir)) {
UISettings::values.game_dirs.append(game_dir);
@@ -1718,26 +1733,7 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
return;
}
- ConfigurePerGame dialog(this, title_id);
- dialog.LoadFromFile(v_file);
- auto result = dialog.exec();
- if (result == QDialog::Accepted) {
- dialog.ApplyConfiguration();
-
- const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
- if (reload) {
- game_list->PopulateAsync(UISettings::values.game_dirs);
- }
-
- // Do not cause the global config to write local settings into the config file
- Settings::RestoreGlobalState();
-
- if (!Core::System::GetInstance().IsPoweredOn()) {
- config->Save();
- }
- } else {
- Settings::RestoreGlobalState();
- }
+ OpenPerGameConfiguration(title_id, file);
}
void GMainWindow::OnMenuLoadFile() {
@@ -1882,8 +1878,8 @@ void GMainWindow::OnMenuInstallToNAND() {
: tr("%n file(s) failed to install\n", "", failed_files.size()));
QMessageBox::information(this, tr("Install Results"), install_results);
- FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
- "game_list");
+ Common::FS::DeleteDirRecursively(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
+ DIR_SEP + "game_list");
game_list->PopulateAsync(UISettings::values.game_dirs);
ui.action_Install_File_NAND->setEnabled(true);
}
@@ -2066,6 +2062,7 @@ void GMainWindow::OnStartGame() {
ui.action_Pause->setEnabled(true);
ui.action_Stop->setEnabled(true);
ui.action_Restart->setEnabled(true);
+ ui.action_Configure_Current_Game->setEnabled(true);
ui.action_Report_Compatibility->setEnabled(true);
discord_rpc->Update();
@@ -2217,7 +2214,7 @@ void GMainWindow::OnConfigure() {
const auto old_theme = UISettings::values.theme;
const bool old_discord_presence = UISettings::values.enable_discord_presence;
- ConfigureDialog configure_dialog(this, hotkey_registry);
+ ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get());
connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this,
&GMainWindow::OnLanguageChanged);
@@ -2255,6 +2252,36 @@ void GMainWindow::OnConfigure() {
UpdateStatusButtons();
}
+void GMainWindow::OnConfigurePerGame() {
+ const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
+ OpenPerGameConfiguration(title_id, game_path.toStdString());
+}
+
+void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_name) {
+ const auto v_file = Core::GetGameFileFromPath(vfs, file_name);
+
+ ConfigurePerGame dialog(this, title_id);
+ dialog.LoadFromFile(v_file);
+ auto result = dialog.exec();
+ if (result == QDialog::Accepted) {
+ dialog.ApplyConfiguration();
+
+ const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
+ if (reload) {
+ game_list->PopulateAsync(UISettings::values.game_dirs);
+ }
+
+ // Do not cause the global config to write local settings into the config file
+ Settings::RestoreGlobalState();
+
+ if (!Core::System::GetInstance().IsPoweredOn()) {
+ config->Save();
+ }
+ } else {
+ Settings::RestoreGlobalState();
+ }
+}
+
void GMainWindow::OnLoadAmiibo() {
const QString extensions{QStringLiteral("*.bin")};
const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
@@ -2302,7 +2329,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
void GMainWindow::OnOpenYuzuFolder() {
QDesktopServices::openUrl(QUrl::fromLocalFile(
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir))));
+ QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::UserDir))));
}
void GMainWindow::OnAbout() {
@@ -2324,7 +2351,7 @@ void GMainWindow::OnCaptureScreenshot() {
const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
const auto screenshot_path =
- QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir));
+ QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir));
const auto date =
QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz"));
QString filename = QStringLiteral("%1%2_%3.png")
@@ -2527,18 +2554,18 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
if (res == QMessageBox::Cancel)
return;
- FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
- "prod.keys_autogenerated");
- FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
- "console.keys_autogenerated");
- FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
- "title.keys_autogenerated");
+ Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) +
+ "prod.keys_autogenerated");
+ Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) +
+ "console.keys_autogenerated");
+ Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) +
+ "title.keys_autogenerated");
}
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
if (keys.BaseDeriveNecessary()) {
Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory(
- FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)};
+ Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), FileSys::Mode::Read)};
const auto function = [this, &keys, &pdm] {
keys.PopulateFromPartitionData(pdm);
@@ -2870,7 +2897,7 @@ int main(int argc, char* argv[]) {
// If you start a bundle (binary) on OSX without the Terminal, the working directory is "/".
// But since we require the working directory to be the executable path for the location of
// the user folder in the Qt Frontend, we need to cd into that working directory
- const std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + "..";
+ const std::string bin_path = Common::FS::GetBundleDirectory() + DIR_SEP + "..";
chdir(bin_path.c_str());
#endif
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 73a44a3bf..0ce66a1ca 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -40,12 +40,20 @@ namespace Core::Frontend {
struct SoftwareKeyboardParameters;
} // namespace Core::Frontend
+namespace DiscordRPC {
+class DiscordInterface;
+}
+
namespace FileSys {
class ContentProvider;
class ManualContentProvider;
class VfsFilesystem;
} // namespace FileSys
+namespace InputCommon {
+class InputSubsystem;
+}
+
enum class EmulatedDirectoryTarget {
NAND,
SDMC,
@@ -62,10 +70,6 @@ enum class ReinitializeKeyBehavior {
Warning,
};
-namespace DiscordRPC {
-class DiscordInterface;
-}
-
class GMainWindow : public QMainWindow {
Q_OBJECT
@@ -86,8 +90,6 @@ public:
GMainWindow();
~GMainWindow() override;
- std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
-
bool DropAction(QDropEvent* event);
void AcceptDropEvent(QDropEvent* event);
@@ -198,7 +200,8 @@ private slots:
void OnOpenFAQ();
/// Called whenever a user selects a game in the game list widget.
void OnGameListLoadFile(QString game_path);
- void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
+ void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
+ const std::string& game_path);
void OnTransferableShaderCacheOpenFile(u64 program_id);
void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type);
void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target);
@@ -216,6 +219,7 @@ private slots:
void OnMenuInstallToNAND();
void OnMenuRecentFile();
void OnConfigure();
+ void OnConfigurePerGame();
void OnLoadAmiibo();
void OnOpenYuzuFolder();
void OnAbout();
@@ -249,9 +253,13 @@ private:
void ShowMouseCursor();
void OpenURL(const QUrl& url);
void LoadTranslation();
+ void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
Ui::MainWindow ui;
+ std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
+ std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
+
GRenderWindow* render_window;
GameList* game_list;
LoadingScreen* loading_screen;
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 @@
<addaction name="action_Restart"/>
<addaction name="separator"/>
<addaction name="action_Configure"/>
+ <addaction name="action_Configure_Current_Game"/>
</widget>
<widget class="QMenu" name="menu_View">
<property name="title">
@@ -287,6 +288,14 @@
<string>Capture Screenshot</string>
</property>
</action>
+ <action name="action_Configure_Current_Game">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Configure Current Game..</string>
+ </property>
+ </action>
</widget>
<resources/>
<connections/>
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index a51175f36..37499fc85 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -16,4 +16,5 @@ const Themes themes{{
}};
Values values = {};
+
} // namespace UISettings
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index bbfeafc55..ce3945485 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -29,14 +29,14 @@ extern const Themes themes;
struct GameDir {
QString path;
- bool deep_scan;
- bool expanded;
+ bool deep_scan = false;
+ bool expanded = false;
bool operator==(const GameDir& rhs) const {
return path == rhs.path;
- };
+ }
bool operator!=(const GameDir& rhs) const {
return !operator==(rhs);
- };
+ }
};
struct Values {
@@ -87,9 +87,6 @@ struct Values {
// logging
bool show_console;
- // Controllers
- int profile_index;
-
// Game List
bool show_add_ons;
uint32_t icon_size;
@@ -100,6 +97,7 @@ struct Values {
};
extern Values values;
+
} // namespace UISettings
Q_DECLARE_METATYPE(UISettings::GameDir*);
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index c2a2982fb..e9f1c6500 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -16,9 +16,11 @@
#include "yuzu_cmd/config.h"
#include "yuzu_cmd/default_ini.h"
+namespace FS = Common::FS;
+
Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
- sdl2_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "sdl2-config.ini";
+ sdl2_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + "sdl2-config.ini";
sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
Reload();
@@ -31,8 +33,8 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
if (sdl2_config->ParseError() < 0) {
if (retry) {
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
- FileUtil::CreateFullPath(location);
- FileUtil::WriteStringToFile(true, location, default_contents);
+ FS::CreateFullPath(location);
+ FS::WriteStringToFile(true, location, default_contents);
sdl2_config = std::make_unique<INIReader>(location); // Reopen file
return LoadINI(default_contents, false);
@@ -286,6 +288,8 @@ void Config::ReadValues() {
Settings::values.debug_pad_analogs[i] = default_param;
}
+ Settings::values.vibration_enabled =
+ sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true);
Settings::values.touchscreen.enabled =
sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
Settings::values.touchscreen.device =
@@ -315,21 +319,21 @@ void Config::ReadValues() {
// Data Storage
Settings::values.use_virtual_sd =
sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
- FileUtil::GetUserPath(FileUtil::UserPath::NANDDir,
- sdl2_config->Get("Data Storage", "nand_directory",
- FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
- FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
- sdl2_config->Get("Data Storage", "sdmc_directory",
- FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
- FileUtil::GetUserPath(FileUtil::UserPath::LoadDir,
- sdl2_config->Get("Data Storage", "load_directory",
- FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
- FileUtil::GetUserPath(FileUtil::UserPath::DumpDir,
- sdl2_config->Get("Data Storage", "dump_directory",
- FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
- FileUtil::GetUserPath(FileUtil::UserPath::CacheDir,
- sdl2_config->Get("Data Storage", "cache_directory",
- FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
+ FS::GetUserPath(
+ FS::UserPath::NANDDir,
+ sdl2_config->Get("Data Storage", "nand_directory", FS::GetUserPath(FS::UserPath::NANDDir)));
+ FS::GetUserPath(
+ FS::UserPath::SDMCDir,
+ sdl2_config->Get("Data Storage", "sdmc_directory", FS::GetUserPath(FS::UserPath::SDMCDir)));
+ FS::GetUserPath(
+ FS::UserPath::LoadDir,
+ sdl2_config->Get("Data Storage", "load_directory", FS::GetUserPath(FS::UserPath::LoadDir)));
+ FS::GetUserPath(
+ FS::UserPath::DumpDir,
+ sdl2_config->Get("Data Storage", "dump_directory", FS::GetUserPath(FS::UserPath::DumpDir)));
+ FS::GetUserPath(FS::UserPath::CacheDir,
+ sdl2_config->Get("Data Storage", "cache_directory",
+ FS::GetUserPath(FS::UserPath::CacheDir)));
Settings::values.gamecard_inserted =
sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false);
Settings::values.gamecard_current_game =
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index e5e684206..a804d5185 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -13,23 +13,25 @@
#include "input_common/sdl/sdl.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
-EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen) : system{system} {
+EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen,
+ InputCommon::InputSubsystem* input_subsystem_)
+ : system{system}, input_subsystem{input_subsystem_} {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
exit(1);
}
- InputCommon::Init();
+ input_subsystem->Initialize();
SDL_SetMainReady();
}
EmuWindow_SDL2::~EmuWindow_SDL2() {
- InputCommon::Shutdown();
+ input_subsystem->Shutdown();
SDL_Quit();
}
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
- InputCommon::GetMotionEmu()->Tilt(x, y);
+ input_subsystem->GetMotionEmu()->Tilt(x, y);
}
void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
@@ -41,9 +43,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
}
} else if (button == SDL_BUTTON_RIGHT) {
if (state == SDL_PRESSED) {
- InputCommon::GetMotionEmu()->BeginTilt(x, y);
+ input_subsystem->GetMotionEmu()->BeginTilt(x, y);
} else {
- InputCommon::GetMotionEmu()->EndTilt();
+ input_subsystem->GetMotionEmu()->EndTilt();
}
}
}
@@ -79,9 +81,9 @@ void EmuWindow_SDL2::OnFingerUp() {
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
if (state == SDL_PRESSED) {
- InputCommon::GetKeyboard()->PressKey(key);
+ input_subsystem->GetKeyboard()->PressKey(key);
} else if (state == SDL_RELEASED) {
- InputCommon::GetKeyboard()->ReleaseKey(key);
+ input_subsystem->GetKeyboard()->ReleaseKey(key);
}
}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index fffac4252..82750ffec 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -14,9 +14,14 @@ namespace Core {
class System;
}
+namespace InputCommon {
+class InputSubsystem;
+}
+
class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
public:
- explicit EmuWindow_SDL2(Core::System& system, bool fullscreen);
+ explicit EmuWindow_SDL2(Core::System& system, bool fullscreen,
+ InputCommon::InputSubsystem* input_subsystem);
~EmuWindow_SDL2();
/// Polls window events
@@ -76,4 +81,7 @@ protected:
/// Keeps track of how often to update the title bar during gameplay
u32 last_time = 0;
+
+ /// Input subsystem to use with this window.
+ InputCommon::InputSubsystem* input_subsystem;
};
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index e78025737..881b67a76 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -87,8 +87,9 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
return unsupported_ext.empty();
}
-EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen)
- : EmuWindow_SDL2{system, fullscreen} {
+EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen,
+ InputCommon::InputSubsystem* input_subsystem)
+ : EmuWindow_SDL2{system, fullscreen, input_subsystem} {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
index 48bb41683..732a64edd 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
@@ -8,9 +8,14 @@
#include "core/frontend/emu_window.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
+namespace InputCommon {
+class InputSubsystem;
+}
+
class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
public:
- explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen);
+ explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen,
+ InputCommon::InputSubsystem* input_subsystem);
~EmuWindow_SDL2_GL();
void Present() override;
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index cb8e68a39..53491f86e 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -19,8 +19,9 @@
#include <SDL.h>
#include <SDL_syswm.h>
-EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen)
- : EmuWindow_SDL2{system, fullscreen} {
+EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen,
+ InputCommon::InputSubsystem* input_subsystem)
+ : EmuWindow_SDL2{system, fullscreen, input_subsystem} {
const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
Common::g_scm_branch, Common::g_scm_desc);
render_window =
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
index 77a6ca72b..f99704d4c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -13,9 +13,14 @@ namespace Core {
class System;
}
+namespace InputCommon {
+class InputSubsystem;
+}
+
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
public:
- explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen);
+ explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen,
+ InputCommon::InputSubsystem* input_subsystem);
~EmuWindow_SDL2_VK();
void Present() override;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 512b060a7..4f00c804d 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -23,12 +23,14 @@
#include "common/telemetry.h"
#include "core/core.h"
#include "core/crypto/key_manager.h"
+#include "core/file_sys/registered_cache.h"
#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
+#include "input_common/main.h"
#include "video_core/renderer_base.h"
#include "yuzu_cmd/config.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
@@ -37,8 +39,6 @@
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
#endif
-#include "core/file_sys/registered_cache.h"
-
#ifdef _WIN32
// windows.h needs to be included before shellapi.h
#include <windows.h>
@@ -82,8 +82,8 @@ static void InitializeLogging() {
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
- const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
- FileUtil::CreateFullPath(log_dir);
+ const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
+ Common::FS::CreateFullPath(log_dir);
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
#ifdef _WIN32
Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
@@ -179,15 +179,16 @@ int main(int argc, char** argv) {
Settings::Apply();
Core::System& system{Core::System::GetInstance()};
+ InputCommon::InputSubsystem input_subsystem;
std::unique_ptr<EmuWindow_SDL2> emu_window;
switch (Settings::values.renderer_backend.GetValue()) {
case Settings::RendererBackend::OpenGL:
- emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen);
+ emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen, &input_subsystem);
break;
case Settings::RendererBackend::Vulkan:
#ifdef HAS_VULKAN
- emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen);
+ emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen, &input_subsystem);
break;
#else
LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!");
@@ -229,7 +230,7 @@ int main(int argc, char** argv) {
}
}
- system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
+ system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL");
// Core is loaded, start the GPU (makes the GPU contexts current to this thread)
system.GPU().Start();
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
index acb22885e..aaf59129a 100644
--- a/src/yuzu_tester/config.cpp
+++ b/src/yuzu_tester/config.cpp
@@ -15,10 +15,11 @@
#include "yuzu_tester/config.h"
#include "yuzu_tester/default_ini.h"
+namespace FS = Common::FS;
+
Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
- sdl2_config_loc =
- FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "sdl2-tester-config.ini";
+ sdl2_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + "sdl2-tester-config.ini";
sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
Reload();
@@ -31,8 +32,8 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
if (sdl2_config->ParseError() < 0) {
if (retry) {
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
- FileUtil::CreateFullPath(location);
- FileUtil::WriteStringToFile(true, default_contents, location);
+ FS::CreateFullPath(location);
+ FS::WriteStringToFile(true, default_contents, location);
sdl2_config = std::make_unique<INIReader>(location); // Reopen file
return LoadINI(default_contents, false);
@@ -74,6 +75,7 @@ void Config::ReadValues() {
Settings::values.debug_pad_analogs[i] = "";
}
+ Settings::values.vibration_enabled = true;
Settings::values.touchscreen.enabled = "";
Settings::values.touchscreen.device = "";
Settings::values.touchscreen.finger = 0;
@@ -87,12 +89,12 @@ void Config::ReadValues() {
// Data Storage
Settings::values.use_virtual_sd =
sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
- FileUtil::GetUserPath(FileUtil::UserPath::NANDDir,
- sdl2_config->Get("Data Storage", "nand_directory",
- FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
- FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
- sdl2_config->Get("Data Storage", "sdmc_directory",
- FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
+ FS::GetUserPath(Common::FS::UserPath::NANDDir,
+ sdl2_config->Get("Data Storage", "nand_directory",
+ Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)));
+ FS::GetUserPath(Common::FS::UserPath::SDMCDir,
+ sdl2_config->Get("Data Storage", "sdmc_directory",
+ Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)));
// System
Settings::values.current_user = std::clamp<int>(
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
index 8584f6671..78f75fb38 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
@@ -13,7 +13,6 @@
#include <glad/glad.h>
-#include "common/assert.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "core/settings.h"
@@ -53,7 +52,7 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
exit(1);
}
- InputCommon::Init();
+ input_subsystem->Initialize();
SDL_SetMainReady();
@@ -105,7 +104,7 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
}
EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
- InputCommon::Shutdown();
+ input_subsystem->Shutdown();
SDL_GL_DeleteContext(gl_context);
SDL_Quit();
}
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
index c13a82df2..a553b4b95 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
@@ -8,6 +8,10 @@
struct SDL_Window;
+namespace InputCommon {
+class InputSubsystem;
+}
+
class EmuWindow_SDL2_Hide : public Core::Frontend::EmuWindow {
public:
explicit EmuWindow_SDL2_Hide();
@@ -25,6 +29,8 @@ private:
/// Whether the GPU and driver supports the OpenGL extension required
bool SupportsRequiredGLExtensions();
+ std::unique_ptr<InputCommon::InputSubsystem> input_subsystem;
+
/// Internal SDL2 render window
SDL_Window* render_window;
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index 083667baf..7acf0caad 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -79,8 +79,8 @@ static void InitializeLogging(bool console) {
if (console)
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
- const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
- FileUtil::CreateFullPath(log_dir);
+ const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
+ Common::FS::CreateFullPath(log_dir);
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
#ifdef _WIN32
Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
@@ -251,7 +251,8 @@ int main(int argc, char** argv) {
Service::Yuzu::InstallInterfaces(system.ServiceManager(), datastring, callback);
- system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester");
+ system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend",
+ "SDLHideTester");
system.GPU().Start();
system.Renderer().Rasterizer().LoadDiskResources();