summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/scripts/format/script.sh2
-rwxr-xr-x.travis/clang-format/script.sh2
-rw-r--r--CMakeLists.txt4
-rw-r--r--dist/icons/controller/controller.qrc25
-rw-r--r--dist/icons/controller/dual_joycon.pngbin0 -> 36466 bytes
-rw-r--r--dist/icons/controller/dual_joycon_dark.pngbin0 -> 36261 bytes
-rw-r--r--dist/icons/controller/dual_joycon_midnight.pngbin0 -> 34667 bytes
-rw-r--r--dist/icons/controller/handheld.pngbin0 -> 14108 bytes
-rw-r--r--dist/icons/controller/handheld_dark.pngbin0 -> 13731 bytes
-rw-r--r--dist/icons/controller/handheld_midnight.pngbin0 -> 13366 bytes
-rw-r--r--dist/icons/controller/pro_controller.pngbin0 -> 36710 bytes
-rw-r--r--dist/icons/controller/pro_controller_dark.pngbin0 -> 34897 bytes
-rw-r--r--dist/icons/controller/pro_controller_midnight.pngbin0 -> 35893 bytes
-rw-r--r--dist/icons/controller/single_joycon_left.pngbin0 -> 25565 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_dark.pngbin0 -> 25682 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_midnight.pngbin0 -> 24405 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_vertical.pngbin0 -> 24764 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_vertical_dark.pngbin0 -> 24938 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_vertical_midnight.pngbin0 -> 23681 bytes
-rw-r--r--dist/icons/controller/single_joycon_right.pngbin0 -> 28320 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_dark.pngbin0 -> 28157 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_midnight.pngbin0 -> 27006 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_vertical.pngbin0 -> 27655 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_vertical_dark.pngbin0 -> 27729 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_vertical_midnight.pngbin0 -> 26354 bytes
-rw-r--r--dist/license.md3
-rw-r--r--dist/qt_themes/colorful_dark/icons/16x16/refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/colorful_dark/icons/16x16/view-refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/colorful_dark/style.qrc1
-rw-r--r--dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/colorful_midnight_blue/style.qrc1
-rw-r--r--dist/qt_themes/default/default.qrc1
-rw-r--r--dist/qt_themes/default/icons/16x16/refresh.pngbin0 -> 349 bytes
-rw-r--r--dist/qt_themes/default/icons/16x16/view-refresh.pngbin0 -> 349 bytes
-rw-r--r--dist/qt_themes/default/style.qss62
-rw-r--r--dist/qt_themes/qdarkstyle/icons/16x16/refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/16x16/view-refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/style.qrc1
-rw-r--r--dist/qt_themes/qdarkstyle/style.qss105
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/style.qrc1
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/style.qss248
m---------externals/dynarmic0
-rw-r--r--externals/microprofile/microprofile.h57
-rw-r--r--externals/microprofile/microprofileui.h212
m---------externals/xbyak0
-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
304 files changed, 11782 insertions, 5588 deletions
diff --git a/.ci/scripts/format/script.sh b/.ci/scripts/format/script.sh
index 5ab828d5e..969ab637c 100644
--- a/.ci/scripts/format/script.sh
+++ b/.ci/scripts/format/script.sh
@@ -7,7 +7,7 @@ if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dis
7fi 7fi
8 8
9# Default clang-format points to default 3.5 version one 9# Default clang-format points to default 3.5 version one
10CLANG_FORMAT=clang-format-6.0 10CLANG_FORMAT=clang-format-10
11$CLANG_FORMAT --version 11$CLANG_FORMAT --version
12 12
13if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then 13if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then
diff --git a/.travis/clang-format/script.sh b/.travis/clang-format/script.sh
index 3eff6322f..56a785fe0 100755
--- a/.travis/clang-format/script.sh
+++ b/.travis/clang-format/script.sh
@@ -7,7 +7,7 @@ if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis*
7fi 7fi
8 8
9# Default clang-format points to default 3.5 version one 9# Default clang-format points to default 3.5 version one
10CLANG_FORMAT=clang-format-6.0 10CLANG_FORMAT=clang-format-10.0
11$CLANG_FORMAT --version 11$CLANG_FORMAT --version
12 12
13if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then 13if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3282ae9d4..2f3c59c5d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -161,7 +161,7 @@ macro(yuzu_find_packages)
161 # Cmake Pkg Prefix Version Conan Pkg 161 # Cmake Pkg Prefix Version Conan Pkg
162 "Boost 1.73 boost/1.73.0" 162 "Boost 1.73 boost/1.73.0"
163 "Catch2 2.13 catch2/2.13.0" 163 "Catch2 2.13 catch2/2.13.0"
164 "fmt 7.0 fmt/7.0.1" 164 "fmt 7.0 fmt/7.0.3"
165 # can't use until https://github.com/bincrafters/community/issues/1173 165 # can't use until https://github.com/bincrafters/community/issues/1173
166 #"libzip 1.5 libzip/1.5.2@bincrafters/stable" 166 #"libzip 1.5 libzip/1.5.2@bincrafters/stable"
167 "lz4 1.8 lz4/1.9.2" 167 "lz4 1.8 lz4/1.9.2"
@@ -456,7 +456,7 @@ endif()
456# against all the src files. This should be used before making a pull request. 456# against all the src files. This should be used before making a pull request.
457# ======================================================================= 457# =======================================================================
458 458
459set(CLANG_FORMAT_POSTFIX "-6.0") 459set(CLANG_FORMAT_POSTFIX "-10")
460find_program(CLANG_FORMAT 460find_program(CLANG_FORMAT
461 NAMES clang-format${CLANG_FORMAT_POSTFIX} 461 NAMES clang-format${CLANG_FORMAT_POSTFIX}
462 clang-format 462 clang-format
diff --git a/dist/icons/controller/controller.qrc b/dist/icons/controller/controller.qrc
new file mode 100644
index 000000000..f44725d8f
--- /dev/null
+++ b/dist/icons/controller/controller.qrc
@@ -0,0 +1,25 @@
1<RCC>
2 <qresource prefix="controller">
3 <file alias="dual_joycon">dual_joycon.png</file>
4 <file alias="dual_joycon_dark">dual_joycon_dark.png</file>
5 <file alias="dual_joycon_midnight">dual_joycon_midnight.png</file>
6 <file alias="handheld">handheld.png</file>
7 <file alias="handheld_dark">handheld_dark.png</file>
8 <file alias="handheld_midnight">handheld_midnight.png</file>
9 <file alias="pro_controller">pro_controller.png</file>
10 <file alias="pro_controller_dark">pro_controller_dark.png</file>
11 <file alias="pro_controller_midnight">pro_controller_midnight.png</file>
12 <file alias="single_joycon_left">single_joycon_left.png</file>
13 <file alias="single_joycon_left_dark">single_joycon_left_dark.png</file>
14 <file alias="single_joycon_left_midnight">single_joycon_left_midnight.png</file>
15 <file alias="single_joycon_right">single_joycon_right.png</file>
16 <file alias="single_joycon_right_dark">single_joycon_right_dark.png</file>
17 <file alias="single_joycon_right_midnight">single_joycon_right_midnight.png</file>
18 <file alias="single_joycon_left_vertical">single_joycon_left_vertical.png</file>
19 <file alias="single_joycon_left_vertical_dark">single_joycon_left_vertical_dark.png</file>
20 <file alias="single_joycon_left_vertical_midnight">single_joycon_left_vertical_midnight.png</file>
21 <file alias="single_joycon_right_vertical">single_joycon_right_vertical.png</file>
22 <file alias="single_joycon_right_vertical_dark">single_joycon_right_vertical_dark.png</file>
23 <file alias="single_joycon_right_vertical_midnight">single_joycon_right_vertical_midnight.png</file>
24 </qresource>
25</RCC>
diff --git a/dist/icons/controller/dual_joycon.png b/dist/icons/controller/dual_joycon.png
new file mode 100644
index 000000000..4230f5f7b
--- /dev/null
+++ b/dist/icons/controller/dual_joycon.png
Binary files differ
diff --git a/dist/icons/controller/dual_joycon_dark.png b/dist/icons/controller/dual_joycon_dark.png
new file mode 100644
index 000000000..4445db489
--- /dev/null
+++ b/dist/icons/controller/dual_joycon_dark.png
Binary files differ
diff --git a/dist/icons/controller/dual_joycon_midnight.png b/dist/icons/controller/dual_joycon_midnight.png
new file mode 100644
index 000000000..aac8e5321
--- /dev/null
+++ b/dist/icons/controller/dual_joycon_midnight.png
Binary files differ
diff --git a/dist/icons/controller/handheld.png b/dist/icons/controller/handheld.png
new file mode 100644
index 000000000..d009b4a47
--- /dev/null
+++ b/dist/icons/controller/handheld.png
Binary files differ
diff --git a/dist/icons/controller/handheld_dark.png b/dist/icons/controller/handheld_dark.png
new file mode 100644
index 000000000..c80ca9259
--- /dev/null
+++ b/dist/icons/controller/handheld_dark.png
Binary files differ
diff --git a/dist/icons/controller/handheld_midnight.png b/dist/icons/controller/handheld_midnight.png
new file mode 100644
index 000000000..19de4629b
--- /dev/null
+++ b/dist/icons/controller/handheld_midnight.png
Binary files differ
diff --git a/dist/icons/controller/pro_controller.png b/dist/icons/controller/pro_controller.png
new file mode 100644
index 000000000..07d65e94a
--- /dev/null
+++ b/dist/icons/controller/pro_controller.png
Binary files differ
diff --git a/dist/icons/controller/pro_controller_dark.png b/dist/icons/controller/pro_controller_dark.png
new file mode 100644
index 000000000..73efe18f4
--- /dev/null
+++ b/dist/icons/controller/pro_controller_dark.png
Binary files differ
diff --git a/dist/icons/controller/pro_controller_midnight.png b/dist/icons/controller/pro_controller_midnight.png
new file mode 100644
index 000000000..8d7e63f0d
--- /dev/null
+++ b/dist/icons/controller/pro_controller_midnight.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left.png b/dist/icons/controller/single_joycon_left.png
new file mode 100644
index 000000000..547153034
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_dark.png b/dist/icons/controller/single_joycon_left_dark.png
new file mode 100644
index 000000000..b6ee073cb
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_dark.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_midnight.png b/dist/icons/controller/single_joycon_left_midnight.png
new file mode 100644
index 000000000..34a485c81
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_midnight.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_vertical.png b/dist/icons/controller/single_joycon_left_vertical.png
new file mode 100644
index 000000000..1e6282ad8
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_vertical.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_vertical_dark.png b/dist/icons/controller/single_joycon_left_vertical_dark.png
new file mode 100644
index 000000000..a615d995d
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_vertical_dark.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_vertical_midnight.png b/dist/icons/controller/single_joycon_left_vertical_midnight.png
new file mode 100644
index 000000000..4cc578216
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_vertical_midnight.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right.png b/dist/icons/controller/single_joycon_right.png
new file mode 100644
index 000000000..8d29173f6
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_dark.png b/dist/icons/controller/single_joycon_right_dark.png
new file mode 100644
index 000000000..ead2c44e0
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_dark.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_midnight.png b/dist/icons/controller/single_joycon_right_midnight.png
new file mode 100644
index 000000000..89afe022d
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_midnight.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_vertical.png b/dist/icons/controller/single_joycon_right_vertical.png
new file mode 100644
index 000000000..4d7d06547
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_vertical.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_vertical_dark.png b/dist/icons/controller/single_joycon_right_vertical_dark.png
new file mode 100644
index 000000000..9a6eb3013
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_vertical_dark.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_vertical_midnight.png b/dist/icons/controller/single_joycon_right_vertical_midnight.png
new file mode 100644
index 000000000..685249b68
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_vertical_midnight.png
Binary files differ
diff --git a/dist/license.md b/dist/license.md
index f1ff35c95..e9bc87656 100644
--- a/dist/license.md
+++ b/dist/license.md
@@ -5,6 +5,7 @@ Icon Name | License | Origin/Author
5qt_themes/default/icons/16x16/checked.png | CC BY-ND 3.0 | https://icons8.com 5qt_themes/default/icons/16x16/checked.png | CC BY-ND 3.0 | https://icons8.com
6qt_themes/default/icons/16x16/failed.png | CC BY-ND 3.0 | https://icons8.com 6qt_themes/default/icons/16x16/failed.png | CC BY-ND 3.0 | https://icons8.com
7qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com 7qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
8qt_themes/default/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
8qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com 9qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
9qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com 10qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
10qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com 11qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
@@ -12,6 +13,7 @@ qt_themes/default/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
12qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team 13qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
13qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com 14qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
14qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com 15qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
16qt_themes/qdarkstyle/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
15qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com 17qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
16qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com 18qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
17qt_themes/qdarkstyle/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com 19qt_themes/qdarkstyle/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
@@ -19,6 +21,7 @@ qt_themes/qdarkstyle/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
19qt_themes/qdarkstyle/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team 21qt_themes/qdarkstyle/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
20qt_themes/qdarkstyle/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com 22qt_themes/qdarkstyle/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
21qt_themes/colorful/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com 23qt_themes/colorful/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
24qt_themes/colorful/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
22qt_themes/colorful/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com 25qt_themes/colorful/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
23qt_themes/colorful/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com 26qt_themes/colorful/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
24qt_themes/colorful/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com 27qt_themes/colorful/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
diff --git a/dist/qt_themes/colorful_dark/icons/16x16/refresh.png b/dist/qt_themes/colorful_dark/icons/16x16/refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/colorful_dark/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/colorful_dark/icons/16x16/view-refresh.png b/dist/qt_themes/colorful_dark/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/colorful_dark/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/colorful_dark/style.qrc b/dist/qt_themes/colorful_dark/style.qrc
index 27a6cc87d..0abcb4e83 100644
--- a/dist/qt_themes/colorful_dark/style.qrc
+++ b/dist/qt_themes/colorful_dark/style.qrc
@@ -2,6 +2,7 @@
2 <qresource prefix="icons/colorful_dark"> 2 <qresource prefix="icons/colorful_dark">
3 <file alias="index.theme">icons/index.theme</file> 3 <file alias="index.theme">icons/index.theme</file>
4 <file alias="16x16/lock.png">icons/16x16/lock.png</file> 4 <file alias="16x16/lock.png">icons/16x16/lock.png</file>
5 <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
5 <file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file> 6 <file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
6 <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file> 7 <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
7 <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file> 8 <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
diff --git a/dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.png b/dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.png b/dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/colorful_midnight_blue/style.qrc b/dist/qt_themes/colorful_midnight_blue/style.qrc
index fd33bc850..bf367099a 100644
--- a/dist/qt_themes/colorful_midnight_blue/style.qrc
+++ b/dist/qt_themes/colorful_midnight_blue/style.qrc
@@ -2,6 +2,7 @@
2 <qresource prefix="icons/colorful_midnight_blue"> 2 <qresource prefix="icons/colorful_midnight_blue">
3 <file alias="index.theme">icons/index.theme</file> 3 <file alias="index.theme">icons/index.theme</file>
4 <file alias="16x16/lock.png">icons/16x16/lock.png</file> 4 <file alias="16x16/lock.png">icons/16x16/lock.png</file>
5 <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
5 <file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file> 6 <file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
6 <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file> 7 <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
7 <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file> 8 <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
diff --git a/dist/qt_themes/default/default.qrc b/dist/qt_themes/default/default.qrc
index c51fdb26c..2182f33f3 100644
--- a/dist/qt_themes/default/default.qrc
+++ b/dist/qt_themes/default/default.qrc
@@ -4,6 +4,7 @@
4 <file alias="16x16/checked.png">icons/16x16/checked.png</file> 4 <file alias="16x16/checked.png">icons/16x16/checked.png</file>
5 <file alias="16x16/failed.png">icons/16x16/failed.png</file> 5 <file alias="16x16/failed.png">icons/16x16/failed.png</file>
6 <file alias="16x16/lock.png">icons/16x16/lock.png</file> 6 <file alias="16x16/lock.png">icons/16x16/lock.png</file>
7 <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
7 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> 8 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
8 <file alias="48x48/chip.png">icons/48x48/chip.png</file> 9 <file alias="48x48/chip.png">icons/48x48/chip.png</file>
9 <file alias="48x48/folder.png">icons/48x48/folder.png</file> 10 <file alias="48x48/folder.png">icons/48x48/folder.png</file>
diff --git a/dist/qt_themes/default/icons/16x16/refresh.png b/dist/qt_themes/default/icons/16x16/refresh.png
new file mode 100644
index 000000000..69f9474ac
--- /dev/null
+++ b/dist/qt_themes/default/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/16x16/view-refresh.png b/dist/qt_themes/default/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..69f9474ac
--- /dev/null
+++ b/dist/qt_themes/default/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss
index 6b5953e38..5da56520b 100644
--- a/dist/qt_themes/default/style.qss
+++ b/dist/qt_themes/default/style.qss
@@ -30,6 +30,66 @@ QPushButton#RendererStatusBarButton:checked {
30 color: #e85c00; 30 color: #e85c00;
31} 31}
32 32
33QPushButton#RendererStatusBarButton:!checked{ 33QPushButton#RendererStatusBarButton:!checked {
34 color: #0066ff; 34 color: #0066ff;
35} 35}
36
37QPushButton#buttonRefreshDevices {
38 min-width: 20px;
39 min-height: 20px;
40 max-width: 20px;
41 max-height: 20px;
42}
43
44QCheckBox#checkboxPlayer1Connected,
45QCheckBox#checkboxPlayer2Connected,
46QCheckBox#checkboxPlayer3Connected,
47QCheckBox#checkboxPlayer4Connected,
48QCheckBox#checkboxPlayer5Connected,
49QCheckBox#checkboxPlayer6Connected,
50QCheckBox#checkboxPlayer7Connected,
51QCheckBox#checkboxPlayer8Connected {
52 spacing: 0px;
53}
54
55QCheckBox#checkboxPlayer1Connected::indicator,
56QCheckBox#checkboxPlayer2Connected::indicator,
57QCheckBox#checkboxPlayer3Connected::indicator,
58QCheckBox#checkboxPlayer4Connected::indicator,
59QCheckBox#checkboxPlayer5Connected::indicator,
60QCheckBox#checkboxPlayer6Connected::indicator,
61QCheckBox#checkboxPlayer7Connected::indicator,
62QCheckBox#checkboxPlayer8Connected::indicator {
63 width: 14px;
64 height: 14px;
65}
66
67QCheckBox#checkboxPlayer1Connected::indicator:checked,
68QCheckBox#checkboxPlayer2Connected::indicator:checked,
69QCheckBox#checkboxPlayer3Connected::indicator:checked,
70QCheckBox#checkboxPlayer4Connected::indicator:checked,
71QCheckBox#checkboxPlayer5Connected::indicator:checked,
72QCheckBox#checkboxPlayer6Connected::indicator:checked,
73QCheckBox#checkboxPlayer7Connected::indicator:checked,
74QCheckBox#checkboxPlayer8Connected::indicator:checked,
75QGroupBox#groupConnectedController::indicator:checked {
76 border-radius: 2px;
77 border: 1px solid black;
78 background: #39ff14;
79 image: none;
80}
81
82QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
83QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
84QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
85QCheckBox#checkboxPlayer4Connected::indicator:unchecked,
86QCheckBox#checkboxPlayer5Connected::indicator:unchecked,
87QCheckBox#checkboxPlayer6Connected::indicator:unchecked,
88QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
89QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
90QGroupBox#groupConnectedController::indicator:unchecked {
91 border-radius: 2px;
92 border: 1px solid black;
93 background: transparent;
94 image: none;
95}
diff --git a/dist/qt_themes/qdarkstyle/icons/16x16/refresh.png b/dist/qt_themes/qdarkstyle/icons/16x16/refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/16x16/view-refresh.png b/dist/qt_themes/qdarkstyle/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/style.qrc b/dist/qt_themes/qdarkstyle/style.qrc
index c2c14c28a..ec07ba160 100644
--- a/dist/qt_themes/qdarkstyle/style.qrc
+++ b/dist/qt_themes/qdarkstyle/style.qrc
@@ -2,6 +2,7 @@
2 <qresource prefix="icons/qdarkstyle"> 2 <qresource prefix="icons/qdarkstyle">
3 <file alias="index.theme">icons/index.theme</file> 3 <file alias="index.theme">icons/index.theme</file>
4 <file alias="16x16/lock.png">icons/16x16/lock.png</file> 4 <file alias="16x16/lock.png">icons/16x16/lock.png</file>
5 <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
5 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> 6 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
6 <file alias="48x48/chip.png">icons/48x48/chip.png</file> 7 <file alias="48x48/chip.png">icons/48x48/chip.png</file>
7 <file alias="48x48/folder.png">icons/48x48/folder.png</file> 8 <file alias="48x48/folder.png">icons/48x48/folder.png</file>
diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss
index 2926a05fa..7755426f8 100644
--- a/dist/qt_themes/qdarkstyle/style.qss
+++ b/dist/qt_themes/qdarkstyle/style.qss
@@ -40,8 +40,8 @@ QCheckBox:disabled {
40 40
41QCheckBox::indicator, 41QCheckBox::indicator,
42QGroupBox::indicator { 42QGroupBox::indicator {
43 width: 18px; 43 width: 16px;
44 height: 18px; 44 height: 16px;
45} 45}
46 46
47QGroupBox::indicator { 47QGroupBox::indicator {
@@ -1237,6 +1237,7 @@ QPlainTextEdit:disabled {
1237 background-color: #2b2e31; 1237 background-color: #2b2e31;
1238} 1238}
1239 1239
1240
1240QPushButton#TogglableStatusBarButton { 1241QPushButton#TogglableStatusBarButton {
1241 min-width: 0px; 1242 min-width: 0px;
1242 color: #656565; 1243 color: #656565;
@@ -1271,6 +1272,102 @@ QPushButton#RendererStatusBarButton:checked {
1271 color: #e85c00; 1272 color: #e85c00;
1272} 1273}
1273 1274
1274QPushButton#RendererStatusBarButton:!checked{ 1275QPushButton#RendererStatusBarButton:!checked {
1275 color: #00ccdd; 1276 color: #00ccdd;
1277}
1278
1279QPushButton#buttonRefreshDevices {
1280 min-width: 24px;
1281 min-height: 24px;
1282 max-width: 24px;
1283 max-height: 24px;
1284 padding: 0px 0px;
1285}
1286
1287QCheckBox#checkboxPlayer1Connected,
1288QCheckBox#checkboxPlayer2Connected,
1289QCheckBox#checkboxPlayer3Connected,
1290QCheckBox#checkboxPlayer4Connected,
1291QCheckBox#checkboxPlayer5Connected,
1292QCheckBox#checkboxPlayer6Connected,
1293QCheckBox#checkboxPlayer7Connected,
1294QCheckBox#checkboxPlayer8Connected {
1295 spacing: 0px;
1296}
1297
1298QCheckBox#checkboxPlayer1Connected::indicator,
1299QCheckBox#checkboxPlayer2Connected::indicator,
1300QCheckBox#checkboxPlayer3Connected::indicator,
1301QCheckBox#checkboxPlayer4Connected::indicator,
1302QCheckBox#checkboxPlayer5Connected::indicator,
1303QCheckBox#checkboxPlayer6Connected::indicator,
1304QCheckBox#checkboxPlayer7Connected::indicator,
1305QCheckBox#checkboxPlayer8Connected::indicator {
1306 width: 14px;
1307 height: 14px;
1308}
1309
1310QCheckBox#checkboxPlayer1Connected::indicator:checked,
1311QCheckBox#checkboxPlayer2Connected::indicator:checked,
1312QCheckBox#checkboxPlayer3Connected::indicator:checked,
1313QCheckBox#checkboxPlayer4Connected::indicator:checked,
1314QCheckBox#checkboxPlayer5Connected::indicator:checked,
1315QCheckBox#checkboxPlayer6Connected::indicator:checked,
1316QCheckBox#checkboxPlayer7Connected::indicator:checked,
1317QCheckBox#checkboxPlayer8Connected::indicator:checked,
1318QGroupBox#groupConnectedController::indicator:checked {
1319 border-radius: 2px;
1320 border: 1px solid #929192;
1321 background: #39ff14;
1322 image: none;
1323}
1324
1325QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
1326QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
1327QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
1328QCheckBox#checkboxPlayer4Connected::indicator:unchecked,
1329QCheckBox#checkboxPlayer5Connected::indicator:unchecked,
1330QCheckBox#checkboxPlayer6Connected::indicator:unchecked,
1331QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
1332QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
1333QGroupBox#groupConnectedController::indicator:unchecked {
1334 border-radius: 2px;
1335 border: 1px solid #929192;
1336 background: transparent;
1337 image: none;
1338}
1339
1340QSpinBox#spinboxLStickRange,
1341QSpinBox#spinboxRStickRange {
1342 padding: 4px 0px 5px 0px;
1343 min-width: 63px;
1344}
1345
1346QSpinBox#vibrationSpin {
1347 padding: 4px 0px 5px 0px;
1348 min-width: 63px;
1349}
1350
1351QSpinBox#spinboxLStickRange:up-button,
1352QSpinBox#spinboxRStickRange:up-button,
1353QSpinBox#vibrationSpin:up-button {
1354 left: -2px;
1355}
1356
1357QSpinBox#spinboxLStickRange:down-button,
1358QSpinBox#spinboxRStickRange:down-button,
1359QSpinBox#vibrationSpin:down-button {
1360 right: -1px;
1361}
1362
1363QGroupBox#motionGroup::indicator,
1364QGroupBox#vibrationGroup::indicator {
1365 margin-left: 0px;
1366}
1367
1368QGroupBox#motionGroup::title,
1369QGroupBox#vibrationGroup::title {
1370 spacing: 2px;
1371 padding-left: 1px;
1372 padding-right: 1px;
1276} 1373}
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc b/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
index 1b7686f15..616aace73 100644
--- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
@@ -2,6 +2,7 @@
2 <qresource prefix="icons/qdarkstyle_midnight_blue"> 2 <qresource prefix="icons/qdarkstyle_midnight_blue">
3 <file alias="index.theme">icons/index.theme</file> 3 <file alias="index.theme">icons/index.theme</file>
4 <file alias="16x16/lock.png">icons/16x16/lock.png</file> 4 <file alias="16x16/lock.png">icons/16x16/lock.png</file>
5 <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
5 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> 6 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
6 <file alias="48x48/chip.png">icons/48x48/chip.png</file> 7 <file alias="48x48/chip.png">icons/48x48/chip.png</file>
7 <file alias="48x48/folder.png">icons/48x48/folder.png</file> 8 <file alias="48x48/folder.png">icons/48x48/folder.png</file>
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
index 9c24b0d07..a714e1475 100644
--- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
@@ -138,8 +138,6 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qstatusbar
138 138
139--------------------------------------------------------------------------- */ 139--------------------------------------------------------------------------- */
140QStatusBar { 140QStatusBar {
141 border: 1px solid #32414B;
142 /* Fixes Spyder #9120, #9121 */
143 background: #32414B; 141 background: #32414B;
144 /* Fixes #205, white vertical borders separating items */ 142 /* Fixes #205, white vertical borders separating items */
145} 143}
@@ -161,6 +159,7 @@ QStatusBar QToolTip {
161QStatusBar QLabel { 159QStatusBar QLabel {
162 /* Fixes Spyder #9120, #9121 */ 160 /* Fixes Spyder #9120, #9121 */
163 background: transparent; 161 background: transparent;
162 padding: 0px;
164} 163}
165 164
166/* QCheckBox -------------------------------------------------------------- 165/* QCheckBox --------------------------------------------------------------
@@ -236,21 +235,19 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qgroupbox
236 235
237--------------------------------------------------------------------------- */ 236--------------------------------------------------------------------------- */
238QGroupBox { 237QGroupBox {
239 font-weight: bold; 238 font-weight: bold;
240 border: 1px solid #32414B; 239 border: 1px solid #32414B;
241 border-radius: 4px; 240 border-radius: 4px;
242 padding: 4px; 241 margin-top: 12px;
243 margin-top: 16px; 242 padding: 4px;
244} 243}
245 244
246QGroupBox::title { 245QGroupBox::title {
247 subcontrol-origin: margin; 246 subcontrol-origin: margin;
248 subcontrol-position: top left; 247 subcontrol-position: top left;
249 left: 3px; 248 padding-left: 3px;
250 padding-left: 3px; 249 padding-right: 5px;
251 padding-right: 5px; 250 padding-top: 4px;
252 padding-top: 8px;
253 padding-bottom: 16px;
254} 251}
255 252
256QGroupBox::indicator { 253QGroupBox::indicator {
@@ -367,28 +364,19 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenubar
367--------------------------------------------------------------------------- */ 364--------------------------------------------------------------------------- */
368QMenuBar { 365QMenuBar {
369 background-color: #32414B; 366 background-color: #32414B;
370 padding: 2px;
371 border: 1px solid #19232D;
372 color: #F0F0F0; 367 color: #F0F0F0;
373} 368}
374 369
375QMenuBar:focus {
376 border: 1px solid #148CD2;
377}
378
379QMenuBar::item { 370QMenuBar::item {
380 background: transparent; 371 background: transparent;
381 padding: 4px;
382} 372}
383 373
384QMenuBar::item:selected { 374QMenuBar::item:selected {
385 padding: 4px;
386 background: transparent; 375 background: transparent;
387 border: 0px solid #32414B; 376 border: 0px solid #32414B;
388} 377}
389 378
390QMenuBar::item:pressed { 379QMenuBar::item:pressed {
391 padding: 4px;
392 border: 0px solid #32414B; 380 border: 0px solid #32414B;
393 background-color: #148CD2; 381 background-color: #148CD2;
394 color: #F0F0F0; 382 color: #F0F0F0;
@@ -396,6 +384,7 @@ QMenuBar::item:pressed {
396 padding-bottom: 0px; 384 padding-bottom: 0px;
397} 385}
398 386
387
399/* QMenu ------------------------------------------------------------------ 388/* QMenu ------------------------------------------------------------------
400 389
401https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenu 390https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenu
@@ -482,7 +471,7 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox
482 471
483--------------------------------------------------------------------------- */ 472--------------------------------------------------------------------------- */
484QAbstractItemView { 473QAbstractItemView {
485 alternate-background-color: #19232D; 474 alternate-background-color: #1f2933;
486 color: #F0F0F0; 475 color: #F0F0F0;
487 border: 1px solid #32414B; 476 border: 1px solid #32414B;
488 border-radius: 4px; 477 border-radius: 4px;
@@ -501,13 +490,13 @@ QAbstractScrollArea {
501 background-color: #19232D; 490 background-color: #19232D;
502 border: 1px solid #32414B; 491 border: 1px solid #32414B;
503 border-radius: 4px; 492 border-radius: 4px;
504 padding: 2px;
505 /* fix #159 */ 493 /* fix #159 */
506 min-height: 1.25em; 494 min-height: 1.25em;
507 /* fix #159 */ 495 /* fix #159 */
508 color: #F0F0F0; 496 color: #F0F0F0;
509} 497}
510 498
499
511QAbstractScrollArea:disabled { 500QAbstractScrollArea:disabled {
512 color: #787878; 501 color: #787878;
513} 502}
@@ -807,20 +796,22 @@ QAbstractSpinBox {
807} 796}
808 797
809QAbstractSpinBox:up-button { 798QAbstractSpinBox:up-button {
810 background-color: transparent #19232D; 799 background-color: #505F69;
811 subcontrol-origin: border; 800 subcontrol-origin: border;
812 subcontrol-position: top right; 801 subcontrol-position: top right;
813 border-left: 1px solid #32414B; 802 border-left: 1px solid #32414B;
814 border-bottom: 1px solid #32414B; 803 border-top: 1px solid #32414B;
804 border-right: 1px solid #32414B;
805 border-top-right-radius: 4px;
815 border-top-left-radius: 0; 806 border-top-left-radius: 0;
816 border-bottom-left-radius: 0; 807 border-bottom-left-radius: 0;
817 margin: 1px; 808 margin: 0px;
818 width: 12px; 809 width: 12px;
819 margin-bottom: -1px; 810 margin-bottom: 0px;
820} 811}
821 812
822QAbstractSpinBox::up-arrow, QAbstractSpinBox::up-arrow:disabled, QAbstractSpinBox::up-arrow:off { 813QAbstractSpinBox::up-arrow, QAbstractSpinBox::up-arrow:disabled, QAbstractSpinBox::up-arrow:off {
823 image: url(":/qss_icons/rc/arrow_up_disabled.png"); 814 image: url(":/qss_icons/rc/up_arrow.png");
824 height: 8px; 815 height: 8px;
825 width: 8px; 816 width: 8px;
826} 817}
@@ -830,20 +821,23 @@ QAbstractSpinBox::up-arrow:hover {
830} 821}
831 822
832QAbstractSpinBox:down-button { 823QAbstractSpinBox:down-button {
833 background-color: transparent #19232D; 824 background-color: #505F69;
834 subcontrol-origin: border; 825 subcontrol-origin: border;
835 subcontrol-position: bottom right; 826 subcontrol-position: bottom right;
836 border-left: 1px solid #32414B; 827 border-left: 1px solid #32414B;
828 border-right: 1px solid #32414B;
829 border-bottom: 1px solid #32414B;
837 border-top: 1px solid #32414B; 830 border-top: 1px solid #32414B;
838 border-top-left-radius: 0; 831 border-top-left-radius: 0;
839 border-bottom-left-radius: 0; 832 border-bottom-left-radius: 0;
840 margin: 1px; 833 border-bottom-right-radius: 4px;
834 margin: 0px;
841 width: 12px; 835 width: 12px;
842 margin-top: -1px; 836 margin-top: 0px;
843} 837}
844 838
845QAbstractSpinBox::down-arrow, QAbstractSpinBox::down-arrow:disabled, QAbstractSpinBox::down-arrow:off { 839QAbstractSpinBox::down-arrow, QAbstractSpinBox::down-arrow:disabled, QAbstractSpinBox::down-arrow:off {
846 image: url(":/qss_icons/rc/arrow_down_disabled.png"); 840 image: url(":/qss_icons/rc/down_arrow.png");
847 height: 8px; 841 height: 8px;
848 width: 8px; 842 width: 8px;
849} 843}
@@ -1199,6 +1193,7 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox
1199 1193
1200--------------------------------------------------------------------------- */ 1194--------------------------------------------------------------------------- */
1201QComboBox { 1195QComboBox {
1196 background-color: #0f1922;
1202 border: 1px solid #32414B; 1197 border: 1px solid #32414B;
1203 border-radius: 4px; 1198 border-radius: 4px;
1204 selection-background-color: #1464A0; 1199 selection-background-color: #1464A0;
@@ -1216,7 +1211,7 @@ QComboBox {
1216QComboBox QAbstractItemView { 1211QComboBox QAbstractItemView {
1217 border: 1px solid #32414B; 1212 border: 1px solid #32414B;
1218 border-radius: 0; 1213 border-radius: 0;
1219 background-color: #19232D; 1214 background-color: #0f1922;
1220 selection-background-color: #1464A0; 1215 selection-background-color: #1464A0;
1221} 1216}
1222 1217
@@ -1285,7 +1280,12 @@ QComboBox::drop-down {
1285} 1280}
1286 1281
1287QComboBox::down-arrow { 1282QComboBox::down-arrow {
1288 image: url(":/qss_icons/rc/arrow_down_disabled.png"); 1283 image: url(":/qss_icons/rc/down_arrow.png");
1284 background-color: #505F69;
1285 padding: 6px 2px;
1286 border: 1px solid #32414B;
1287 border-top-right-radius: 4px;
1288 border-bottom-right-radius: 4px;
1289 height: 8px; 1289 height: 8px;
1290 width: 8px; 1290 width: 8px;
1291} 1291}
@@ -1559,12 +1559,12 @@ QTabBar::tab:right:!selected {
1559QTabBar::tab:top { 1559QTabBar::tab:top {
1560 background-color: #32414B; 1560 background-color: #32414B;
1561 color: #F0F0F0; 1561 color: #F0F0F0;
1562 min-width: 36px;
1562 margin-left: 2px; 1563 margin-left: 2px;
1563 padding-left: 4px; 1564 padding-left: 8px;
1564 padding-right: 4px; 1565 padding-right: 8px;
1565 padding-top: 2px; 1566 padding-top: 2px;
1566 padding-bottom: 2px; 1567 padding-bottom: 2px;
1567 min-width: 5px;
1568 border-bottom: 3px solid #32414B; 1568 border-bottom: 3px solid #32414B;
1569 border-top-left-radius: 3px; 1569 border-top-left-radius: 3px;
1570 border-top-right-radius: 3px; 1570 border-top-right-radius: 3px;
@@ -1588,16 +1588,16 @@ QTabBar::tab:top:!selected:hover {
1588 1588
1589QTabBar::tab:bottom { 1589QTabBar::tab:bottom {
1590 color: #F0F0F0; 1590 color: #F0F0F0;
1591 min-width: 36px;
1591 border-top: 3px solid #32414B; 1592 border-top: 3px solid #32414B;
1592 background-color: #32414B; 1593 background-color: #32414B;
1593 margin-left: 2px; 1594 margin-left: 2px;
1594 padding-left: 4px; 1595 padding-left: 8px;
1595 padding-right: 4px; 1596 padding-right: 8px;
1596 padding-top: 2px; 1597 padding-top: 2px;
1597 padding-bottom: 2px; 1598 padding-bottom: 2px;
1598 border-bottom-left-radius: 3px; 1599 border-bottom-left-radius: 3px;
1599 border-bottom-right-radius: 3px; 1600 border-bottom-right-radius: 3px;
1600 min-width: 5px;
1601} 1601}
1602 1602
1603QTabBar::tab:bottom:selected { 1603QTabBar::tab:bottom:selected {
@@ -1752,21 +1752,6 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qlistview
1752https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtableview 1752https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtableview
1753 1753
1754--------------------------------------------------------------------------- */ 1754--------------------------------------------------------------------------- */
1755QTreeView:branch:selected, QTreeView:branch:hover {
1756 background: url(":/qss_icons/rc/transparent.png");
1757}
1758
1759QTreeView:branch:has-siblings:!adjoins-item {
1760 border-image: url(":/qss_icons/rc/branch_line.png") 0;
1761}
1762
1763QTreeView:branch:has-siblings:adjoins-item {
1764 border-image: url(":/qss_icons/rc/branch_more.png") 0;
1765}
1766
1767QTreeView:branch:!has-children:!has-siblings:adjoins-item {
1768 border-image: url(":/qss_icons/rc/branch_end.png") 0;
1769}
1770 1755
1771QTreeView:branch:has-children:!has-siblings:closed, QTreeView:branch:closed:has-children:has-siblings { 1756QTreeView:branch:has-children:!has-siblings:closed, QTreeView:branch:closed:has-children:has-siblings {
1772 border-image: none; 1757 border-image: none;
@@ -1900,21 +1885,21 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qheaderview
1900 1885
1901--------------------------------------------------------------------------- */ 1886--------------------------------------------------------------------------- */
1902QHeaderView { 1887QHeaderView {
1903 background-color: #32414B; 1888 background-color: #19232D;
1904 border: 0px transparent #32414B; 1889 border: 0px transparent #19232D;
1905 padding: 0px; 1890 padding: 0px;
1906 margin: 0px; 1891 margin: 0px;
1907 border-radius: 0px; 1892 border-radius: 0px;
1908} 1893}
1909 1894
1910QHeaderView:disabled { 1895QHeaderView:disabled {
1911 background-color: #32414B; 1896 background-color: #19232D;
1912 border: 1px transparent #32414B; 1897 border: 1px transparent #19232D;
1913 padding: 2px; 1898 padding: 2px;
1914} 1899}
1915 1900
1916QHeaderView::section { 1901QHeaderView::section {
1917 background-color: #32414B; 1902 background-color: #19232D;
1918 color: #F0F0F0; 1903 color: #F0F0F0;
1919 padding: 2px; 1904 padding: 2px;
1920 border-radius: 0px; 1905 border-radius: 0px;
@@ -1934,11 +1919,11 @@ QHeaderView::section:checked:disabled {
1934QHeaderView::section::horizontal { 1919QHeaderView::section::horizontal {
1935 padding-left: 4px; 1920 padding-left: 4px;
1936 padding-right: 4px; 1921 padding-right: 4px;
1937 border-left: 1px solid #19232D; 1922 border-left: 1px solid #32414B;
1938} 1923}
1939 1924
1940QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one { 1925QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one {
1941 border-left: 1px solid #32414B; 1926 border-left: 1px solid #19232D;
1942} 1927}
1943 1928
1944QHeaderView::section::horizontal:disabled { 1929QHeaderView::section::horizontal:disabled {
@@ -1948,7 +1933,7 @@ QHeaderView::section::horizontal:disabled {
1948QHeaderView::section::vertical { 1933QHeaderView::section::vertical {
1949 padding-left: 4px; 1934 padding-left: 4px;
1950 padding-right: 4px; 1935 padding-right: 4px;
1951 border-top: 1px solid #19232D; 1936 border-top: 1px solid #32414B;
1952} 1937}
1953 1938
1954QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one { 1939QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one {
@@ -1962,7 +1947,7 @@ QHeaderView::section::vertical:disabled {
1962QHeaderView::down-arrow { 1947QHeaderView::down-arrow {
1963 /* Those settings (border/width/height/background-color) solve bug */ 1948 /* Those settings (border/width/height/background-color) solve bug */
1964 /* transparent arrow background and size */ 1949 /* transparent arrow background and size */
1965 background-color: #32414B; 1950 background-color: #19232D;
1966 border: none; 1951 border: none;
1967 height: 12px; 1952 height: 12px;
1968 width: 12px; 1953 width: 12px;
@@ -1972,7 +1957,7 @@ QHeaderView::down-arrow {
1972} 1957}
1973 1958
1974QHeaderView::up-arrow { 1959QHeaderView::up-arrow {
1975 background-color: #32414B; 1960 background-color: #19232D;
1976 border: none; 1961 border: none;
1977 height: 12px; 1962 height: 12px;
1978 width: 12px; 1963 width: 12px;
@@ -2172,3 +2157,132 @@ PlotWidget {
2172 /* Fix cut labels in plots #134 */ 2157 /* Fix cut labels in plots #134 */
2173 padding: 0px; 2158 padding: 0px;
2174} 2159}
2160
2161
2162QPushButton#TogglableStatusBarButton {
2163 min-width: 0px;
2164 color: #656565;
2165 border: 1px solid transparent;
2166 background-color: transparent;
2167 padding: 0px 3px 0px 3px;
2168 text-align: center;
2169}
2170
2171QPushButton#TogglableStatusBarButton:checked {
2172 color: #ffffff;
2173}
2174
2175QPushButton#TogglableStatusBarButton:hover {
2176 border: 1px solid #76797C;
2177}
2178
2179QPushButton#RendererStatusBarButton {
2180 min-width: 0px;
2181 color: #656565;
2182 border: 1px solid transparent;
2183 background-color: transparent;
2184 padding: 0px 3px 0px 3px;
2185 text-align: center;
2186}
2187
2188QPushButton#RendererStatusBarButton:hover {
2189 border: 1px solid #76797C;
2190}
2191
2192QPushButton#RendererStatusBarButton:checked {
2193 color: #e85c00;
2194}
2195
2196QPushButton#RendererStatusBarButton:!checked {
2197 color: #00ccdd;
2198}
2199
2200QPushButton#buttonRefreshDevices {
2201 min-width: 20px;
2202 min-height: 20px;
2203 max-width: 20px;
2204 max-height: 20px;
2205 padding: 0px 0px;
2206}
2207
2208
2209QCheckBox#checkboxPlayer1Connected,
2210QCheckBox#checkboxPlayer2Connected,
2211QCheckBox#checkboxPlayer3Connected,
2212QCheckBox#checkboxPlayer4Connected,
2213QCheckBox#checkboxPlayer5Connected,
2214QCheckBox#checkboxPlayer6Connected,
2215QCheckBox#checkboxPlayer7Connected,
2216QCheckBox#checkboxPlayer8Connected {
2217 spacing: 0px;
2218}
2219
2220QCheckBox#checkboxPlayer1Connected::indicator,
2221QCheckBox#checkboxPlayer2Connected::indicator,
2222QCheckBox#checkboxPlayer3Connected::indicator,
2223QCheckBox#checkboxPlayer4Connected::indicator,
2224QCheckBox#checkboxPlayer5Connected::indicator,
2225QCheckBox#checkboxPlayer6Connected::indicator,
2226QCheckBox#checkboxPlayer7Connected::indicator,
2227QCheckBox#checkboxPlayer8Connected::indicator {
2228 width: 14px;
2229 height: 14px;
2230}
2231
2232QCheckBox#checkboxPlayer1Connected::indicator:checked,
2233QCheckBox#checkboxPlayer2Connected::indicator:checked,
2234QCheckBox#checkboxPlayer3Connected::indicator:checked,
2235QCheckBox#checkboxPlayer4Connected::indicator:checked,
2236QCheckBox#checkboxPlayer5Connected::indicator:checked,
2237QCheckBox#checkboxPlayer6Connected::indicator:checked,
2238QCheckBox#checkboxPlayer7Connected::indicator:checked,
2239QCheckBox#checkboxPlayer8Connected::indicator:checked,
2240QGroupBox#groupConnectedController::indicator:checked {
2241 border-radius: 2px;
2242 border: 1px solid #929192;
2243 background: #39ff14;
2244 image: none;
2245}
2246
2247QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
2248QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
2249QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
2250QCheckBox#checkboxPlayer4Connected::indicator:unchecked,
2251QCheckBox#checkboxPlayer5Connected::indicator:unchecked,
2252QCheckBox#checkboxPlayer6Connected::indicator:unchecked,
2253QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
2254QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
2255QGroupBox#groupConnectedController::indicator:unchecked {
2256 border-radius: 2px;
2257 border: 1px solid #929192;
2258 background: transparent;
2259 image: none;
2260}
2261
2262QSpinBox#spinboxLStickRange,
2263QSpinBox#spinboxRStickRange {
2264 min-width: 38px;
2265}
2266
2267QGroupBox#motionGroup::indicator,
2268QGroupBox#vibrationGroup::indicator {
2269 margin-left: 0px;
2270}
2271
2272QGroupBox#motionGroup::title,
2273QGroupBox#vibrationGroup::title {
2274spacing: 2px;
2275 padding-left: 1px;
2276 padding-right: 1px;
2277}
2278
2279QListWidget#selectorList {
2280 background-color: #0f1922;
2281}
2282
2283QSpinBox,
2284QLineEdit,
2285QTreeView#hotkey_list,
2286QScrollArea#scrollArea QTreeView {
2287 background-color: #0f1922;
2288} \ No newline at end of file
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject 82417da7803e2cf18efc28a1cd3f3d0a4b6045a Subproject 0e1112b7df77ae55a62a51622940d5c8f9e8c84
diff --git a/externals/microprofile/microprofile.h b/externals/microprofile/microprofile.h
index 0c0d0a4d3..6dae65a66 100644
--- a/externals/microprofile/microprofile.h
+++ b/externals/microprofile/microprofile.h
@@ -152,9 +152,11 @@ typedef uint16_t MicroProfileGroupId;
152 152
153#include <stdint.h> 153#include <stdint.h>
154#include <string.h> 154#include <string.h>
155#include <thread> 155#include <algorithm>
156#include <mutex> 156#include <array>
157#include <atomic> 157#include <atomic>
158#include <mutex>
159#include <thread>
158 160
159#ifndef MICROPROFILE_API 161#ifndef MICROPROFILE_API
160#define MICROPROFILE_API 162#define MICROPROFILE_API
@@ -605,28 +607,45 @@ struct MicroProfileFrameState
605 607
606struct MicroProfileThreadLog 608struct MicroProfileThreadLog
607{ 609{
608 MicroProfileLogEntry Log[MICROPROFILE_BUFFER_SIZE]; 610 std::array<MicroProfileLogEntry, MICROPROFILE_BUFFER_SIZE> Log{};
609 611
610 std::atomic<uint32_t> nPut; 612 std::atomic<uint32_t> nPut{0};
611 std::atomic<uint32_t> nGet; 613 std::atomic<uint32_t> nGet{0};
612 uint32_t nActive; 614 uint32_t nActive = 0;
613 uint32_t nGpu; 615 uint32_t nGpu = 0;
614 ThreadIdType nThreadId; 616 ThreadIdType nThreadId{};
615 617
616 uint32_t nStack[MICROPROFILE_STACK_MAX]; 618 std::array<uint32_t, MICROPROFILE_STACK_MAX> nStack{};
617 int64_t nChildTickStack[MICROPROFILE_STACK_MAX]; 619 std::array<int64_t, MICROPROFILE_STACK_MAX> nChildTickStack{};
618 uint32_t nStackPos; 620 uint32_t nStackPos = 0;
619 621
620 622
621 uint8_t nGroupStackPos[MICROPROFILE_MAX_GROUPS]; 623 std::array<uint8_t, MICROPROFILE_MAX_GROUPS> nGroupStackPos{};
622 int64_t nGroupTicks[MICROPROFILE_MAX_GROUPS]; 624 std::array<int64_t, MICROPROFILE_MAX_GROUPS> nGroupTicks{};
623 int64_t nAggregateGroupTicks[MICROPROFILE_MAX_GROUPS]; 625 std::array<int64_t, MICROPROFILE_MAX_GROUPS> nAggregateGroupTicks{};
624 enum 626 enum
625 { 627 {
626 THREAD_MAX_LEN = 64, 628 THREAD_MAX_LEN = 64,
627 }; 629 };
628 char ThreadName[64]; 630 char ThreadName[64]{};
629 int nFreeListNext; 631 int nFreeListNext = 0;
632
633 void Reset() {
634 Log.fill({});
635 nPut = 0;
636 nGet = 0;
637 nActive = 0;
638 nGpu = 0;
639 nThreadId = {};
640 nStack.fill(0);
641 nChildTickStack.fill(0);
642 nStackPos = 0;
643 nGroupStackPos.fill(0);
644 nGroupTicks.fill(0);
645 nAggregateGroupTicks.fill(0);
646 std::fill(std::begin(ThreadName), std::end(ThreadName), '\0');
647 nFreeListNext = 0;
648 }
630}; 649};
631 650
632#if MICROPROFILE_GPU_TIMERS_D3D11 651#if MICROPROFILE_GPU_TIMERS_D3D11
@@ -1151,6 +1170,7 @@ MicroProfileThreadLog* MicroProfileCreateThreadLog(const char* pName)
1151 MP_ASSERT(pLog->nPut.load() == 0); 1170 MP_ASSERT(pLog->nPut.load() == 0);
1152 MP_ASSERT(pLog->nGet.load() == 0); 1171 MP_ASSERT(pLog->nGet.load() == 0);
1153 S.nFreeListHead = S.Pool[S.nFreeListHead]->nFreeListNext; 1172 S.nFreeListHead = S.Pool[S.nFreeListHead]->nFreeListNext;
1173 pLog->Reset();
1154 } 1174 }
1155 else 1175 else
1156 { 1176 {
@@ -1158,7 +1178,6 @@ MicroProfileThreadLog* MicroProfileCreateThreadLog(const char* pName)
1158 S.nMemUsage += sizeof(MicroProfileThreadLog); 1178 S.nMemUsage += sizeof(MicroProfileThreadLog);
1159 S.Pool[S.nNumLogs++] = pLog; 1179 S.Pool[S.nNumLogs++] = pLog;
1160 } 1180 }
1161 memset(pLog, 0, sizeof(*pLog));
1162 int len = (int)strlen(pName); 1181 int len = (int)strlen(pName);
1163 int maxlen = sizeof(pLog->ThreadName)-1; 1182 int maxlen = sizeof(pLog->ThreadName)-1;
1164 len = len < maxlen ? len : maxlen; 1183 len = len < maxlen ? len : maxlen;
@@ -1206,8 +1225,8 @@ void MicroProfileOnThreadExit()
1206 { 1225 {
1207 S.Frames[i].nLogStart[nLogIndex] = 0; 1226 S.Frames[i].nLogStart[nLogIndex] = 0;
1208 } 1227 }
1209 memset(pLog->nGroupStackPos, 0, sizeof(pLog->nGroupStackPos)); 1228 pLog->nGroupStackPos.fill(0);
1210 memset(pLog->nGroupTicks, 0, sizeof(pLog->nGroupTicks)); 1229 pLog->nGroupTicks.fill(0);
1211 } 1230 }
1212} 1231}
1213 1232
diff --git a/externals/microprofile/microprofileui.h b/externals/microprofile/microprofileui.h
index fe2410cf4..85fbf2cb9 100644
--- a/externals/microprofile/microprofileui.h
+++ b/externals/microprofile/microprofileui.h
@@ -169,14 +169,13 @@ MICROPROFILEUI_API void MicroProfileCustomGroup(const char* pCustomName, uint32_
169MICROPROFILEUI_API void MicroProfileCustomGroupAddTimer(const char* pCustomName, const char* pGroup, const char* pTimer); 169MICROPROFILEUI_API void MicroProfileCustomGroupAddTimer(const char* pCustomName, const char* pGroup, const char* pTimer);
170 170
171#ifdef MICROPROFILEUI_IMPL 171#ifdef MICROPROFILEUI_IMPL
172#ifdef _WIN32 172#include <inttypes.h>
173#define snprintf _snprintf
174#endif
175#include <stdio.h> 173#include <stdio.h>
176#include <stdlib.h> 174#include <stdlib.h>
177#include <stdarg.h> 175#include <stdarg.h>
178#include <math.h> 176#include <math.h>
179#include <algorithm> 177#include <algorithm>
178#include <array>
180 179
181MICROPROFILE_DEFINE(g_MicroProfileDetailed, "MicroProfile", "Detailed View", 0x8888000); 180MICROPROFILE_DEFINE(g_MicroProfileDetailed, "MicroProfile", "Detailed View", 0x8888000);
182MICROPROFILE_DEFINE(g_MicroProfileDrawGraph, "MicroProfile", "Draw Graph", 0xff44ee00); 181MICROPROFILE_DEFINE(g_MicroProfileDrawGraph, "MicroProfile", "Draw Graph", 0xff44ee00);
@@ -227,10 +226,10 @@ struct SOptionDesc
227 uint8_t nIndex; 226 uint8_t nIndex;
228 bool bSelected; 227 bool bSelected;
229}; 228};
230static uint32_t g_MicroProfileAggregatePresets[] = {0, 10, 20, 30, 60, 120}; 229static const std::array<uint32_t, 6> g_MicroProfileAggregatePresets{0, 10, 20, 30, 60, 120};
231static float g_MicroProfileReferenceTimePresets[] = {5.f, 10.f, 15.f,20.f, 33.33f, 66.66f, 100.f, 250.f, 500.f, 1000.f}; 230static const std::array<float, 10> g_MicroProfileReferenceTimePresets{5.f, 10.f, 15.f,20.f, 33.33f, 66.66f, 100.f, 250.f, 500.f, 1000.f};
232static uint32_t g_MicroProfileOpacityPresets[] = {0x40, 0x80, 0xc0, 0xff}; 231static const std::array<uint32_t, 4> g_MicroProfileOpacityPresets{0x40, 0x80, 0xc0, 0xff};
233static const char* g_MicroProfilePresetNames[] = 232static const std::array<const char*, 7> g_MicroProfilePresetNames
234{ 233{
235 MICROPROFILE_DEFAULT_PRESET, 234 MICROPROFILE_DEFAULT_PRESET,
236 "Render", 235 "Render",
@@ -243,8 +242,8 @@ static const char* g_MicroProfilePresetNames[] =
243 242
244enum 243enum
245{ 244{
246 MICROPROFILE_NUM_REFERENCE_PRESETS = sizeof(g_MicroProfileReferenceTimePresets)/sizeof(g_MicroProfileReferenceTimePresets[0]), 245 MICROPROFILE_NUM_REFERENCE_PRESETS = g_MicroProfileReferenceTimePresets.size(),
247 MICROPROFILE_NUM_OPACITY_PRESETS = sizeof(g_MicroProfileOpacityPresets)/sizeof(g_MicroProfileOpacityPresets[0]), 246 MICROPROFILE_NUM_OPACITY_PRESETS = g_MicroProfileOpacityPresets.size(),
248#if MICROPROFILE_CONTEXT_SWITCH_TRACE 247#if MICROPROFILE_CONTEXT_SWITCH_TRACE
249 MICROPROFILE_OPTION_SIZE = MICROPROFILE_NUM_REFERENCE_PRESETS + MICROPROFILE_NUM_OPACITY_PRESETS * 2 + 2 + 7, 248 MICROPROFILE_OPTION_SIZE = MICROPROFILE_NUM_REFERENCE_PRESETS + MICROPROFILE_NUM_OPACITY_PRESETS * 2 + 2 + 7,
250#else 249#else
@@ -326,9 +325,9 @@ struct MicroProfileUI
326 325
327MicroProfileUI g_MicroProfileUI; 326MicroProfileUI g_MicroProfileUI;
328#define UI g_MicroProfileUI 327#define UI g_MicroProfileUI
329static uint32_t g_nMicroProfileBackColors[2] = { 0x474747, 0x313131 }; 328static const std::array<uint32_t, 2> g_nMicroProfileBackColors{ 0x474747, 0x313131 };
330#define MICROPROFILE_NUM_CONTEXT_SWITCH_COLORS 16 329#define MICROPROFILE_NUM_CONTEXT_SWITCH_COLORS 16
331static uint32_t g_nMicroProfileContextSwitchThreadColors[MICROPROFILE_NUM_CONTEXT_SWITCH_COLORS] = //palette generated by http://tools.medialab.sciences-po.fr/iwanthue/index.php 330static const std::array<uint32_t, MICROPROFILE_NUM_CONTEXT_SWITCH_COLORS> g_nMicroProfileContextSwitchThreadColors //palette generated by http://tools.medialab.sciences-po.fr/iwanthue/index.php
332{ 331{
333 0x63607B, 332 0x63607B,
334 0x755E2B, 333 0x755E2B,
@@ -356,7 +355,7 @@ void MicroProfileInitUI()
356 { 355 {
357 bInitialized = true; 356 bInitialized = true;
358 memset(&g_MicroProfileUI, 0, sizeof(g_MicroProfileUI)); 357 memset(&g_MicroProfileUI, 0, sizeof(g_MicroProfileUI));
359 UI.nActiveMenu = (uint32_t)-1; 358 UI.nActiveMenu = UINT32_MAX;
360 UI.fDetailedOffsetTarget = UI.fDetailedOffset = 0.f; 359 UI.fDetailedOffsetTarget = UI.fDetailedOffset = 0.f;
361 UI.fDetailedRangeTarget = UI.fDetailedRange = 50.f; 360 UI.fDetailedRangeTarget = UI.fDetailedRange = 50.f;
362 361
@@ -368,7 +367,7 @@ void MicroProfileInitUI()
368 UI.nWidth = 100; 367 UI.nWidth = 100;
369 UI.nHeight = 100; 368 UI.nHeight = 100;
370 369
371 UI.nCustomActive = (uint32_t)-1; 370 UI.nCustomActive = UINT32_MAX;
372 UI.nCustomTimerCount = 0; 371 UI.nCustomTimerCount = 0;
373 UI.nCustomCount = 0; 372 UI.nCustomCount = 0;
374 373
@@ -498,8 +497,8 @@ inline void MicroProfileDrawFloatWindow(uint32_t nX, uint32_t nY, const char** p
498 { 497 {
499 MicroProfileDrawBox(nX-MICROPROFILE_TEXT_WIDTH, nY, nX, nY + MICROPROFILE_TEXT_WIDTH, pColors[i]|0xff000000); 498 MicroProfileDrawBox(nX-MICROPROFILE_TEXT_WIDTH, nY, nX, nY + MICROPROFILE_TEXT_WIDTH, pColors[i]|0xff000000);
500 } 499 }
501 MicroProfileDrawText(nX + 1, nY + 1, (uint32_t)-1, ppStrings[i0], (uint32_t)strlen(ppStrings[i0])); 500 MicroProfileDrawText(nX + 1, nY + 1, UINT32_MAX, ppStrings[i0], (uint32_t)strlen(ppStrings[i0]));
502 MicroProfileDrawText(nX + nWidth - nStringLengths[i0+1] * (MICROPROFILE_TEXT_WIDTH+1), nY + 1, (uint32_t)-1, ppStrings[i0+1], (uint32_t)strlen(ppStrings[i0+1])); 501 MicroProfileDrawText(nX + nWidth - nStringLengths[i0+1] * (MICROPROFILE_TEXT_WIDTH+1), nY + 1, UINT32_MAX, ppStrings[i0+1], (uint32_t)strlen(ppStrings[i0+1]));
503 nY += (MICROPROFILE_TEXT_HEIGHT+1); 502 nY += (MICROPROFILE_TEXT_HEIGHT+1);
504 } 503 }
505} 504}
@@ -522,7 +521,7 @@ inline void MicroProfileDrawTextBox(uint32_t nX, uint32_t nY, const char** ppStr
522 MicroProfileDrawBox(nX, nY, nX + nWidth, nY + nHeight, 0xff000000); 521 MicroProfileDrawBox(nX, nY, nX + nWidth, nY + nHeight, 0xff000000);
523 for(uint32_t i = 0; i < nNumStrings; ++i) 522 for(uint32_t i = 0; i < nNumStrings; ++i)
524 { 523 {
525 MicroProfileDrawText(nX + 1, nY + 1, (uint32_t)-1, ppStrings[i], (uint32_t)strlen(ppStrings[i])); 524 MicroProfileDrawText(nX + 1, nY + 1, UINT32_MAX, ppStrings[i], (uint32_t)strlen(ppStrings[i]));
526 nY += (MICROPROFILE_TEXT_HEIGHT+1); 525 nY += (MICROPROFILE_TEXT_HEIGHT+1);
527 } 526 }
528} 527}
@@ -781,7 +780,7 @@ inline void MicroProfileDrawDetailedContextSwitchBars(uint32_t nY, uint32_t nThr
781{ 780{
782 MicroProfile& S = *MicroProfileGet(); 781 MicroProfile& S = *MicroProfileGet();
783 int64_t nTickIn = -1; 782 int64_t nTickIn = -1;
784 uint32_t nThreadBefore = -1; 783 uint32_t nThreadBefore = UINT32_MAX;
785 float fToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()); 784 float fToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu());
786 float fMsToScreen = UI.nWidth / UI.fDetailedRange; 785 float fMsToScreen = UI.nWidth / UI.fDetailedRange;
787 float fMouseX = (float)UI.nMouseX; 786 float fMouseX = (float)UI.nMouseX;
@@ -949,10 +948,10 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
949 948
950 uint32_t nContextSwitchHoverThreadAfter = S.nContextSwitchHoverThreadAfter; 949 uint32_t nContextSwitchHoverThreadAfter = S.nContextSwitchHoverThreadAfter;
951 uint32_t nContextSwitchHoverThreadBefore = S.nContextSwitchHoverThreadBefore; 950 uint32_t nContextSwitchHoverThreadBefore = S.nContextSwitchHoverThreadBefore;
952 S.nContextSwitchHoverThread = S.nContextSwitchHoverThreadAfter = S.nContextSwitchHoverThreadBefore = -1; 951 S.nContextSwitchHoverThread = S.nContextSwitchHoverThreadAfter = S.nContextSwitchHoverThreadBefore = UINT32_MAX;
953 952
954 uint32_t nContextSwitchStart = -1; 953 uint32_t nContextSwitchStart = UINT32_MAX;
955 uint32_t nContextSwitchEnd = -1; 954 uint32_t nContextSwitchEnd = UINT32_MAX;
956 S.nContextSwitchHoverCpuNext = 0xff; 955 S.nContextSwitchHoverCpuNext = 0xff;
957 S.nContextSwitchHoverTickIn = -1; 956 S.nContextSwitchHoverTickIn = -1;
958 S.nContextSwitchHoverTickOut = -1; 957 S.nContextSwitchHoverTickOut = -1;
@@ -1005,9 +1004,10 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
1005 }while(pFrameLogFirst != pFrameFirst); 1004 }while(pFrameLogFirst != pFrameFirst);
1006 1005
1007 1006
1008 if(nGet == (uint32_t)-1) 1007 if (nGet == UINT32_MAX) {
1009 continue; 1008 continue;
1010 MP_ASSERT(nGet != (uint32_t)-1); 1009 }
1010 MP_ASSERT(nGet != UINT32_MAX);
1011 1011
1012 nPut = pFrameLogLast->nLogStart[i]; 1012 nPut = pFrameLogLast->nLogStart[i];
1013 1013
@@ -1023,9 +1023,9 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
1023 int64_t nBaseTicks = bGpu ? nBaseTicksGpu : nBaseTicksCpu; 1023 int64_t nBaseTicks = bGpu ? nBaseTicksGpu : nBaseTicksCpu;
1024 char ThreadName[MicroProfileThreadLog::THREAD_MAX_LEN + 16]; 1024 char ThreadName[MicroProfileThreadLog::THREAD_MAX_LEN + 16];
1025 uint64_t nThreadId = pLog->nThreadId; 1025 uint64_t nThreadId = pLog->nThreadId;
1026 snprintf(ThreadName, sizeof(ThreadName)-1, "%04llx: %s", nThreadId, &pLog->ThreadName[0] ); 1026 snprintf(ThreadName, sizeof(ThreadName)-1, "%04" PRIx64 ": %s", nThreadId, &pLog->ThreadName[0] );
1027 nY += 3; 1027 nY += 3;
1028 uint32_t nThreadColor = -1; 1028 uint32_t nThreadColor = UINT32_MAX;
1029 if(pLog->nThreadId == nContextSwitchHoverThreadAfter || pLog->nThreadId == nContextSwitchHoverThreadBefore) 1029 if(pLog->nThreadId == nContextSwitchHoverThreadAfter || pLog->nThreadId == nContextSwitchHoverThreadBefore)
1030 nThreadColor = UI.nHoverColorShared|0x906060; 1030 nThreadColor = UI.nHoverColorShared|0x906060;
1031 MicroProfileDrawText(0, nY, nThreadColor, &ThreadName[0], (uint32_t)strlen(&ThreadName[0])); 1031 MicroProfileDrawText(0, nY, nThreadColor, &ThreadName[0], (uint32_t)strlen(&ThreadName[0]));
@@ -1048,7 +1048,7 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
1048 uint32_t nEnd = nRange[j][1]; 1048 uint32_t nEnd = nRange[j][1];
1049 for(uint32_t k = nStart; k < nEnd; ++k) 1049 for(uint32_t k = nStart; k < nEnd; ++k)
1050 { 1050 {
1051 MicroProfileLogEntry* pEntry = pLog->Log + k; 1051 MicroProfileLogEntry* pEntry = &pLog->Log[k];
1052 int nType = MicroProfileLogType(*pEntry); 1052 int nType = MicroProfileLogType(*pEntry);
1053 if(MP_LOG_ENTER == nType) 1053 if(MP_LOG_ENTER == nType)
1054 { 1054 {
@@ -1066,7 +1066,7 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
1066 continue; 1066 continue;
1067 } 1067 }
1068 1068
1069 MicroProfileLogEntry* pEntryEnter = pLog->Log + nStack[nStackPos-1]; 1069 MicroProfileLogEntry* pEntryEnter = &pLog->Log[nStack[nStackPos-1]];
1070 if(MicroProfileLogTimerIndex(*pEntryEnter) != MicroProfileLogTimerIndex(*pEntry)) 1070 if(MicroProfileLogTimerIndex(*pEntryEnter) != MicroProfileLogTimerIndex(*pEntry))
1071 { 1071 {
1072 //uprintf("mismatch %llx %llx\n", pEntryEnter->nToken, pEntry->nToken); 1072 //uprintf("mismatch %llx %llx\n", pEntryEnter->nToken, pEntry->nToken);
@@ -1126,7 +1126,7 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
1126 uint32_t nIntegerWidth = (uint32_t)(fXEnd - fXStart); 1126 uint32_t nIntegerWidth = (uint32_t)(fXEnd - fXStart);
1127 if(nIntegerWidth) 1127 if(nIntegerWidth)
1128 { 1128 {
1129 if(bHover && UI.nActiveMenu == -1) 1129 if(bHover && UI.nActiveMenu == UINT32_MAX)
1130 { 1130 {
1131 nHoverToken = MicroProfileLogTimerIndex(*pEntry); 1131 nHoverToken = MicroProfileLogTimerIndex(*pEntry);
1132 #if MICROPROFILE_DEBUG 1132 #if MICROPROFILE_DEBUG
@@ -1146,7 +1146,7 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
1146 int nCharacters = (nTextWidth - 2*MICROPROFILE_TEXT_WIDTH) / MICROPROFILE_TEXT_WIDTH; 1146 int nCharacters = (nTextWidth - 2*MICROPROFILE_TEXT_WIDTH) / MICROPROFILE_TEXT_WIDTH;
1147 if(nCharacters>0) 1147 if(nCharacters>0)
1148 { 1148 {
1149 MicroProfileDrawText(fXStartText+1, fYStart+1, -1, S.TimerInfo[nTimerIndex].pName, MicroProfileMin<uint32_t>(S.TimerInfo[nTimerIndex].nNameLen, nCharacters)); 1149 MicroProfileDrawText(fXStartText + 1, fYStart + 1, UINT32_MAX, S.TimerInfo[nTimerIndex].pName, MicroProfileMin<uint32_t>(S.TimerInfo[nTimerIndex].nNameLen, nCharacters));
1150 } 1150 }
1151 } 1151 }
1152#endif 1152#endif
@@ -1158,7 +1158,7 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
1158 int nLineX = (int)floor(fXAvg+0.5f); 1158 int nLineX = (int)floor(fXAvg+0.5f);
1159 if(nLineX != (int)nLinesDrawn[nStackPos]) 1159 if(nLineX != (int)nLinesDrawn[nStackPos])
1160 { 1160 {
1161 if(bHover && UI.nActiveMenu == -1) 1161 if(bHover && UI.nActiveMenu == UINT32_MAX)
1162 { 1162 {
1163 nHoverToken = (uint32_t)MicroProfileLogTimerIndex(*pEntry); 1163 nHoverToken = (uint32_t)MicroProfileLogTimerIndex(*pEntry);
1164 nHoverTime = MicroProfileLogTickDifference(nTickStart, nTickEnd); 1164 nHoverTime = MicroProfileLogTickDifference(nTickStart, nTickEnd);
@@ -1235,9 +1235,9 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
1235 // nThreadId is 32-bit on Windows 1235 // nThreadId is 32-bit on Windows
1236 int nStrLen = snprintf(ThreadName, sizeof(ThreadName)-1, "%04x: %s%s", nThreadId, cLocal, i < nNumThreadsBase ? &S.Pool[i]->ThreadName[0] : MICROPROFILE_THREAD_NAME_FROM_ID(nThreadId) ); 1236 int nStrLen = snprintf(ThreadName, sizeof(ThreadName)-1, "%04x: %s%s", nThreadId, cLocal, i < nNumThreadsBase ? &S.Pool[i]->ThreadName[0] : MICROPROFILE_THREAD_NAME_FROM_ID(nThreadId) );
1237#else 1237#else
1238 int nStrLen = snprintf(ThreadName, sizeof(ThreadName)-1, "%04llx: %s%s", nThreadId, cLocal, i < nNumThreadsBase ? &S.Pool[i]->ThreadName[0] : MICROPROFILE_THREAD_NAME_FROM_ID(nThreadId) ); 1238 int nStrLen = snprintf(ThreadName, sizeof(ThreadName)-1, "%04" PRIx64 ": %s%s", nThreadId, cLocal, i < nNumThreadsBase ? &S.Pool[i]->ThreadName[0] : MICROPROFILE_THREAD_NAME_FROM_ID(nThreadId) );
1239#endif 1239#endif
1240 uint32_t nThreadColor = -1; 1240 uint32_t nThreadColor = UINT32_MAX;
1241 if(nThreadId == nContextSwitchHoverThreadAfter || nThreadId == nContextSwitchHoverThreadBefore) 1241 if(nThreadId == nContextSwitchHoverThreadAfter || nThreadId == nContextSwitchHoverThreadBefore)
1242 nThreadColor = UI.nHoverColorShared|0x906060; 1242 nThreadColor = UI.nHoverColorShared|0x906060;
1243 MicroProfileDrawDetailedContextSwitchBars(nY+2, nThreadId, nContextSwitchStart, nContextSwitchEnd, nBaseTicksCpu, nBaseY); 1243 MicroProfileDrawDetailedContextSwitchBars(nY+2, nThreadId, nContextSwitchStart, nContextSwitchEnd, nBaseTicksCpu, nBaseY);
@@ -1249,9 +1249,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
1249 1249
1250 S.nContextSwitchHoverCpu = S.nContextSwitchHoverCpuNext; 1250 S.nContextSwitchHoverCpu = S.nContextSwitchHoverCpuNext;
1251 1251
1252
1253
1254
1255 UI.pDisplayMouseOver = pMouseOverNext; 1252 UI.pDisplayMouseOver = pMouseOverNext;
1256 1253
1257 if(!S.nRunning) 1254 if(!S.nRunning)
@@ -1286,10 +1283,10 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
1286 float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart); 1283 float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart);
1287 float fStartTextX = fXStart - fStartTextWidth - 2; 1284 float fStartTextX = fXStart - fStartTextWidth - 2;
1288 MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat); 1285 MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
1289 MicroProfileDrawText(fStartTextX+1, nBaseY, (uint32_t)-1, sBuffer, nLenStart); 1286 MicroProfileDrawText(fStartTextX+1, nBaseY, UINT32_MAX, sBuffer, nLenStart);
1290 uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd); 1287 uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd);
1291 MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat); 1288 MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
1292 MicroProfileDrawText(fXEnd+2, nBaseY+1, (uint32_t)-1, sBuffer, nLenEnd); 1289 MicroProfileDrawText(fXEnd+2, nBaseY+1, UINT32_MAX, sBuffer, nLenEnd);
1293 1290
1294 if(UI.nMouseRight) 1291 if(UI.nMouseRight)
1295 { 1292 {
@@ -1316,10 +1313,10 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
1316 float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart); 1313 float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart);
1317 float fStartTextX = fXStart - fStartTextWidth - 2; 1314 float fStartTextX = fXStart - fStartTextWidth - 2;
1318 MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat); 1315 MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
1319 MicroProfileDrawText(fStartTextX+1, nBaseY, (uint32_t)-1, sBuffer, nLenStart); 1316 MicroProfileDrawText(fStartTextX+1, nBaseY, UINT32_MAX, sBuffer, nLenStart);
1320 uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd); 1317 uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd);
1321 MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat); 1318 MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
1322 MicroProfileDrawText(fXEnd+2, nBaseY+1, (uint32_t)-1, sBuffer, nLenEnd); 1319 MicroProfileDrawText(fXEnd+2, nBaseY+1, UINT32_MAX, sBuffer, nLenEnd);
1323 } 1320 }
1324 } 1321 }
1325} 1322}
@@ -1365,7 +1362,7 @@ inline void MicroProfileDrawDetailedFrameHistory(uint32_t nWidth, uint32_t nHeig
1365 fBaseX = fXStart; 1362 fBaseX = fXStart;
1366 uint32_t nColor = MICROPROFILE_FRAME_HISTORY_COLOR_CPU; 1363 uint32_t nColor = MICROPROFILE_FRAME_HISTORY_COLOR_CPU;
1367 if(nIndex == nSelectedFrame) 1364 if(nIndex == nSelectedFrame)
1368 nColor = (uint32_t)-1; 1365 nColor = UINT32_MAX;
1369 MicroProfileDrawBox(fXStart, nBaseY + fScale * nBarHeight, fXEnd, nBaseY+MICROPROFILE_FRAME_HISTORY_HEIGHT, nColor, MicroProfileBoxTypeBar); 1366 MicroProfileDrawBox(fXStart, nBaseY + fScale * nBarHeight, fXEnd, nBaseY+MICROPROFILE_FRAME_HISTORY_HEIGHT, nColor, MicroProfileBoxTypeBar);
1370 if(pNext->nFrameStartCpu > nCpuStart) 1367 if(pNext->nFrameStartCpu > nCpuStart)
1371 { 1368 {
@@ -1387,7 +1384,7 @@ inline void MicroProfileDrawDetailedView(uint32_t nWidth, uint32_t nHeight)
1387 uint32_t nBaseY = MICROPROFILE_TEXT_HEIGHT + 1; 1384 uint32_t nBaseY = MICROPROFILE_TEXT_HEIGHT + 1;
1388 1385
1389 int nSelectedFrame = -1; 1386 int nSelectedFrame = -1;
1390 if(UI.nMouseY > nBaseY && UI.nMouseY <= nBaseY + MICROPROFILE_FRAME_HISTORY_HEIGHT && UI.nActiveMenu == -1) 1387 if(UI.nMouseY > nBaseY && UI.nMouseY <= nBaseY + MICROPROFILE_FRAME_HISTORY_HEIGHT && UI.nActiveMenu == UINT32_MAX)
1391 { 1388 {
1392 1389
1393 nSelectedFrame = ((MICROPROFILE_NUM_FRAMES) * (UI.nWidth-UI.nMouseX) / UI.nWidth); 1390 nSelectedFrame = ((MICROPROFILE_NUM_FRAMES) * (UI.nWidth-UI.nMouseX) / UI.nWidth);
@@ -1425,7 +1422,7 @@ inline void MicroProfileDrawHeader(int32_t nX, uint32_t nWidth, const char* pNam
1425 if(pName) 1422 if(pName)
1426 { 1423 {
1427 MicroProfileDrawBox(nX-8, MICROPROFILE_TEXT_HEIGHT + 2, nX + nWidth+5, MICROPROFILE_TEXT_HEIGHT + 2 + (MICROPROFILE_TEXT_HEIGHT+1), 0xff000000|g_nMicroProfileBackColors[1]); 1424 MicroProfileDrawBox(nX-8, MICROPROFILE_TEXT_HEIGHT + 2, nX + nWidth+5, MICROPROFILE_TEXT_HEIGHT + 2 + (MICROPROFILE_TEXT_HEIGHT+1), 0xff000000|g_nMicroProfileBackColors[1]);
1428 MicroProfileDrawText(nX, MICROPROFILE_TEXT_HEIGHT + 2, (uint32_t)-1, pName, (uint32_t)strlen(pName)); 1425 MicroProfileDrawText(nX, MICROPROFILE_TEXT_HEIGHT + 2, UINT32_MAX, pName, (uint32_t)strlen(pName));
1429 } 1426 }
1430} 1427}
1431 1428
@@ -1440,7 +1437,7 @@ inline void MicroProfileLoopActiveGroupsDraw(int32_t nX, int32_t nY, const char*
1440 uint32_t nCount = 0; 1437 uint32_t nCount = 0;
1441 for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j) 1438 for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
1442 { 1439 {
1443 uint64_t nMask = 1ll << j; 1440 uint64_t nMask = 1ULL << j;
1444 if(nMask & nGroup) 1441 if(nMask & nGroup)
1445 { 1442 {
1446 nY += MICROPROFILE_TEXT_HEIGHT + 1; 1443 nY += MICROPROFILE_TEXT_HEIGHT + 1;
@@ -1521,7 +1518,7 @@ inline void MicroProfileCalcTimers(float* pTimers, float* pAverage, float* pMax,
1521 } 1518 }
1522 } 1519 }
1523 } 1520 }
1524 nMask <<= 1ll; 1521 nMask <<= 1;
1525 } 1522 }
1526} 1523}
1527 1524
@@ -1543,7 +1540,7 @@ inline void MicroProfileDrawBarArrayCallback(uint32_t nTimer, uint32_t nIdx, uin
1543 snprintf(sBuffer, SBUF_MAX-1, "%5.2f", pTimers[nIdx]); 1540 snprintf(sBuffer, SBUF_MAX-1, "%5.2f", pTimers[nIdx]);
1544 if (!pTimers2) 1541 if (!pTimers2)
1545 MicroProfileDrawBox(nX + nTextWidth, nY, nX + nTextWidth + fWidth * pTimers[nIdx+1], nY + nHeight, UI.nOpacityForeground|S.TimerInfo[nTimer].nColor, MicroProfileBoxTypeBar); 1542 MicroProfileDrawBox(nX + nTextWidth, nY, nX + nTextWidth + fWidth * pTimers[nIdx+1], nY + nHeight, UI.nOpacityForeground|S.TimerInfo[nTimer].nColor, MicroProfileBoxTypeBar);
1546 MicroProfileDrawText(nX, nY, (uint32_t)-1, sBuffer, (uint32_t)strlen(sBuffer)); 1543 MicroProfileDrawText(nX, nY, UINT32_MAX, sBuffer, (uint32_t)strlen(sBuffer));
1547} 1544}
1548 1545
1549 1546
@@ -1564,7 +1561,7 @@ inline void MicroProfileDrawBarCallCountCallback(uint32_t nTimer, uint32_t nIdx,
1564 MicroProfile& S = *MicroProfileGet(); 1561 MicroProfile& S = *MicroProfileGet();
1565 char sBuffer[SBUF_MAX]; 1562 char sBuffer[SBUF_MAX];
1566 int nLen = snprintf(sBuffer, SBUF_MAX-1, "%5d", S.Frame[nTimer].nCount);//fix 1563 int nLen = snprintf(sBuffer, SBUF_MAX-1, "%5d", S.Frame[nTimer].nCount);//fix
1567 MicroProfileDrawText(nX, nY, (uint32_t)-1, sBuffer, nLen); 1564 MicroProfileDrawText(nX, nY, UINT32_MAX, sBuffer, nLen);
1568} 1565}
1569 1566
1570inline uint32_t MicroProfileDrawBarCallCount(int32_t nX, int32_t nY, const char* pName) 1567inline uint32_t MicroProfileDrawBarCallCount(int32_t nX, int32_t nY, const char* pName)
@@ -1588,7 +1585,7 @@ inline void MicroProfileDrawBarMetaAverageCallback(uint32_t nTimer, uint32_t nId
1588 float fRcpFrames = pArgs->fRcpFrames; 1585 float fRcpFrames = pArgs->fRcpFrames;
1589 char sBuffer[SBUF_MAX]; 1586 char sBuffer[SBUF_MAX];
1590 int nLen = snprintf(sBuffer, SBUF_MAX-1, "%5.2f", pCounters[nTimer] * fRcpFrames); 1587 int nLen = snprintf(sBuffer, SBUF_MAX-1, "%5.2f", pCounters[nTimer] * fRcpFrames);
1591 MicroProfileDrawText(nX - nLen * (MICROPROFILE_TEXT_WIDTH+1), nY, (uint32_t)-1, sBuffer, nLen); 1588 MicroProfileDrawText(nX - nLen * (MICROPROFILE_TEXT_WIDTH+1), nY, UINT32_MAX, sBuffer, nLen);
1592} 1589}
1593 1590
1594inline uint32_t MicroProfileDrawBarMetaAverage(int32_t nX, int32_t nY, uint64_t* pCounters, const char* pName, uint32_t nTotalHeight) 1591inline uint32_t MicroProfileDrawBarMetaAverage(int32_t nX, int32_t nY, uint64_t* pCounters, const char* pName, uint32_t nTotalHeight)
@@ -1609,8 +1606,8 @@ inline void MicroProfileDrawBarMetaCountCallback(uint32_t nTimer, uint32_t nIdx,
1609{ 1606{
1610 uint64_t* pCounters = (uint64_t*)pExtra; 1607 uint64_t* pCounters = (uint64_t*)pExtra;
1611 char sBuffer[SBUF_MAX]; 1608 char sBuffer[SBUF_MAX];
1612 int nLen = snprintf(sBuffer, SBUF_MAX-1, "%5llu", pCounters[nTimer]); 1609 int nLen = snprintf(sBuffer, SBUF_MAX-1, "%5" PRIu64, pCounters[nTimer]);
1613 MicroProfileDrawText(nX - nLen * (MICROPROFILE_TEXT_WIDTH+1), nY, (uint32_t)-1, sBuffer, nLen); 1610 MicroProfileDrawText(nX - nLen * (MICROPROFILE_TEXT_WIDTH+1), nY, UINT32_MAX, sBuffer, nLen);
1614} 1611}
1615 1612
1616inline uint32_t MicroProfileDrawBarMetaCount(int32_t nX, int32_t nY, uint64_t* pCounters, const char* pName, uint32_t nTotalHeight) 1613inline uint32_t MicroProfileDrawBarMetaCount(int32_t nX, int32_t nY, uint64_t* pCounters, const char* pName, uint32_t nTotalHeight)
@@ -1667,7 +1664,7 @@ bool MicroProfileDrawGraph(uint32_t nScreenWidth, uint32_t nScreenHeight)
1667 if(bMouseOver) 1664 if(bMouseOver)
1668 { 1665 {
1669 float fXAvg = fMouseXPrc * MICROPROFILE_GRAPH_WIDTH + nX; 1666 float fXAvg = fMouseXPrc * MICROPROFILE_GRAPH_WIDTH + nX;
1670 MicroProfileDrawLineVertical(fXAvg, nY, nY + MICROPROFILE_GRAPH_HEIGHT, (uint32_t)-1); 1667 MicroProfileDrawLineVertical(fXAvg, nY, nY + MICROPROFILE_GRAPH_HEIGHT, UINT32_MAX);
1671 } 1668 }
1672 1669
1673 1670
@@ -1706,7 +1703,7 @@ bool MicroProfileDrawGraph(uint32_t nScreenWidth, uint32_t nScreenHeight)
1706 1703
1707 char buf[32]; 1704 char buf[32];
1708 int nLen = snprintf(buf, sizeof(buf)-1, "%5.2fms", S.fReferenceTime); 1705 int nLen = snprintf(buf, sizeof(buf)-1, "%5.2fms", S.fReferenceTime);
1709 MicroProfileDrawText(nX+1, fY1 - (2+MICROPROFILE_TEXT_HEIGHT), (uint32_t)-1, buf, nLen); 1706 MicroProfileDrawText(nX+1, fY1 - (2+MICROPROFILE_TEXT_HEIGHT), UINT32_MAX, buf, nLen);
1710 } 1707 }
1711 1708
1712 1709
@@ -1782,7 +1779,7 @@ void MicroProfileDumpTimers()
1782 1779
1783 for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j) 1780 for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
1784 { 1781 {
1785 uint64_t nMask = 1ll << j; 1782 uint64_t nMask = 1ULL << j;
1786 if(nMask & nActiveGroup) 1783 if(nMask & nActiveGroup)
1787 { 1784 {
1788 MICROPROFILE_PRINTF("%s\n", S.GroupInfo[j].pName); 1785 MICROPROFILE_PRINTF("%s\n", S.GroupInfo[j].pName);
@@ -1823,7 +1820,7 @@ inline void MicroProfileDrawBarView(uint32_t nScreenWidth, uint32_t nScreenHeigh
1823 uint32_t nNumGroups = 0; 1820 uint32_t nNumGroups = 0;
1824 for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j) 1821 for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
1825 { 1822 {
1826 if(nActiveGroup & (1ll << j)) 1823 if(nActiveGroup & (1ULL << j))
1827 { 1824 {
1828 nNumTimers += S.GroupInfo[j].nNumTimers; 1825 nNumTimers += S.GroupInfo[j].nNumTimers;
1829 nNumGroups += 1; 1826 nNumGroups += 1;
@@ -1878,7 +1875,7 @@ inline void MicroProfileDrawBarView(uint32_t nScreenWidth, uint32_t nScreenHeigh
1878 for(uint32_t i = 0; i < nNumTimers+nNumGroups+1; ++i) 1875 for(uint32_t i = 0; i < nNumTimers+nNumGroups+1; ++i)
1879 { 1876 {
1880 uint32_t nY0 = nY + i * (nHeight + 1); 1877 uint32_t nY0 = nY + i * (nHeight + 1);
1881 bool bInside = (UI.nActiveMenu == -1) && ((UI.nMouseY >= nY0) && (UI.nMouseY < (nY0 + nHeight + 1))); 1878 bool bInside = (UI.nActiveMenu == UINT32_MAX) && ((UI.nMouseY >= nY0) && (UI.nMouseY < (nY0 + nHeight + 1)));
1882 MicroProfileDrawBox(nX, nY0, nWidth+nX, nY0 + (nHeight+1)+1, UI.nOpacityBackground | (g_nMicroProfileBackColors[nColorIndex++ & 1] + ((bInside) ? 0x002c2c2c : 0))); 1879 MicroProfileDrawBox(nX, nY0, nWidth+nX, nY0 + (nHeight+1)+1, UI.nOpacityBackground | (g_nMicroProfileBackColors[nColorIndex++ & 1] + ((bInside) ? 0x002c2c2c : 0)));
1883 } 1880 }
1884 nX += 10; 1881 nX += 10;
@@ -1927,22 +1924,22 @@ inline void MicroProfileDrawBarView(uint32_t nScreenWidth, uint32_t nScreenHeigh
1927 nY = nHeight + 3 - UI.nOffsetY; 1924 nY = nHeight + 3 - UI.nOffsetY;
1928 for(uint32_t i = 0; i < nNumTimers+nNumGroups+1; ++i) 1925 for(uint32_t i = 0; i < nNumTimers+nNumGroups+1; ++i)
1929 { 1926 {
1930 uint32_t nY0 = nY + i * (nHeight + 1); 1927 const uint32_t nY0 = nY + i * (nHeight + 1);
1931 bool bInside = (UI.nActiveMenu == -1) && ((UI.nMouseY >= nY0) && (UI.nMouseY < (nY0 + nHeight + 1))); 1928 const bool bInside = (UI.nActiveMenu == UINT32_MAX) && ((UI.nMouseY >= nY0) && (UI.nMouseY < (nY0 + nHeight + 1)));
1932 MicroProfileDrawBox(nX, nY0, nTimerWidth, nY0 + (nHeight+1)+1, 0xff0000000 | (g_nMicroProfileBackColors[nColorIndex++ & 1] + ((bInside) ? 0x002c2c2c : 0))); 1929 MicroProfileDrawBox(nX, nY0, nTimerWidth, nY0 + (nHeight+1)+1, 0xff0000000 | (g_nMicroProfileBackColors[nColorIndex++ & 1] + ((bInside) ? 0x002c2c2c : 0)));
1933 } 1930 }
1934 nX += MicroProfileDrawBarLegend(nX, nY, nTotalHeight, nTimerWidth-5) + 1; 1931 nX += MicroProfileDrawBarLegend(nX, nY, nTotalHeight, nTimerWidth-5) + 1;
1935 1932
1936 for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j) 1933 for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
1937 { 1934 {
1938 if(nActiveGroup & (1ll << j)) 1935 if(nActiveGroup & (1ULL << j))
1939 { 1936 {
1940 MicroProfileDrawText(nX, nY + (1+nHeight) * nLegendOffset, (uint32_t)-1, S.GroupInfo[j].pName, S.GroupInfo[j].nNameLen); 1937 MicroProfileDrawText(nX, nY + (1+nHeight) * nLegendOffset, UINT32_MAX, S.GroupInfo[j].pName, S.GroupInfo[j].nNameLen);
1941 nLegendOffset += S.GroupInfo[j].nNumTimers+1; 1938 nLegendOffset += S.GroupInfo[j].nNumTimers+1;
1942 } 1939 }
1943 } 1940 }
1944 MicroProfileDrawHeader(nX, nTimerWidth-5, "Group"); 1941 MicroProfileDrawHeader(nX, nTimerWidth-5, "Group");
1945 MicroProfileDrawTextRight(nTimerWidth-3, MICROPROFILE_TEXT_HEIGHT + 2, (uint32_t)-1, "Timer", 5); 1942 MicroProfileDrawTextRight(nTimerWidth-3, MICROPROFILE_TEXT_HEIGHT + 2, UINT32_MAX, "Timer", 5);
1946 MicroProfileDrawLineVertical(nTimerWidth, 0, nTotalHeight+nY, UI.nOpacityBackground|g_nMicroProfileBackColors[0]|g_nMicroProfileBackColors[1]); 1943 MicroProfileDrawLineVertical(nTimerWidth, 0, nTotalHeight+nY, UI.nOpacityBackground|g_nMicroProfileBackColors[0]|g_nMicroProfileBackColors[1]);
1947 MicroProfileDrawLineHorizontal(0, nWidth, 2*MICROPROFILE_TEXT_HEIGHT + 3, UI.nOpacityBackground|g_nMicroProfileBackColors[0]|g_nMicroProfileBackColors[1]); 1944 MicroProfileDrawLineHorizontal(0, nWidth, 2*MICROPROFILE_TEXT_HEIGHT + 3, UI.nOpacityBackground|g_nMicroProfileBackColors[0]|g_nMicroProfileBackColors[1]);
1948} 1945}
@@ -2003,7 +2000,7 @@ inline const char* MicroProfileUIMenuGroups(int nIndex, bool* bSelected)
2003 } 2000 }
2004 else 2001 else
2005 { 2002 {
2006 *bSelected = 0 != (S.nActiveGroupWanted & (1ll << Item.nIndex)); 2003 *bSelected = 0 != (S.nActiveGroupWanted & (1ULL << Item.nIndex));
2007 snprintf(buffer, sizeof(buffer)-1, " %s", Item.pName); 2004 snprintf(buffer, sizeof(buffer)-1, " %s", Item.pName);
2008 } 2005 }
2009 return buffer; 2006 return buffer;
@@ -2015,16 +2012,18 @@ inline const char* MicroProfileUIMenuGroups(int nIndex, bool* bSelected)
2015inline const char* MicroProfileUIMenuAggregate(int nIndex, bool* bSelected) 2012inline const char* MicroProfileUIMenuAggregate(int nIndex, bool* bSelected)
2016{ 2013{
2017 MicroProfile& S = *MicroProfileGet(); 2014 MicroProfile& S = *MicroProfileGet();
2018 if(nIndex < sizeof(g_MicroProfileAggregatePresets)/sizeof(g_MicroProfileAggregatePresets[0])) 2015 if(static_cast<uint32_t>(nIndex) < g_MicroProfileAggregatePresets.size())
2019 { 2016 {
2020 int val = g_MicroProfileAggregatePresets[nIndex]; 2017 uint32_t val = g_MicroProfileAggregatePresets[nIndex];
2021 *bSelected = (int)S.nAggregateFlip == val; 2018 *bSelected = S.nAggregateFlip == val;
2022 if(0 == val) 2019 if (0 == val)
2020 {
2023 return "Infinite"; 2021 return "Infinite";
2022 }
2024 else 2023 else
2025 { 2024 {
2026 static char buf[128]; 2025 static char buf[128];
2027 snprintf(buf, sizeof(buf)-1, "%7d", val); 2026 snprintf(buf, sizeof(buf)-1, "%7u", val);
2028 return buf; 2027 return buf;
2029 } 2028 }
2030 } 2029 }
@@ -2098,11 +2097,13 @@ inline const char* MicroProfileUIMenuPreset(int nIndex, bool* bSelected)
2098{ 2097{
2099 static char buf[128]; 2098 static char buf[128];
2100 *bSelected = false; 2099 *bSelected = false;
2101 int nNumPresets = sizeof(g_MicroProfilePresetNames) / sizeof(g_MicroProfilePresetNames[0]); 2100 int nNumPresets = static_cast<int>(g_MicroProfilePresetNames.size());
2102 int nIndexSave = nIndex - nNumPresets - 1; 2101 int nIndexSave = nIndex - nNumPresets - 1;
2103 if(nIndex == nNumPresets) 2102 if (nIndex == nNumPresets)
2103 {
2104 return "--"; 2104 return "--";
2105 else if(nIndexSave >=0 && nIndexSave <nNumPresets) 2105 }
2106 else if(nIndexSave >=0 && nIndexSave < nNumPresets)
2106 { 2107 {
2107 snprintf(buf, sizeof(buf)-1, "Save '%s'", g_MicroProfilePresetNames[nIndexSave]); 2108 snprintf(buf, sizeof(buf)-1, "Save '%s'", g_MicroProfilePresetNames[nIndexSave]);
2108 return buf; 2109 return buf;
@@ -2120,13 +2121,13 @@ inline const char* MicroProfileUIMenuPreset(int nIndex, bool* bSelected)
2120 2121
2121inline const char* MicroProfileUIMenuCustom(int nIndex, bool* bSelected) 2122inline const char* MicroProfileUIMenuCustom(int nIndex, bool* bSelected)
2122{ 2123{
2123 if((uint32_t)-1 == UI.nCustomActive) 2124 if(UINT32_MAX == UI.nCustomActive)
2124 { 2125 {
2125 *bSelected = nIndex == 0; 2126 *bSelected = nIndex == 0;
2126 } 2127 }
2127 else 2128 else
2128 { 2129 {
2129 *bSelected = nIndex-2 == UI.nCustomActive; 2130 *bSelected = nIndex-2 == static_cast<int>(UI.nCustomActive);
2130 } 2131 }
2131 switch(nIndex) 2132 switch(nIndex)
2132 { 2133 {
@@ -2202,7 +2203,7 @@ inline void MicroProfileUIClickGroups(int nIndex)
2202 else 2203 else
2203 { 2204 {
2204 MP_ASSERT(Item.nIndex < S.nGroupCount); 2205 MP_ASSERT(Item.nIndex < S.nGroupCount);
2205 S.nActiveGroupWanted ^= (1ll << Item.nIndex); 2206 S.nActiveGroupWanted ^= (1ULL << Item.nIndex);
2206 } 2207 }
2207 } 2208 }
2208 } 2209 }
@@ -2273,7 +2274,7 @@ inline void MicroProfileUIClickOptions(int nIndex)
2273 2274
2274inline void MicroProfileUIClickPreset(int nIndex) 2275inline void MicroProfileUIClickPreset(int nIndex)
2275{ 2276{
2276 int nNumPresets = sizeof(g_MicroProfilePresetNames) / sizeof(g_MicroProfilePresetNames[0]); 2277 int nNumPresets = static_cast<int>(g_MicroProfilePresetNames.size());
2277 int nIndexSave = nIndex - nNumPresets - 1; 2278 int nIndexSave = nIndex - nNumPresets - 1;
2278 if(nIndexSave >= 0 && nIndexSave < nNumPresets) 2279 if(nIndexSave >= 0 && nIndexSave < nNumPresets)
2279 { 2280 {
@@ -2310,7 +2311,7 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
2310 2311
2311 uint32_t nX = 0; 2312 uint32_t nX = 0;
2312 uint32_t nY = 0; 2313 uint32_t nY = 0;
2313 bool bMouseOver = UI.nMouseY < MICROPROFILE_TEXT_HEIGHT + 1; 2314
2314#define SBUF_SIZE 256 2315#define SBUF_SIZE 256
2315 char buffer[256]; 2316 char buffer[256];
2316 MicroProfileDrawBox(nX, nY, nX + nWidth, nY + (MICROPROFILE_TEXT_HEIGHT+1)+1, 0xff000000|g_nMicroProfileBackColors[1]); 2317 MicroProfileDrawBox(nX, nY, nX + nWidth, nY + (MICROPROFILE_TEXT_HEIGHT+1)+1, 0xff000000|g_nMicroProfileBackColors[1]);
@@ -2321,7 +2322,7 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
2321 uint32_t nNumMenuItems = 0; 2322 uint32_t nNumMenuItems = 0;
2322 2323
2323 int nLen = snprintf(buffer, 127, "MicroProfile"); 2324 int nLen = snprintf(buffer, 127, "MicroProfile");
2324 MicroProfileDrawText(nX, nY, (uint32_t)-1, buffer, nLen); 2325 MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nLen);
2325 nX += (sizeof("MicroProfile")+2) * (MICROPROFILE_TEXT_WIDTH+1); 2326 nX += (sizeof("MicroProfile")+2) * (MICROPROFILE_TEXT_WIDTH+1);
2326 pMenuText[nNumMenuItems++] = "Mode"; 2327 pMenuText[nNumMenuItems++] = "Mode";
2327 pMenuText[nNumMenuItems++] = "Groups"; 2328 pMenuText[nNumMenuItems++] = "Groups";
@@ -2409,7 +2410,7 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
2409 }; 2410 };
2410 2411
2411 2412
2412 uint32_t nSelectMenu = (uint32_t)-1; 2413 uint32_t nSelectMenu = UINT32_MAX;
2413 for(uint32_t i = 0; i < nNumMenuItems; ++i) 2414 for(uint32_t i = 0; i < nNumMenuItems; ++i)
2414 { 2415 {
2415 nMenuX[i] = nX; 2416 nMenuX[i] = nX;
@@ -2419,17 +2420,17 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
2419 { 2420 {
2420 MicroProfileDrawBox(nX-1, nY, nX + nLen * (MICROPROFILE_TEXT_WIDTH+1), nY +(MICROPROFILE_TEXT_HEIGHT+1)+1, 0xff888888); 2421 MicroProfileDrawBox(nX-1, nY, nX + nLen * (MICROPROFILE_TEXT_WIDTH+1), nY +(MICROPROFILE_TEXT_HEIGHT+1)+1, 0xff888888);
2421 nSelectMenu = i; 2422 nSelectMenu = i;
2422 if((UI.nMouseLeft || UI.nMouseRight) && i == (int)nPauseIndex) 2423 if((UI.nMouseLeft || UI.nMouseRight) && i == (uint32_t)nPauseIndex)
2423 { 2424 {
2424 S.nToggleRunning = 1; 2425 S.nToggleRunning = 1;
2425 } 2426 }
2426 } 2427 }
2427 MicroProfileDrawText(nX, nY, (uint32_t)-1, pMenuText[i], (uint32_t)strlen(pMenuText[i])); 2428 MicroProfileDrawText(nX, nY, UINT32_MAX, pMenuText[i], (uint32_t)strlen(pMenuText[i]));
2428 nX += (nLen+1) * (MICROPROFILE_TEXT_WIDTH+1); 2429 nX += (nLen+1) * (MICROPROFILE_TEXT_WIDTH+1);
2429 } 2430 }
2430 uint32_t nMenu = nSelectMenu != (uint32_t)-1 ? nSelectMenu : UI.nActiveMenu; 2431 uint32_t nMenu = nSelectMenu != UINT32_MAX ? nSelectMenu : UI.nActiveMenu;
2431 UI.nActiveMenu = nMenu; 2432 UI.nActiveMenu = nMenu;
2432 if((uint32_t)-1 != nMenu) 2433 if(UINT32_MAX != nMenu)
2433 { 2434 {
2434 nX = nMenuX[nMenu]; 2435 nX = nMenuX[nMenu];
2435 nY += MICROPROFILE_TEXT_HEIGHT+1; 2436 nY += MICROPROFILE_TEXT_HEIGHT+1;
@@ -2450,9 +2451,9 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
2450 { 2451 {
2451 UI.nActiveMenu = nMenu; 2452 UI.nActiveMenu = nMenu;
2452 } 2453 }
2453 else if(nSelectMenu == (uint32_t)-1) 2454 else if(nSelectMenu == UINT32_MAX)
2454 { 2455 {
2455 UI.nActiveMenu = (uint32_t)-1; 2456 UI.nActiveMenu = UINT32_MAX;
2456 } 2457 }
2457 MicroProfileDrawBox(nX, nY, nX + nWidth, nY + nHeight, 0xff000000|g_nMicroProfileBackColors[1]); 2458 MicroProfileDrawBox(nX, nY, nX + nWidth, nY + nHeight, 0xff000000|g_nMicroProfileBackColors[1]);
2458 for(int i = 0; i < nNumLines; ++i) 2459 for(int i = 0; i < nNumLines; ++i)
@@ -2461,7 +2462,6 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
2461 const char* pString = CB(i, &bSelected); 2462 const char* pString = CB(i, &bSelected);
2462 if(UI.nMouseY >= nY && UI.nMouseY < nY + MICROPROFILE_TEXT_HEIGHT + 1) 2463 if(UI.nMouseY >= nY && UI.nMouseY < nY + MICROPROFILE_TEXT_HEIGHT + 1)
2463 { 2464 {
2464 bMouseOver = true;
2465 if(UI.nMouseLeft || UI.nMouseRight) 2465 if(UI.nMouseLeft || UI.nMouseRight)
2466 { 2466 {
2467 CBClick[nMenu](i); 2467 CBClick[nMenu](i);
@@ -2469,7 +2469,7 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
2469 MicroProfileDrawBox(nX, nY, nX + nWidth, nY + MICROPROFILE_TEXT_HEIGHT + 1, 0xff888888); 2469 MicroProfileDrawBox(nX, nY, nX + nWidth, nY + MICROPROFILE_TEXT_HEIGHT + 1, 0xff888888);
2470 } 2470 }
2471 int nLen = snprintf(buffer, SBUF_SIZE-1, "%c %s", bSelected ? '*' : ' ' ,pString); 2471 int nLen = snprintf(buffer, SBUF_SIZE-1, "%c %s", bSelected ? '*' : ' ' ,pString);
2472 MicroProfileDrawText(nX, nY, (uint32_t)-1, buffer, nLen); 2472 MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nLen);
2473 nY += MICROPROFILE_TEXT_HEIGHT+1; 2473 nY += MICROPROFILE_TEXT_HEIGHT+1;
2474 } 2474 }
2475 } 2475 }
@@ -2484,7 +2484,7 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
2484 float fMaxMs = fToMs * S.nFlipMaxDisplay; 2484 float fMaxMs = fToMs * S.nFlipMaxDisplay;
2485 int nLen = snprintf(FrameTimeMessage, sizeof(FrameTimeMessage)-1, "Time[%6.2f] Avg[%6.2f] Max[%6.2f]", fMs, fAverageMs, fMaxMs); 2485 int nLen = snprintf(FrameTimeMessage, sizeof(FrameTimeMessage)-1, "Time[%6.2f] Avg[%6.2f] Max[%6.2f]", fMs, fAverageMs, fMaxMs);
2486 pMenuText[nNumMenuItems++] = &FrameTimeMessage[0]; 2486 pMenuText[nNumMenuItems++] = &FrameTimeMessage[0];
2487 MicroProfileDrawText(nWidth - nLen * (MICROPROFILE_TEXT_WIDTH+1), 0, -1, FrameTimeMessage, nLen); 2487 MicroProfileDrawText(nWidth - nLen * (MICROPROFILE_TEXT_WIDTH+1), 0, UINT32_MAX, FrameTimeMessage, nLen);
2488 } 2488 }
2489} 2489}
2490 2490
@@ -2538,7 +2538,7 @@ inline void MicroProfileMoveGraph()
2538 2538
2539inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight) 2539inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
2540{ 2540{
2541 if((uint32_t)-1 != UI.nCustomActive) 2541 if(UINT32_MAX != UI.nCustomActive)
2542 { 2542 {
2543 MicroProfile& S = *MicroProfileGet(); 2543 MicroProfile& S = *MicroProfileGet();
2544 MP_ASSERT(UI.nCustomActive < MICROPROFILE_CUSTOM_MAX); 2544 MP_ASSERT(UI.nCustomActive < MICROPROFILE_CUSTOM_MAX);
@@ -2571,8 +2571,8 @@ inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
2571 pColors[i] = S.TimerInfo[nTimerIndex].nColor; 2571 pColors[i] = S.TimerInfo[nTimerIndex].nColor;
2572 } 2572 }
2573 2573
2574 MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING + 3*MICROPROFILE_TEXT_WIDTH, nOffsetY, (uint32_t)-1, "Avg", sizeof("Avg")-1); 2574 MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING + 3*MICROPROFILE_TEXT_WIDTH, nOffsetY, UINT32_MAX, "Avg", sizeof("Avg")-1);
2575 MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING + 13*MICROPROFILE_TEXT_WIDTH, nOffsetY, (uint32_t)-1, "Max", sizeof("Max")-1); 2575 MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING + 13*MICROPROFILE_TEXT_WIDTH, nOffsetY, UINT32_MAX, "Max", sizeof("Max")-1);
2576 for(uint32_t i = 0; i < nCount; ++i) 2576 for(uint32_t i = 0; i < nCount; ++i)
2577 { 2577 {
2578 nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT); 2578 nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT);
@@ -2582,10 +2582,10 @@ inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
2582 int nSize; 2582 int nSize;
2583 uint32_t nOffsetX = MICROPROFILE_CUSTOM_PADDING; 2583 uint32_t nOffsetX = MICROPROFILE_CUSTOM_PADDING;
2584 nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2f", pTimeAvg[i]); 2584 nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2f", pTimeAvg[i]);
2585 MicroProfileDrawText(nOffsetX, nOffsetY, (uint32_t)-1, Buffer, nSize); 2585 MicroProfileDrawText(nOffsetX, nOffsetY, UINT32_MAX, Buffer, nSize);
2586 nOffsetX += (nSize+2) * (MICROPROFILE_TEXT_WIDTH+1); 2586 nOffsetX += (nSize+2) * (MICROPROFILE_TEXT_WIDTH+1);
2587 nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2f", pTimeMax[i]); 2587 nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2f", pTimeMax[i]);
2588 MicroProfileDrawText(nOffsetX, nOffsetY, (uint32_t)-1, Buffer, nSize); 2588 MicroProfileDrawText(nOffsetX, nOffsetY, UINT32_MAX, Buffer, nSize);
2589 nOffsetX += (nSize+2) * (MICROPROFILE_TEXT_WIDTH+1); 2589 nOffsetX += (nSize+2) * (MICROPROFILE_TEXT_WIDTH+1);
2590 nSize = snprintf(Buffer, sizeof(Buffer)-1, "%s:%s", S.GroupInfo[nGroupIndex].pName, pTimerInfo->pName); 2590 nSize = snprintf(Buffer, sizeof(Buffer)-1, "%s:%s", S.GroupInfo[nGroupIndex].pName, pTimerInfo->pName);
2591 MicroProfileDrawText(nOffsetX, nOffsetY, pTimerInfo->nColor, Buffer, nSize); 2591 MicroProfileDrawText(nOffsetX, nOffsetY, pTimerInfo->nColor, Buffer, nSize);
@@ -2599,9 +2599,9 @@ inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
2599 nOffsetY = nOffsetYBase; 2599 nOffsetY = nOffsetYBase;
2600 float* pMs = pCustom->nFlags & MICROPROFILE_CUSTOM_BAR_SOURCE_MAX ? pTimeMax : pTimeAvg; 2600 float* pMs = pCustom->nFlags & MICROPROFILE_CUSTOM_BAR_SOURCE_MAX ? pTimeMax : pTimeAvg;
2601 const char* pString = pCustom->nFlags & MICROPROFILE_CUSTOM_BAR_SOURCE_MAX ? "Max" : "Avg"; 2601 const char* pString = pCustom->nFlags & MICROPROFILE_CUSTOM_BAR_SOURCE_MAX ? "Max" : "Avg";
2602 MicroProfileDrawText(nMaxOffsetX, nOffsetY, (uint32_t)-1, pString, static_cast<uint32_t>(strlen(pString))); 2602 MicroProfileDrawText(nMaxOffsetX, nOffsetY, UINT32_MAX, pString, static_cast<uint32_t>(strlen(pString)));
2603 int nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2fms", fReference); 2603 int nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2fms", fReference);
2604 MicroProfileDrawText(nReducedWidth - (1+nSize) * (MICROPROFILE_TEXT_WIDTH+1), nOffsetY, (uint32_t)-1, Buffer, nSize); 2604 MicroProfileDrawText(nReducedWidth - (1+nSize) * (MICROPROFILE_TEXT_WIDTH+1), nOffsetY, UINT32_MAX, Buffer, nSize);
2605 for(uint32_t i = 0; i < nCount; ++i) 2605 for(uint32_t i = 0; i < nCount; ++i)
2606 { 2606 {
2607 nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT); 2607 nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT);
@@ -2613,9 +2613,9 @@ inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
2613 { 2613 {
2614 nOffsetY += 2*(1+MICROPROFILE_TEXT_HEIGHT); 2614 nOffsetY += 2*(1+MICROPROFILE_TEXT_HEIGHT);
2615 const char* pString = pCustom->nFlags & MICROPROFILE_CUSTOM_STACK_SOURCE_MAX ? "Max" : "Avg"; 2615 const char* pString = pCustom->nFlags & MICROPROFILE_CUSTOM_STACK_SOURCE_MAX ? "Max" : "Avg";
2616 MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING, nOffsetY, (uint32_t)-1, pString, static_cast<uint32_t>(strlen(pString))); 2616 MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING, nOffsetY, UINT32_MAX, pString, static_cast<uint32_t>(strlen(pString)));
2617 int nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2fms", fReference); 2617 int nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2fms", fReference);
2618 MicroProfileDrawText(nReducedWidth - (1+nSize) * (MICROPROFILE_TEXT_WIDTH+1), nOffsetY, (uint32_t)-1, Buffer, nSize); 2618 MicroProfileDrawText(nReducedWidth - (1+nSize) * (MICROPROFILE_TEXT_WIDTH+1), nOffsetY, UINT32_MAX, Buffer, nSize);
2619 nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT); 2619 nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT);
2620 float fPosX = MICROPROFILE_CUSTOM_PADDING; 2620 float fPosX = MICROPROFILE_CUSTOM_PADDING;
2621 float* pMs = pCustom->nFlags & MICROPROFILE_CUSTOM_STACK_SOURCE_MAX ? pTimeMax : pTimeAvg; 2621 float* pMs = pCustom->nFlags & MICROPROFILE_CUSTOM_STACK_SOURCE_MAX ? pTimeMax : pTimeAvg;
@@ -2668,7 +2668,7 @@ inline void MicroProfileDraw(uint32_t nWidth, uint32_t nHeight)
2668 UI.nHoverTime = 0; 2668 UI.nHoverTime = 0;
2669 UI.nHoverFrame = -1; 2669 UI.nHoverFrame = -1;
2670 if(S.nDisplay != MP_DRAW_DETAILED) 2670 if(S.nDisplay != MP_DRAW_DETAILED)
2671 S.nContextSwitchHoverThread = S.nContextSwitchHoverThreadAfter = S.nContextSwitchHoverThreadBefore = -1; 2671 S.nContextSwitchHoverThread = S.nContextSwitchHoverThreadAfter = S.nContextSwitchHoverThreadBefore = UINT32_MAX;
2672 MicroProfileMoveGraph(); 2672 MicroProfileMoveGraph();
2673 2673
2674 2674
@@ -2798,13 +2798,13 @@ inline void MicroProfileDraw(uint32_t nWidth, uint32_t nHeight)
2798 2798
2799 2799
2800 2800
2801 if(UI.nActiveMenu == -1 && !bMouseOverGraph) 2801 if(UI.nActiveMenu == UINT32_MAX && !bMouseOverGraph)
2802 { 2802 {
2803 if(UI.nHoverToken != MICROPROFILE_INVALID_TOKEN) 2803 if(UI.nHoverToken != MICROPROFILE_INVALID_TOKEN)
2804 { 2804 {
2805 MicroProfileDrawFloatTooltip(UI.nMouseX, UI.nMouseY, UI.nHoverToken, UI.nHoverTime); 2805 MicroProfileDrawFloatTooltip(UI.nMouseX, UI.nMouseY, UI.nHoverToken, UI.nHoverTime);
2806 } 2806 }
2807 else if(S.nContextSwitchHoverThreadAfter != -1 && S.nContextSwitchHoverThreadBefore != -1) 2807 else if(S.nContextSwitchHoverThreadAfter != UINT32_MAX && S.nContextSwitchHoverThreadBefore != UINT32_MAX)
2808 { 2808 {
2809 float fToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()); 2809 float fToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu());
2810 MicroProfileStringArray ToolTip; 2810 MicroProfileStringArray ToolTip;
@@ -2820,7 +2820,7 @@ inline void MicroProfileDraw(uint32_t nWidth, uint32_t nHeight)
2820 MicroProfileStringArrayFormat(&ToolTip, "%6.2fms", fToMs * nDifference ); 2820 MicroProfileStringArrayFormat(&ToolTip, "%6.2fms", fToMs * nDifference );
2821 MicroProfileStringArrayAddLiteral(&ToolTip, "CPU"); 2821 MicroProfileStringArrayAddLiteral(&ToolTip, "CPU");
2822 MicroProfileStringArrayFormat(&ToolTip, "%d", S.nContextSwitchHoverCpu); 2822 MicroProfileStringArrayFormat(&ToolTip, "%d", S.nContextSwitchHoverCpu);
2823 MicroProfileDrawFloatWindow(UI.nMouseX, UI.nMouseY+20, &ToolTip.ppStrings[0], ToolTip.nNumStrings, -1); 2823 MicroProfileDrawFloatWindow(UI.nMouseX, UI.nMouseY+20, &ToolTip.ppStrings[0], ToolTip.nNumStrings, UINT32_MAX);
2824 2824
2825 2825
2826 } 2826 }
@@ -2858,7 +2858,7 @@ inline void MicroProfileDraw(uint32_t nWidth, uint32_t nHeight)
2858 } 2858 }
2859 } 2859 }
2860 #endif 2860 #endif
2861 MicroProfileDrawFloatWindow(UI.nMouseX, UI.nMouseY+20, &ToolTip.ppStrings[0], ToolTip.nNumStrings, -1); 2861 MicroProfileDrawFloatWindow(UI.nMouseX, UI.nMouseY+20, &ToolTip.ppStrings[0], ToolTip.nNumStrings, UINT32_MAX);
2862 } 2862 }
2863 if(UI.nMouseLeft) 2863 if(UI.nMouseLeft)
2864 { 2864 {
@@ -2883,7 +2883,7 @@ inline void MicroProfileDraw(uint32_t nWidth, uint32_t nHeight)
2883#endif 2883#endif
2884 m.unlock(); 2884 m.unlock();
2885 } 2885 }
2886 else if(UI.nCustomActive != (uint32_t)-1) 2886 else if(UI.nCustomActive != UINT32_MAX)
2887 { 2887 {
2888 std::recursive_mutex& m = MicroProfileGetMutex(); 2888 std::recursive_mutex& m = MicroProfileGetMutex();
2889 m.lock(); 2889 m.lock();
@@ -3179,7 +3179,7 @@ void MicroProfileLoadPreset(const char* pSuffix)
3179 { 3179 {
3180 if(0 == MP_STRCASECMP(pGroupName, S.GroupInfo[j].pName)) 3180 if(0 == MP_STRCASECMP(pGroupName, S.GroupInfo[j].pName))
3181 { 3181 {
3182 S.nActiveGroupWanted |= (1ll << j); 3182 S.nActiveGroupWanted |= (1ULL << j);
3183 } 3183 }
3184 } 3184 }
3185 } 3185 }
@@ -3212,7 +3212,7 @@ void MicroProfileLoadPreset(const char* pSuffix)
3212 uint64_t nGroupIndex = S.TimerInfo[j].nGroupIndex; 3212 uint64_t nGroupIndex = S.TimerInfo[j].nGroupIndex;
3213 if(0 == MP_STRCASECMP(pGraphName, S.TimerInfo[j].pName) && 0 == MP_STRCASECMP(pGraphGroupName, S.GroupInfo[nGroupIndex].pName)) 3213 if(0 == MP_STRCASECMP(pGraphName, S.TimerInfo[j].pName) && 0 == MP_STRCASECMP(pGraphGroupName, S.GroupInfo[nGroupIndex].pName))
3214 { 3214 {
3215 MicroProfileToken nToken = MicroProfileMakeToken(1ll << nGroupIndex, (uint16_t)j); 3215 MicroProfileToken nToken = MicroProfileMakeToken(1ULL << nGroupIndex, (uint16_t)j);
3216 S.Graph[i].nToken = nToken; // note: group index is stored here but is checked without in MicroProfileToggleGraph()! 3216 S.Graph[i].nToken = nToken; // note: group index is stored here but is checked without in MicroProfileToggleGraph()!
3217 S.TimerInfo[j].bGraph = true; 3217 S.TimerInfo[j].bGraph = true;
3218 if(nToken != nPrevToken) 3218 if(nToken != nPrevToken)
@@ -3235,7 +3235,7 @@ inline uint32_t MicroProfileCustomGroupFind(const char* pCustomName)
3235 return i; 3235 return i;
3236 } 3236 }
3237 } 3237 }
3238 return (uint32_t)-1; 3238 return UINT32_MAX;
3239} 3239}
3240 3240
3241inline uint32_t MicroProfileCustomGroup(const char* pCustomName) 3241inline uint32_t MicroProfileCustomGroup(const char* pCustomName)
@@ -3251,7 +3251,7 @@ inline uint32_t MicroProfileCustomGroup(const char* pCustomName)
3251 uint32_t nIndex = UI.nCustomCount; 3251 uint32_t nIndex = UI.nCustomCount;
3252 UI.nCustomCount++; 3252 UI.nCustomCount++;
3253 memset(&UI.Custom[nIndex], 0, sizeof(UI.Custom[nIndex])); 3253 memset(&UI.Custom[nIndex], 0, sizeof(UI.Custom[nIndex]));
3254 uint32_t nLen = (uint32_t)strlen(pCustomName); 3254 size_t nLen = strlen(pCustomName);
3255 if(nLen > MICROPROFILE_NAME_MAX_LEN-1) 3255 if(nLen > MICROPROFILE_NAME_MAX_LEN-1)
3256 nLen = MICROPROFILE_NAME_MAX_LEN-1; 3256 nLen = MICROPROFILE_NAME_MAX_LEN-1;
3257 memcpy(&UI.Custom[nIndex].pName[0], pCustomName, nLen); 3257 memcpy(&UI.Custom[nIndex].pName[0], pCustomName, nLen);
@@ -3309,7 +3309,7 @@ inline void MicroProfileCustomGroupEnable(uint32_t nIndex)
3309void MicroProfileCustomGroupToggle(const char* pCustomName) 3309void MicroProfileCustomGroupToggle(const char* pCustomName)
3310{ 3310{
3311 uint32_t nIndex = MicroProfileCustomGroupFind(pCustomName); 3311 uint32_t nIndex = MicroProfileCustomGroupFind(pCustomName);
3312 if(nIndex == (uint32_t)-1 || nIndex == UI.nCustomActive) 3312 if(nIndex == UINT32_MAX || nIndex == UI.nCustomActive)
3313 { 3313 {
3314 MicroProfileCustomGroupDisable(); 3314 MicroProfileCustomGroupDisable();
3315 } 3315 }
@@ -3328,13 +3328,13 @@ void MicroProfileCustomGroupDisable()
3328{ 3328{
3329 MicroProfile& S = *MicroProfileGet(); 3329 MicroProfile& S = *MicroProfileGet();
3330 S.nForceGroupUI = 0; 3330 S.nForceGroupUI = 0;
3331 UI.nCustomActive = (uint32_t)-1; 3331 UI.nCustomActive = UINT32_MAX;
3332} 3332}
3333 3333
3334void MicroProfileCustomGroupAddTimer(const char* pCustomName, const char* pGroup, const char* pTimer) 3334void MicroProfileCustomGroupAddTimer(const char* pCustomName, const char* pGroup, const char* pTimer)
3335{ 3335{
3336 uint32_t nIndex = MicroProfileCustomGroupFind(pCustomName); 3336 uint32_t nIndex = MicroProfileCustomGroupFind(pCustomName);
3337 if((uint32_t)-1 == nIndex) 3337 if(UINT32_MAX == nIndex)
3338 { 3338 {
3339 return; 3339 return;
3340 } 3340 }
@@ -3344,7 +3344,7 @@ void MicroProfileCustomGroupAddTimer(const char* pCustomName, const char* pGroup
3344 MP_ASSERT(nToken != MICROPROFILE_INVALID_TOKEN); //Timer must be registered first. 3344 MP_ASSERT(nToken != MICROPROFILE_INVALID_TOKEN); //Timer must be registered first.
3345 UI.Custom[nIndex].pTimers[nTimerIndex] = nToken; 3345 UI.Custom[nIndex].pTimers[nTimerIndex] = nToken;
3346 uint16_t nGroup = MicroProfileGetGroupIndex(nToken); 3346 uint16_t nGroup = MicroProfileGetGroupIndex(nToken);
3347 UI.Custom[nIndex].nGroupMask |= (1ll << nGroup); 3347 UI.Custom[nIndex].nGroupMask |= (1ULL << nGroup);
3348 UI.Custom[nIndex].nNumTimers++; 3348 UI.Custom[nIndex].nNumTimers++;
3349} 3349}
3350 3350
diff --git a/externals/xbyak b/externals/xbyak
Subproject 82b70e665918efc2ee348091742fd0237b3b68c Subproject 18c9caaa0a3ed5706c39f5aa86cce0db6e65b17
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 41bf5cd4d..c27df946c 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -78,7 +78,7 @@ public:
78 const s16 surround_left{samples[i + 4]}; 78 const s16 surround_left{samples[i + 4]};
79 const s16 surround_right{samples[i + 5]}; 79 const s16 surround_right{samples[i + 5]};
80 // Not used in the ATSC reference implementation 80 // Not used in the ATSC reference implementation
81 [[maybe_unused]] const s16 low_frequency_effects { samples[i + 3] }; 81 [[maybe_unused]] const s16 low_frequency_effects{samples[i + 3]};
82 82
83 constexpr s32 clev{707}; // center mixing level coefficient 83 constexpr s32 clev{707}; // center mixing level coefficient
84 constexpr s32 slev{707}; // surround mixing level coefficient 84 constexpr s32 slev{707}; // surround mixing level coefficient
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 78c3bfb3b..5d54516eb 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -172,7 +172,6 @@ add_library(common STATIC
172 virtual_buffer.h 172 virtual_buffer.h
173 wall_clock.cpp 173 wall_clock.cpp
174 wall_clock.h 174 wall_clock.h
175 web_result.h
176 zstd_compression.cpp 175 zstd_compression.cpp
177 zstd_compression.h 176 zstd_compression.h
178) 177)
diff --git a/src/common/algorithm.h b/src/common/algorithm.h
index e21b1373c..4804a3421 100644
--- a/src/common/algorithm.h
+++ b/src/common/algorithm.h
@@ -15,7 +15,8 @@
15namespace Common { 15namespace Common {
16 16
17template <class ForwardIt, class T, class Compare = std::less<>> 17template <class ForwardIt, class T, class Compare = std::less<>>
18ForwardIt BinaryFind(ForwardIt first, ForwardIt last, const T& value, Compare comp = {}) { 18[[nodiscard]] ForwardIt BinaryFind(ForwardIt first, ForwardIt last, const T& value,
19 Compare comp = {}) {
19 // Note: BOTH type T and the type after ForwardIt is dereferenced 20 // Note: BOTH type T and the type after ForwardIt is dereferenced
20 // must be implicitly convertible to BOTH Type1 and Type2, used in Compare. 21 // must be implicitly convertible to BOTH Type1 and Type2, used in Compare.
21 // This is stricter than lower_bound requirement (see above) 22 // This is stricter than lower_bound requirement (see above)
diff --git a/src/common/alignment.h b/src/common/alignment.h
index ef4d6f896..5040043de 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -9,7 +9,7 @@
9namespace Common { 9namespace Common {
10 10
11template <typename T> 11template <typename T>
12constexpr T AlignUp(T value, std::size_t size) { 12[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) {
13 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 13 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
14 auto mod{static_cast<T>(value % size)}; 14 auto mod{static_cast<T>(value % size)};
15 value -= mod; 15 value -= mod;
@@ -17,31 +17,31 @@ constexpr T AlignUp(T value, std::size_t size) {
17} 17}
18 18
19template <typename T> 19template <typename T>
20constexpr T AlignDown(T value, std::size_t size) { 20[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) {
21 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 21 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
22 return static_cast<T>(value - value % size); 22 return static_cast<T>(value - value % size);
23} 23}
24 24
25template <typename T> 25template <typename T>
26constexpr T AlignBits(T value, std::size_t align) { 26[[nodiscard]] constexpr T AlignBits(T value, std::size_t align) {
27 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 27 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
28 return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align); 28 return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align);
29} 29}
30 30
31template <typename T> 31template <typename T>
32constexpr bool Is4KBAligned(T value) { 32[[nodiscard]] constexpr bool Is4KBAligned(T value) {
33 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 33 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
34 return (value & 0xFFF) == 0; 34 return (value & 0xFFF) == 0;
35} 35}
36 36
37template <typename T> 37template <typename T>
38constexpr bool IsWordAligned(T value) { 38[[nodiscard]] constexpr bool IsWordAligned(T value) {
39 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 39 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
40 return (value & 0b11) == 0; 40 return (value & 0b11) == 0;
41} 41}
42 42
43template <typename T> 43template <typename T>
44constexpr bool IsAligned(T value, std::size_t alignment) { 44[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) {
45 using U = typename std::make_unsigned<T>::type; 45 using U = typename std::make_unsigned<T>::type;
46 const U mask = static_cast<U>(alignment - 1); 46 const U mask = static_cast<U>(alignment - 1);
47 return (value & mask) == 0; 47 return (value & mask) == 0;
@@ -64,7 +64,7 @@ public:
64 template <typename T2> 64 template <typename T2>
65 constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {} 65 constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {}
66 66
67 T* allocate(size_type n) { 67 [[nodiscard]] T* allocate(size_type n) {
68 return static_cast<T*>(::operator new (n * sizeof(T), std::align_val_t{Align})); 68 return static_cast<T*>(::operator new (n * sizeof(T), std::align_val_t{Align}));
69 } 69 }
70 70
diff --git a/src/common/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 @@
17// enough for our purposes. 17// enough for our purposes.
18template <typename Fn> 18template <typename Fn>
19#if defined(_MSC_VER) 19#if defined(_MSC_VER)
20__declspec(noinline, noreturn) 20[[msvc::noinline, noreturn]]
21#elif defined(__GNUC__) 21#elif defined(__GNUC__)
22 __attribute__((noinline, noreturn, cold)) 22[[gnu::cold, gnu::noinline, noreturn]]
23#endif 23#endif
24 static void assert_noinline_call(const Fn& fn) { 24static void
25assert_noinline_call(const Fn& fn) {
25 fn(); 26 fn();
26 Crash(); 27 Crash();
27 exit(1); // Keeps GCC's mouth shut about this actually returning 28 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 @@
8 8
9namespace Common { 9namespace Common {
10 10
11bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected); 11[[nodiscard]] bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected);
12bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected); 12[[nodiscard]] bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected);
13bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected); 13[[nodiscard]] bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected);
14bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected); 14[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected);
15bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected); 15[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected);
16 16
17} // namespace Common 17} // namespace Common
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 26ae6c7fc..0f0661172 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -36,13 +36,6 @@
36#include "common/common_funcs.h" 36#include "common/common_funcs.h"
37#include "common/swap.h" 37#include "common/swap.h"
38 38
39// Inlining
40#ifdef _WIN32
41#define FORCE_INLINE __forceinline
42#else
43#define FORCE_INLINE inline __attribute__((always_inline))
44#endif
45
46/* 39/*
47 * Abstract bitfield class 40 * Abstract bitfield class
48 * 41 *
@@ -142,8 +135,8 @@ public:
142 * containing several bitfields can be assembled by formatting each of their values and ORing 135 * containing several bitfields can be assembled by formatting each of their values and ORing
143 * the results together. 136 * the results together.
144 */ 137 */
145 static constexpr FORCE_INLINE StorageType FormatValue(const T& value) { 138 [[nodiscard]] static constexpr StorageType FormatValue(const T& value) {
146 return ((StorageType)value << position) & mask; 139 return (static_cast<StorageType>(value) << position) & mask;
147 } 140 }
148 141
149 /** 142 /**
@@ -151,7 +144,7 @@ public:
151 * (such as Value() or operator T), but this can be used to extract a value from a bitfield 144 * (such as Value() or operator T), but this can be used to extract a value from a bitfield
152 * union in a constexpr context. 145 * union in a constexpr context.
153 */ 146 */
154 static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) { 147 [[nodiscard]] static constexpr T ExtractValue(const StorageType& storage) {
155 if constexpr (std::numeric_limits<UnderlyingType>::is_signed) { 148 if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
156 std::size_t shift = 8 * sizeof(T) - bits; 149 std::size_t shift = 8 * sizeof(T) - bits;
157 return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >> 150 return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
@@ -175,7 +168,7 @@ public:
175 constexpr BitField(BitField&&) noexcept = default; 168 constexpr BitField(BitField&&) noexcept = default;
176 constexpr BitField& operator=(BitField&&) noexcept = default; 169 constexpr BitField& operator=(BitField&&) noexcept = default;
177 170
178 constexpr operator T() const { 171 [[nodiscard]] constexpr operator T() const {
179 return Value(); 172 return Value();
180 } 173 }
181 174
@@ -183,11 +176,11 @@ public:
183 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); 176 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
184 } 177 }
185 178
186 constexpr T Value() const { 179 [[nodiscard]] constexpr T Value() const {
187 return ExtractValue(storage); 180 return ExtractValue(storage);
188 } 181 }
189 182
190 constexpr explicit operator bool() const { 183 [[nodiscard]] constexpr explicit operator bool() const {
191 return Value() != 0; 184 return Value() != 0;
192 } 185 }
193 186
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index 6f7d5a947..29f59a9a3 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -17,12 +17,12 @@ namespace Common {
17 17
18/// Gets the size of a specified type T in bits. 18/// Gets the size of a specified type T in bits.
19template <typename T> 19template <typename T>
20constexpr std::size_t BitSize() { 20[[nodiscard]] constexpr std::size_t BitSize() {
21 return sizeof(T) * CHAR_BIT; 21 return sizeof(T) * CHAR_BIT;
22} 22}
23 23
24#ifdef _MSC_VER 24#ifdef _MSC_VER
25inline u32 CountLeadingZeroes32(u32 value) { 25[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
26 unsigned long leading_zero = 0; 26 unsigned long leading_zero = 0;
27 27
28 if (_BitScanReverse(&leading_zero, value) != 0) { 28 if (_BitScanReverse(&leading_zero, value) != 0) {
@@ -32,7 +32,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
32 return 32; 32 return 32;
33} 33}
34 34
35inline u32 CountLeadingZeroes64(u64 value) { 35[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
36 unsigned long leading_zero = 0; 36 unsigned long leading_zero = 0;
37 37
38 if (_BitScanReverse64(&leading_zero, value) != 0) { 38 if (_BitScanReverse64(&leading_zero, value) != 0) {
@@ -42,7 +42,7 @@ inline u32 CountLeadingZeroes64(u64 value) {
42 return 64; 42 return 64;
43} 43}
44#else 44#else
45inline u32 CountLeadingZeroes32(u32 value) { 45[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
46 if (value == 0) { 46 if (value == 0) {
47 return 32; 47 return 32;
48 } 48 }
@@ -50,7 +50,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
50 return static_cast<u32>(__builtin_clz(value)); 50 return static_cast<u32>(__builtin_clz(value));
51} 51}
52 52
53inline u32 CountLeadingZeroes64(u64 value) { 53[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
54 if (value == 0) { 54 if (value == 0) {
55 return 64; 55 return 64;
56 } 56 }
@@ -60,7 +60,7 @@ inline u32 CountLeadingZeroes64(u64 value) {
60#endif 60#endif
61 61
62#ifdef _MSC_VER 62#ifdef _MSC_VER
63inline u32 CountTrailingZeroes32(u32 value) { 63[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
64 unsigned long trailing_zero = 0; 64 unsigned long trailing_zero = 0;
65 65
66 if (_BitScanForward(&trailing_zero, value) != 0) { 66 if (_BitScanForward(&trailing_zero, value) != 0) {
@@ -70,7 +70,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
70 return 32; 70 return 32;
71} 71}
72 72
73inline u32 CountTrailingZeroes64(u64 value) { 73[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
74 unsigned long trailing_zero = 0; 74 unsigned long trailing_zero = 0;
75 75
76 if (_BitScanForward64(&trailing_zero, value) != 0) { 76 if (_BitScanForward64(&trailing_zero, value) != 0) {
@@ -80,7 +80,7 @@ inline u32 CountTrailingZeroes64(u64 value) {
80 return 64; 80 return 64;
81} 81}
82#else 82#else
83inline u32 CountTrailingZeroes32(u32 value) { 83[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
84 if (value == 0) { 84 if (value == 0) {
85 return 32; 85 return 32;
86 } 86 }
@@ -88,7 +88,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
88 return static_cast<u32>(__builtin_ctz(value)); 88 return static_cast<u32>(__builtin_ctz(value));
89} 89}
90 90
91inline u32 CountTrailingZeroes64(u64 value) { 91[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
92 if (value == 0) { 92 if (value == 0) {
93 return 64; 93 return 64;
94 } 94 }
@@ -99,13 +99,13 @@ inline u32 CountTrailingZeroes64(u64 value) {
99 99
100#ifdef _MSC_VER 100#ifdef _MSC_VER
101 101
102inline u32 MostSignificantBit32(const u32 value) { 102[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
103 unsigned long result; 103 unsigned long result;
104 _BitScanReverse(&result, value); 104 _BitScanReverse(&result, value);
105 return static_cast<u32>(result); 105 return static_cast<u32>(result);
106} 106}
107 107
108inline u32 MostSignificantBit64(const u64 value) { 108[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
109 unsigned long result; 109 unsigned long result;
110 _BitScanReverse64(&result, value); 110 _BitScanReverse64(&result, value);
111 return static_cast<u32>(result); 111 return static_cast<u32>(result);
@@ -113,30 +113,30 @@ inline u32 MostSignificantBit64(const u64 value) {
113 113
114#else 114#else
115 115
116inline u32 MostSignificantBit32(const u32 value) { 116[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
117 return 31U - static_cast<u32>(__builtin_clz(value)); 117 return 31U - static_cast<u32>(__builtin_clz(value));
118} 118}
119 119
120inline u32 MostSignificantBit64(const u64 value) { 120[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
121 return 63U - static_cast<u32>(__builtin_clzll(value)); 121 return 63U - static_cast<u32>(__builtin_clzll(value));
122} 122}
123 123
124#endif 124#endif
125 125
126inline u32 Log2Floor32(const u32 value) { 126[[nodiscard]] inline u32 Log2Floor32(const u32 value) {
127 return MostSignificantBit32(value); 127 return MostSignificantBit32(value);
128} 128}
129 129
130inline u32 Log2Ceil32(const u32 value) { 130[[nodiscard]] inline u32 Log2Ceil32(const u32 value) {
131 const u32 log2_f = Log2Floor32(value); 131 const u32 log2_f = Log2Floor32(value);
132 return log2_f + ((value ^ (1U << log2_f)) != 0U); 132 return log2_f + ((value ^ (1U << log2_f)) != 0U);
133} 133}
134 134
135inline u32 Log2Floor64(const u64 value) { 135[[nodiscard]] inline u32 Log2Floor64(const u64 value) {
136 return MostSignificantBit64(value); 136 return MostSignificantBit64(value);
137} 137}
138 138
139inline u32 Log2Ceil64(const u64 value) { 139[[nodiscard]] inline u32 Log2Ceil64(const u64 value) {
140 const u64 log2_f = static_cast<u64>(Log2Floor64(value)); 140 const u64 log2_f = static_cast<u64>(Log2Floor64(value));
141 return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL)); 141 return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL));
142} 142}
diff --git a/src/common/cityhash.h b/src/common/cityhash.h
index 4b94f8e18..a00804e01 100644
--- a/src/common/cityhash.h
+++ b/src/common/cityhash.h
@@ -61,42 +61,43 @@
61 61
62#pragma once 62#pragma once
63 63
64#include <cstddef>
65#include <cstdint>
64#include <utility> 66#include <utility>
65#include <stdint.h>
66#include <stdlib.h> // for std::size_t.
67 67
68namespace Common { 68namespace Common {
69 69
70typedef std::pair<uint64_t, uint64_t> uint128; 70using uint128 = std::pair<uint64_t, uint64_t>;
71 71
72inline uint64_t Uint128Low64(const uint128& x) { 72[[nodiscard]] inline uint64_t Uint128Low64(const uint128& x) {
73 return x.first; 73 return x.first;
74} 74}
75inline uint64_t Uint128High64(const uint128& x) { 75[[nodiscard]] inline uint64_t Uint128High64(const uint128& x) {
76 return x.second; 76 return x.second;
77} 77}
78 78
79// Hash function for a byte array. 79// Hash function for a byte array.
80uint64_t CityHash64(const char* buf, std::size_t len); 80[[nodiscard]] uint64_t CityHash64(const char* buf, std::size_t len);
81 81
82// Hash function for a byte array. For convenience, a 64-bit seed is also 82// Hash function for a byte array. For convenience, a 64-bit seed is also
83// hashed into the result. 83// hashed into the result.
84uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed); 84[[nodiscard]] uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed);
85 85
86// Hash function for a byte array. For convenience, two seeds are also 86// Hash function for a byte array. For convenience, two seeds are also
87// hashed into the result. 87// hashed into the result.
88uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0, uint64_t seed1); 88[[nodiscard]] uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0,
89 uint64_t seed1);
89 90
90// Hash function for a byte array. 91// Hash function for a byte array.
91uint128 CityHash128(const char* s, std::size_t len); 92[[nodiscard]] uint128 CityHash128(const char* s, std::size_t len);
92 93
93// Hash function for a byte array. For convenience, a 128-bit seed is also 94// Hash function for a byte array. For convenience, a 128-bit seed is also
94// hashed into the result. 95// hashed into the result.
95uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed); 96[[nodiscard]] uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed);
96 97
97// Hash 128 input bits down to 64 bits of output. 98// Hash 128 input bits down to 64 bits of output.
98// This is intended to be a reasonably good hash function. 99// This is intended to be a reasonably good hash function.
99inline uint64_t Hash128to64(const uint128& x) { 100[[nodiscard]] inline uint64_t Hash128to64(const uint128& x) {
100 // Murmur-inspired hashing. 101 // Murmur-inspired hashing.
101 const uint64_t kMul = 0x9ddfea08eb382d69ULL; 102 const uint64_t kMul = 0x9ddfea08eb382d69ULL;
102 uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; 103 uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
diff --git a/src/common/color.h b/src/common/color.h
index 3a2222077..bbcac858e 100644
--- a/src/common/color.h
+++ b/src/common/color.h
@@ -10,45 +10,45 @@
10#include "common/swap.h" 10#include "common/swap.h"
11#include "common/vector_math.h" 11#include "common/vector_math.h"
12 12
13namespace Color { 13namespace Common::Color {
14 14
15/// Convert a 1-bit color component to 8 bit 15/// Convert a 1-bit color component to 8 bit
16constexpr u8 Convert1To8(u8 value) { 16[[nodiscard]] constexpr u8 Convert1To8(u8 value) {
17 return value * 255; 17 return value * 255;
18} 18}
19 19
20/// Convert a 4-bit color component to 8 bit 20/// Convert a 4-bit color component to 8 bit
21constexpr u8 Convert4To8(u8 value) { 21[[nodiscard]] constexpr u8 Convert4To8(u8 value) {
22 return (value << 4) | value; 22 return (value << 4) | value;
23} 23}
24 24
25/// Convert a 5-bit color component to 8 bit 25/// Convert a 5-bit color component to 8 bit
26constexpr u8 Convert5To8(u8 value) { 26[[nodiscard]] constexpr u8 Convert5To8(u8 value) {
27 return (value << 3) | (value >> 2); 27 return (value << 3) | (value >> 2);
28} 28}
29 29
30/// Convert a 6-bit color component to 8 bit 30/// Convert a 6-bit color component to 8 bit
31constexpr u8 Convert6To8(u8 value) { 31[[nodiscard]] constexpr u8 Convert6To8(u8 value) {
32 return (value << 2) | (value >> 4); 32 return (value << 2) | (value >> 4);
33} 33}
34 34
35/// Convert a 8-bit color component to 1 bit 35/// Convert a 8-bit color component to 1 bit
36constexpr u8 Convert8To1(u8 value) { 36[[nodiscard]] constexpr u8 Convert8To1(u8 value) {
37 return value >> 7; 37 return value >> 7;
38} 38}
39 39
40/// Convert a 8-bit color component to 4 bit 40/// Convert a 8-bit color component to 4 bit
41constexpr u8 Convert8To4(u8 value) { 41[[nodiscard]] constexpr u8 Convert8To4(u8 value) {
42 return value >> 4; 42 return value >> 4;
43} 43}
44 44
45/// Convert a 8-bit color component to 5 bit 45/// Convert a 8-bit color component to 5 bit
46constexpr u8 Convert8To5(u8 value) { 46[[nodiscard]] constexpr u8 Convert8To5(u8 value) {
47 return value >> 3; 47 return value >> 3;
48} 48}
49 49
50/// Convert a 8-bit color component to 6 bit 50/// Convert a 8-bit color component to 6 bit
51constexpr u8 Convert8To6(u8 value) { 51[[nodiscard]] constexpr u8 Convert8To6(u8 value) {
52 return value >> 2; 52 return value >> 2;
53} 53}
54 54
@@ -57,7 +57,7 @@ constexpr u8 Convert8To6(u8 value) {
57 * @param bytes Pointer to encoded source color 57 * @param bytes Pointer to encoded source color
58 * @return Result color decoded as Common::Vec4<u8> 58 * @return Result color decoded as Common::Vec4<u8>
59 */ 59 */
60inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) { 60[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
61 return {bytes[3], bytes[2], bytes[1], bytes[0]}; 61 return {bytes[3], bytes[2], bytes[1], bytes[0]};
62} 62}
63 63
@@ -66,7 +66,7 @@ inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
66 * @param bytes Pointer to encoded source color 66 * @param bytes Pointer to encoded source color
67 * @return Result color decoded as Common::Vec4<u8> 67 * @return Result color decoded as Common::Vec4<u8>
68 */ 68 */
69inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) { 69[[nodiscard]] inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
70 return {bytes[2], bytes[1], bytes[0], 255}; 70 return {bytes[2], bytes[1], bytes[0], 255};
71} 71}
72 72
@@ -75,7 +75,7 @@ inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
75 * @param bytes Pointer to encoded source color 75 * @param bytes Pointer to encoded source color
76 * @return Result color decoded as Common::Vec4<u8> 76 * @return Result color decoded as Common::Vec4<u8>
77 */ 77 */
78inline Common::Vec4<u8> DecodeRG8(const u8* bytes) { 78[[nodiscard]] inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
79 return {bytes[1], bytes[0], 0, 255}; 79 return {bytes[1], bytes[0], 0, 255};
80} 80}
81 81
@@ -84,7 +84,7 @@ inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
84 * @param bytes Pointer to encoded source color 84 * @param bytes Pointer to encoded source color
85 * @return Result color decoded as Common::Vec4<u8> 85 * @return Result color decoded as Common::Vec4<u8>
86 */ 86 */
87inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) { 87[[nodiscard]] inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
88 u16_le pixel; 88 u16_le pixel;
89 std::memcpy(&pixel, bytes, sizeof(pixel)); 89 std::memcpy(&pixel, bytes, sizeof(pixel));
90 return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F), 90 return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
@@ -96,7 +96,7 @@ inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
96 * @param bytes Pointer to encoded source color 96 * @param bytes Pointer to encoded source color
97 * @return Result color decoded as Common::Vec4<u8> 97 * @return Result color decoded as Common::Vec4<u8>
98 */ 98 */
99inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) { 99[[nodiscard]] inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
100 u16_le pixel; 100 u16_le pixel;
101 std::memcpy(&pixel, bytes, sizeof(pixel)); 101 std::memcpy(&pixel, bytes, sizeof(pixel));
102 return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F), 102 return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
@@ -108,7 +108,7 @@ inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
108 * @param bytes Pointer to encoded source color 108 * @param bytes Pointer to encoded source color
109 * @return Result color decoded as Common::Vec4<u8> 109 * @return Result color decoded as Common::Vec4<u8>
110 */ 110 */
111inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) { 111[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
112 u16_le pixel; 112 u16_le pixel;
113 std::memcpy(&pixel, bytes, sizeof(pixel)); 113 std::memcpy(&pixel, bytes, sizeof(pixel));
114 return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF), 114 return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
@@ -120,7 +120,7 @@ inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
120 * @param bytes Pointer to encoded source value 120 * @param bytes Pointer to encoded source value
121 * @return Depth value as an u32 121 * @return Depth value as an u32
122 */ 122 */
123inline u32 DecodeD16(const u8* bytes) { 123[[nodiscard]] inline u32 DecodeD16(const u8* bytes) {
124 u16_le data; 124 u16_le data;
125 std::memcpy(&data, bytes, sizeof(data)); 125 std::memcpy(&data, bytes, sizeof(data));
126 return data; 126 return data;
@@ -131,7 +131,7 @@ inline u32 DecodeD16(const u8* bytes) {
131 * @param bytes Pointer to encoded source value 131 * @param bytes Pointer to encoded source value
132 * @return Depth value as an u32 132 * @return Depth value as an u32
133 */ 133 */
134inline u32 DecodeD24(const u8* bytes) { 134[[nodiscard]] inline u32 DecodeD24(const u8* bytes) {
135 return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]; 135 return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
136} 136}
137 137
@@ -140,7 +140,7 @@ inline u32 DecodeD24(const u8* bytes) {
140 * @param bytes Pointer to encoded source values 140 * @param bytes Pointer to encoded source values
141 * @return Resulting values stored as a Common::Vec2 141 * @return Resulting values stored as a Common::Vec2
142 */ 142 */
143inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) { 143[[nodiscard]] inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) {
144 return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]}; 144 return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
145} 145}
146 146
@@ -268,4 +268,4 @@ inline void EncodeX24S8(u8 stencil, u8* bytes) {
268 bytes[3] = stencil; 268 bytes[3] = stencil;
269} 269}
270 270
271} // namespace Color 271} // namespace Common::Color
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 88cf5250a..98421bced 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -53,14 +53,14 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
53// Call directly after the command or use the error num. 53// Call directly after the command or use the error num.
54// This function might change the error code. 54// This function might change the error code.
55// Defined in Misc.cpp. 55// Defined in Misc.cpp.
56std::string GetLastErrorMsg(); 56[[nodiscard]] std::string GetLastErrorMsg();
57 57
58#define DECLARE_ENUM_FLAG_OPERATORS(type) \ 58#define DECLARE_ENUM_FLAG_OPERATORS(type) \
59 constexpr type operator|(type a, type b) noexcept { \ 59 [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
60 using T = std::underlying_type_t<type>; \ 60 using T = std::underlying_type_t<type>; \
61 return static_cast<type>(static_cast<T>(a) | static_cast<T>(b)); \ 61 return static_cast<type>(static_cast<T>(a) | static_cast<T>(b)); \
62 } \ 62 } \
63 constexpr type operator&(type a, type b) noexcept { \ 63 [[nodiscard]] constexpr type operator&(type a, type b) noexcept { \
64 using T = std::underlying_type_t<type>; \ 64 using T = std::underlying_type_t<type>; \
65 return static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \ 65 return static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \
66 } \ 66 } \
@@ -74,22 +74,22 @@ std::string GetLastErrorMsg();
74 a = static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \ 74 a = static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \
75 return a; \ 75 return a; \
76 } \ 76 } \
77 constexpr type operator~(type key) noexcept { \ 77 [[nodiscard]] constexpr type operator~(type key) noexcept { \
78 using T = std::underlying_type_t<type>; \ 78 using T = std::underlying_type_t<type>; \
79 return static_cast<type>(~static_cast<T>(key)); \ 79 return static_cast<type>(~static_cast<T>(key)); \
80 } \ 80 } \
81 constexpr bool True(type key) noexcept { \ 81 [[nodiscard]] constexpr bool True(type key) noexcept { \
82 using T = std::underlying_type_t<type>; \ 82 using T = std::underlying_type_t<type>; \
83 return static_cast<T>(key) != 0; \ 83 return static_cast<T>(key) != 0; \
84 } \ 84 } \
85 constexpr bool False(type key) noexcept { \ 85 [[nodiscard]] constexpr bool False(type key) noexcept { \
86 using T = std::underlying_type_t<type>; \ 86 using T = std::underlying_type_t<type>; \
87 return static_cast<T>(key) == 0; \ 87 return static_cast<T>(key) == 0; \
88 } 88 }
89 89
90namespace Common { 90namespace Common {
91 91
92constexpr u32 MakeMagic(char a, char b, char c, char d) { 92[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) {
93 return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24; 93 return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24;
94} 94}
95 95
diff --git a/src/common/concepts.h b/src/common/concepts.h
index db5fb373d..5bef3ad67 100644
--- a/src/common/concepts.h
+++ b/src/common/concepts.h
@@ -4,10 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7namespace Common {
8
9#include <type_traits> 7#include <type_traits>
10 8
9namespace Common {
10
11// Check if type is like an STL container 11// Check if type is like an STL container
12template <typename T> 12template <typename T>
13concept IsSTLContainer = requires(T t) { 13concept IsSTLContainer = requires(T t) {
@@ -23,10 +23,12 @@ concept IsSTLContainer = requires(T t) {
23 t.size(); 23 t.size();
24}; 24};
25 25
26// Check if type T is derived from T2 26// TODO: Replace with std::derived_from when the <concepts> header
27template <typename T, typename T2> 27// is available on all supported platforms.
28concept IsBaseOf = requires { 28template <typename Derived, typename Base>
29 std::is_base_of_v<T, T2>; 29concept DerivedFrom = requires {
30 std::is_base_of_v<Base, Derived>;
31 std::is_convertible_v<const volatile Derived*, const volatile Base*>;
30}; 32};
31 33
32} // namespace Common 34} // namespace Common
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp
index f268d6021..f2b4939df 100644
--- a/src/common/detached_tasks.cpp
+++ b/src/common/detached_tasks.cpp
@@ -34,8 +34,7 @@ void DetachedTasks::AddTask(std::function<void()> task) {
34 std::unique_lock lock{instance->mutex}; 34 std::unique_lock lock{instance->mutex};
35 --instance->count; 35 --instance->count;
36 std::notify_all_at_thread_exit(instance->cv, std::move(lock)); 36 std::notify_all_at_thread_exit(instance->cv, std::move(lock));
37 }) 37 }).detach();
38 .detach();
39} 38}
40 39
41} // namespace Common 40} // namespace Common
diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp
index 7ab54e9e4..7f0a10521 100644
--- a/src/common/dynamic_library.cpp
+++ b/src/common/dynamic_library.cpp
@@ -21,7 +21,7 @@ namespace Common {
21DynamicLibrary::DynamicLibrary() = default; 21DynamicLibrary::DynamicLibrary() = default;
22 22
23DynamicLibrary::DynamicLibrary(const char* filename) { 23DynamicLibrary::DynamicLibrary(const char* filename) {
24 Open(filename); 24 void(Open(filename));
25} 25}
26 26
27DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept 27DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept
diff --git a/src/common/dynamic_library.h b/src/common/dynamic_library.h
index 2a06372fd..3512da940 100644
--- a/src/common/dynamic_library.h
+++ b/src/common/dynamic_library.h
@@ -33,7 +33,7 @@ public:
33 ~DynamicLibrary(); 33 ~DynamicLibrary();
34 34
35 /// Returns the specified library name with the platform-specific suffix added. 35 /// Returns the specified library name with the platform-specific suffix added.
36 static std::string GetUnprefixedFilename(const char* filename); 36 [[nodiscard]] static std::string GetUnprefixedFilename(const char* filename);
37 37
38 /// Returns the specified library name in platform-specific format. 38 /// Returns the specified library name in platform-specific format.
39 /// Major/minor versions will not be included if set to -1. 39 /// Major/minor versions will not be included if set to -1.
@@ -41,28 +41,29 @@ public:
41 /// Windows: LIBNAME-MAJOR-MINOR.dll 41 /// Windows: LIBNAME-MAJOR-MINOR.dll
42 /// Linux: libLIBNAME.so.MAJOR.MINOR 42 /// Linux: libLIBNAME.so.MAJOR.MINOR
43 /// Mac: libLIBNAME.MAJOR.MINOR.dylib 43 /// Mac: libLIBNAME.MAJOR.MINOR.dylib
44 static std::string GetVersionedFilename(const char* libname, int major = -1, int minor = -1); 44 [[nodiscard]] static std::string GetVersionedFilename(const char* libname, int major = -1,
45 int minor = -1);
45 46
46 /// Returns true if a module is loaded, otherwise false. 47 /// Returns true if a module is loaded, otherwise false.
47 bool IsOpen() const { 48 [[nodiscard]] bool IsOpen() const {
48 return handle != nullptr; 49 return handle != nullptr;
49 } 50 }
50 51
51 /// Loads (or replaces) the handle with the specified library file name. 52 /// Loads (or replaces) the handle with the specified library file name.
52 /// Returns true if the library was loaded and can be used. 53 /// Returns true if the library was loaded and can be used.
53 bool Open(const char* filename); 54 [[nodiscard]] bool Open(const char* filename);
54 55
55 /// Unloads the library, any function pointers from this library are no longer valid. 56 /// Unloads the library, any function pointers from this library are no longer valid.
56 void Close(); 57 void Close();
57 58
58 /// Returns the address of the specified symbol (function or variable) as an untyped pointer. 59 /// Returns the address of the specified symbol (function or variable) as an untyped pointer.
59 /// If the specified symbol does not exist in this library, nullptr is returned. 60 /// If the specified symbol does not exist in this library, nullptr is returned.
60 void* GetSymbolAddress(const char* name) const; 61 [[nodiscard]] void* GetSymbolAddress(const char* name) const;
61 62
62 /// Obtains the address of the specified symbol, automatically casting to the correct type. 63 /// Obtains the address of the specified symbol, automatically casting to the correct type.
63 /// Returns true if the symbol was found and assigned, otherwise false. 64 /// Returns true if the symbol was found and assigned, otherwise false.
64 template <typename T> 65 template <typename T>
65 bool GetSymbol(const char* name, T* ptr) const { 66 [[nodiscard]] bool GetSymbol(const char* name, T* ptr) const {
66 *ptr = reinterpret_cast<T>(GetSymbolAddress(name)); 67 *ptr = reinterpret_cast<T>(GetSymbolAddress(name));
67 return *ptr != nullptr; 68 return *ptr != nullptr;
68 } 69 }
diff --git a/src/common/fiber.h b/src/common/fiber.h
index dafc1100e..89dde5e36 100644
--- a/src/common/fiber.h
+++ b/src/common/fiber.h
@@ -47,7 +47,7 @@ public:
47 /// Yields control from Fiber 'from' to Fiber 'to' 47 /// Yields control from Fiber 'from' to Fiber 'to'
48 /// Fiber 'from' must be the currently running fiber. 48 /// Fiber 'from' must be the currently running fiber.
49 static void YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to); 49 static void YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to);
50 static std::shared_ptr<Fiber> ThreadToFiber(); 50 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
51 51
52 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter); 52 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter);
53 53
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 4ede9f72c..16c3713e0 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -74,7 +74,7 @@
74// This namespace has various generic functions related to files and paths. 74// This namespace has various generic functions related to files and paths.
75// The code still needs a ton of cleanup. 75// The code still needs a ton of cleanup.
76// REMEMBER: strdup considered harmful! 76// REMEMBER: strdup considered harmful!
77namespace FileUtil { 77namespace Common::FS {
78 78
79// Remove any ending forward slashes from directory paths 79// Remove any ending forward slashes from directory paths
80// Modifies argument. 80// Modifies argument.
@@ -196,7 +196,7 @@ bool CreateFullPath(const std::string& fullPath) {
196 int panicCounter = 100; 196 int panicCounter = 100;
197 LOG_TRACE(Common_Filesystem, "path {}", fullPath); 197 LOG_TRACE(Common_Filesystem, "path {}", fullPath);
198 198
199 if (FileUtil::Exists(fullPath)) { 199 if (Exists(fullPath)) {
200 LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath); 200 LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath);
201 return true; 201 return true;
202 } 202 }
@@ -212,7 +212,7 @@ bool CreateFullPath(const std::string& fullPath) {
212 212
213 // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") 213 // Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
214 std::string const subPath(fullPath.substr(0, position + 1)); 214 std::string const subPath(fullPath.substr(0, position + 1));
215 if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) { 215 if (!IsDirectory(subPath) && !CreateDir(subPath)) {
216 LOG_ERROR(Common, "CreateFullPath: directory creation failed"); 216 LOG_ERROR(Common, "CreateFullPath: directory creation failed");
217 return false; 217 return false;
218 } 218 }
@@ -231,7 +231,7 @@ bool DeleteDir(const std::string& filename) {
231 LOG_TRACE(Common_Filesystem, "directory {}", filename); 231 LOG_TRACE(Common_Filesystem, "directory {}", filename);
232 232
233 // check if a directory 233 // check if a directory
234 if (!FileUtil::IsDirectory(filename)) { 234 if (!IsDirectory(filename)) {
235 LOG_ERROR(Common_Filesystem, "Not a directory {}", filename); 235 LOG_ERROR(Common_Filesystem, "Not a directory {}", filename);
236 return false; 236 return false;
237 } 237 }
@@ -371,7 +371,7 @@ u64 GetSize(FILE* f) {
371bool CreateEmptyFile(const std::string& filename) { 371bool CreateEmptyFile(const std::string& filename) {
372 LOG_TRACE(Common_Filesystem, "{}", filename); 372 LOG_TRACE(Common_Filesystem, "{}", filename);
373 373
374 if (!FileUtil::IOFile(filename, "wb").IsOpen()) { 374 if (!IOFile(filename, "wb").IsOpen()) {
375 LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); 375 LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg());
376 return false; 376 return false;
377 } 377 }
@@ -488,29 +488,34 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
488 return false; 488 return false;
489 489
490 // Delete the outermost directory 490 // Delete the outermost directory
491 FileUtil::DeleteDir(directory); 491 DeleteDir(directory);
492 return true; 492 return true;
493} 493}
494 494
495void CopyDir(const std::string& source_path, const std::string& dest_path) { 495void CopyDir(const std::string& source_path, const std::string& dest_path) {
496#ifndef _WIN32 496#ifndef _WIN32
497 if (source_path == dest_path) 497 if (source_path == dest_path) {
498 return; 498 return;
499 if (!FileUtil::Exists(source_path)) 499 }
500 if (!Exists(source_path)) {
500 return; 501 return;
501 if (!FileUtil::Exists(dest_path)) 502 }
502 FileUtil::CreateFullPath(dest_path); 503 if (!Exists(dest_path)) {
504 CreateFullPath(dest_path);
505 }
503 506
504 DIR* dirp = opendir(source_path.c_str()); 507 DIR* dirp = opendir(source_path.c_str());
505 if (!dirp) 508 if (!dirp) {
506 return; 509 return;
510 }
507 511
508 while (struct dirent* result = readdir(dirp)) { 512 while (struct dirent* result = readdir(dirp)) {
509 const std::string virtualName(result->d_name); 513 const std::string virtualName(result->d_name);
510 // check for "." and ".." 514 // check for "." and ".."
511 if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || 515 if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
512 ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0'))) 516 ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0'))) {
513 continue; 517 continue;
518 }
514 519
515 std::string source, dest; 520 std::string source, dest;
516 source = source_path + virtualName; 521 source = source_path + virtualName;
@@ -518,11 +523,13 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) {
518 if (IsDirectory(source)) { 523 if (IsDirectory(source)) {
519 source += '/'; 524 source += '/';
520 dest += '/'; 525 dest += '/';
521 if (!FileUtil::Exists(dest)) 526 if (!Exists(dest)) {
522 FileUtil::CreateFullPath(dest); 527 CreateFullPath(dest);
528 }
523 CopyDir(source, dest); 529 CopyDir(source, dest);
524 } else if (!FileUtil::Exists(dest)) 530 } else if (!Exists(dest)) {
525 FileUtil::Copy(source, dest); 531 Copy(source, dest);
532 }
526 } 533 }
527 closedir(dirp); 534 closedir(dirp);
528#endif 535#endif
@@ -538,7 +545,7 @@ std::optional<std::string> GetCurrentDir() {
538 if (!dir) { 545 if (!dir) {
539#endif 546#endif
540 LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); 547 LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg());
541 return {}; 548 return std::nullopt;
542 } 549 }
543#ifdef _WIN32 550#ifdef _WIN32
544 std::string strDir = Common::UTF16ToUTF8(dir); 551 std::string strDir = Common::UTF16ToUTF8(dir);
@@ -546,7 +553,7 @@ std::optional<std::string> GetCurrentDir() {
546 std::string strDir = dir; 553 std::string strDir = dir;
547#endif 554#endif
548 free(dir); 555 free(dir);
549 return strDir; 556 return std::move(strDir);
550} 557}
551 558
552bool SetCurrentDir(const std::string& directory) { 559bool SetCurrentDir(const std::string& directory) {
@@ -668,7 +675,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
668 if (user_path.empty()) { 675 if (user_path.empty()) {
669#ifdef _WIN32 676#ifdef _WIN32
670 user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; 677 user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
671 if (!FileUtil::IsDirectory(user_path)) { 678 if (!IsDirectory(user_path)) {
672 user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP; 679 user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
673 } else { 680 } else {
674 LOG_INFO(Common_Filesystem, "Using the local user directory"); 681 LOG_INFO(Common_Filesystem, "Using the local user directory");
@@ -677,7 +684,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
677 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP); 684 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
678 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP); 685 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
679#else 686#else
680 if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) { 687 if (Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
681 user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; 688 user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
682 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP); 689 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
683 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP); 690 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
@@ -704,7 +711,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
704 } 711 }
705 712
706 if (!new_path.empty()) { 713 if (!new_path.empty()) {
707 if (!FileUtil::IsDirectory(new_path)) { 714 if (!IsDirectory(new_path)) {
708 LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path); 715 LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path);
709 return paths[path]; 716 return paths[path];
710 } else { 717 } else {
@@ -902,10 +909,10 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
902 return std::string(RemoveTrailingSlash(path)); 909 return std::string(RemoveTrailingSlash(path));
903} 910}
904 911
905IOFile::IOFile() {} 912IOFile::IOFile() = default;
906 913
907IOFile::IOFile(const std::string& filename, const char openmode[], int flags) { 914IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
908 Open(filename, openmode, flags); 915 void(Open(filename, openmode, flags));
909} 916}
910 917
911IOFile::~IOFile() { 918IOFile::~IOFile() {
@@ -946,17 +953,18 @@ bool IOFile::Open(const std::string& filename, const char openmode[], int flags)
946} 953}
947 954
948bool IOFile::Close() { 955bool IOFile::Close() {
949 if (!IsOpen() || 0 != std::fclose(m_file)) 956 if (!IsOpen() || 0 != std::fclose(m_file)) {
950 return false; 957 return false;
958 }
951 959
952 m_file = nullptr; 960 m_file = nullptr;
953 return true; 961 return true;
954} 962}
955 963
956u64 IOFile::GetSize() const { 964u64 IOFile::GetSize() const {
957 if (IsOpen()) 965 if (IsOpen()) {
958 return FileUtil::GetSize(m_file); 966 return FS::GetSize(m_file);
959 967 }
960 return 0; 968 return 0;
961} 969}
962 970
@@ -965,9 +973,9 @@ bool IOFile::Seek(s64 off, int origin) const {
965} 973}
966 974
967u64 IOFile::Tell() const { 975u64 IOFile::Tell() const {
968 if (IsOpen()) 976 if (IsOpen()) {
969 return ftello(m_file); 977 return ftello(m_file);
970 978 }
971 return std::numeric_limits<u64>::max(); 979 return std::numeric_limits<u64>::max();
972} 980}
973 981
@@ -1016,4 +1024,4 @@ bool IOFile::Resize(u64 size) {
1016 ; 1024 ;
1017} 1025}
1018 1026
1019} // namespace FileUtil 1027} // namespace Common::FS
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 187b93161..8b587320f 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -19,7 +19,7 @@
19#include "common/string_util.h" 19#include "common/string_util.h"
20#endif 20#endif
21 21
22namespace FileUtil { 22namespace Common::FS {
23 23
24// User paths for GetUserPath 24// User paths for GetUserPath
25enum class UserPath { 25enum class UserPath {
@@ -48,19 +48,19 @@ struct FSTEntry {
48}; 48};
49 49
50// Returns true if file filename exists 50// Returns true if file filename exists
51bool Exists(const std::string& filename); 51[[nodiscard]] bool Exists(const std::string& filename);
52 52
53// Returns true if filename is a directory 53// Returns true if filename is a directory
54bool IsDirectory(const std::string& filename); 54[[nodiscard]] bool IsDirectory(const std::string& filename);
55 55
56// Returns the size of filename (64bit) 56// Returns the size of filename (64bit)
57u64 GetSize(const std::string& filename); 57[[nodiscard]] u64 GetSize(const std::string& filename);
58 58
59// Overloaded GetSize, accepts file descriptor 59// Overloaded GetSize, accepts file descriptor
60u64 GetSize(const int fd); 60[[nodiscard]] u64 GetSize(int fd);
61 61
62// Overloaded GetSize, accepts FILE* 62// Overloaded GetSize, accepts FILE*
63u64 GetSize(FILE* f); 63[[nodiscard]] u64 GetSize(FILE* f);
64 64
65// Returns true if successful, or path already exists. 65// Returns true if successful, or path already exists.
66bool CreateDir(const std::string& filename); 66bool CreateDir(const std::string& filename);
@@ -120,7 +120,7 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
120bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256); 120bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256);
121 121
122// Returns the current directory 122// Returns the current directory
123std::optional<std::string> GetCurrentDir(); 123[[nodiscard]] std::optional<std::string> GetCurrentDir();
124 124
125// Create directory and copy contents (does not overwrite existing files) 125// Create directory and copy contents (does not overwrite existing files)
126void CopyDir(const std::string& source_path, const std::string& dest_path); 126void CopyDir(const std::string& source_path, const std::string& dest_path);
@@ -132,20 +132,20 @@ bool SetCurrentDir(const std::string& directory);
132// directory. To be used in "multi-user" mode (that is, installed). 132// directory. To be used in "multi-user" mode (that is, installed).
133const std::string& GetUserPath(UserPath path, const std::string& new_path = ""); 133const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
134 134
135std::string GetHactoolConfigurationPath(); 135[[nodiscard]] std::string GetHactoolConfigurationPath();
136 136
137std::string GetNANDRegistrationDir(bool system = false); 137[[nodiscard]] std::string GetNANDRegistrationDir(bool system = false);
138 138
139// Returns the path to where the sys file are 139// Returns the path to where the sys file are
140std::string GetSysDirectory(); 140[[nodiscard]] std::string GetSysDirectory();
141 141
142#ifdef __APPLE__ 142#ifdef __APPLE__
143std::string GetBundleDirectory(); 143[[nodiscard]] std::string GetBundleDirectory();
144#endif 144#endif
145 145
146#ifdef _WIN32 146#ifdef _WIN32
147const std::string& GetExeDirectory(); 147[[nodiscard]] const std::string& GetExeDirectory();
148std::string AppDataRoamingDirectory(); 148[[nodiscard]] std::string AppDataRoamingDirectory();
149#endif 149#endif
150 150
151std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str); 151std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str);
@@ -164,38 +164,55 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
164 164
165// Splits the path on '/' or '\' and put the components into a vector 165// Splits the path on '/' or '\' and put the components into a vector
166// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" } 166// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
167std::vector<std::string> SplitPathComponents(std::string_view filename); 167[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename);
168 168
169// Gets all of the text up to the last '/' or '\' in the path. 169// Gets all of the text up to the last '/' or '\' in the path.
170std::string_view GetParentPath(std::string_view path); 170[[nodiscard]] std::string_view GetParentPath(std::string_view path);
171 171
172// Gets all of the text after the first '/' or '\' in the path. 172// Gets all of the text after the first '/' or '\' in the path.
173std::string_view GetPathWithoutTop(std::string_view path); 173[[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path);
174 174
175// Gets the filename of the path 175// Gets the filename of the path
176std::string_view GetFilename(std::string_view path); 176[[nodiscard]] std::string_view GetFilename(std::string_view path);
177 177
178// Gets the extension of the filename 178// Gets the extension of the filename
179std::string_view GetExtensionFromFilename(std::string_view name); 179[[nodiscard]] std::string_view GetExtensionFromFilename(std::string_view name);
180 180
181// Removes the final '/' or '\' if one exists 181// Removes the final '/' or '\' if one exists
182std::string_view RemoveTrailingSlash(std::string_view path); 182[[nodiscard]] std::string_view RemoveTrailingSlash(std::string_view path);
183 183
184// Creates a new vector containing indices [first, last) from the original. 184// Creates a new vector containing indices [first, last) from the original.
185template <typename T> 185template <typename T>
186std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, std::size_t last) { 186[[nodiscard]] std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first,
187 if (first >= last) 187 std::size_t last) {
188 if (first >= last) {
188 return {}; 189 return {};
190 }
189 last = std::min<std::size_t>(last, vector.size()); 191 last = std::min<std::size_t>(last, vector.size());
190 return std::vector<T>(vector.begin() + first, vector.begin() + first + last); 192 return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
191} 193}
192 194
193enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault }; 195enum class DirectorySeparator {
196 ForwardSlash,
197 BackwardSlash,
198 PlatformDefault,
199};
194 200
195// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\' 201// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
196// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows 202// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
197std::string SanitizePath(std::string_view path, 203[[nodiscard]] std::string SanitizePath(
198 DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash); 204 std::string_view path,
205 DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
206
207// To deal with Windows being dumb at Unicode
208template <typename T>
209void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
210#ifdef _MSC_VER
211 fstream.open(Common::UTF8ToUTF16W(filename), openmode);
212#else
213 fstream.open(filename, openmode);
214#endif
215}
199 216
200// simple wrapper for cstdlib file functions to 217// simple wrapper for cstdlib file functions to
201// hopefully will make error checking easier 218// hopefully will make error checking easier
@@ -215,7 +232,7 @@ public:
215 232
216 void Swap(IOFile& other) noexcept; 233 void Swap(IOFile& other) noexcept;
217 234
218 bool Open(const std::string& filename, const char openmode[], int flags = 0); 235 [[nodiscard]] bool Open(const std::string& filename, const char openmode[], int flags = 0);
219 bool Close(); 236 bool Close();
220 237
221 template <typename T> 238 template <typename T>
@@ -256,13 +273,13 @@ public:
256 return WriteArray(str.data(), str.length()); 273 return WriteArray(str.data(), str.length());
257 } 274 }
258 275
259 bool IsOpen() const { 276 [[nodiscard]] bool IsOpen() const {
260 return nullptr != m_file; 277 return nullptr != m_file;
261 } 278 }
262 279
263 bool Seek(s64 off, int origin) const; 280 bool Seek(s64 off, int origin) const;
264 u64 Tell() const; 281 [[nodiscard]] u64 Tell() const;
265 u64 GetSize() const; 282 [[nodiscard]] u64 GetSize() const;
266 bool Resize(u64 size); 283 bool Resize(u64 size);
267 bool Flush(); 284 bool Flush();
268 285
@@ -278,14 +295,4 @@ private:
278 std::FILE* m_file = nullptr; 295 std::FILE* m_file = nullptr;
279}; 296};
280 297
281} // namespace FileUtil 298} // namespace Common::FS
282
283// To deal with Windows being dumb at unicode:
284template <typename T>
285void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
286#ifdef _MSC_VER
287 fstream.open(Common::UTF8ToUTF16W(filename), openmode);
288#else
289 fstream.open(filename, openmode);
290#endif
291}
diff --git a/src/common/hash.h b/src/common/hash.h
index b2538f3ea..298930702 100644
--- a/src/common/hash.h
+++ b/src/common/hash.h
@@ -5,36 +5,11 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <cstring>
9#include <utility> 8#include <utility>
10#include <boost/functional/hash.hpp> 9#include <boost/functional/hash.hpp>
11#include "common/cityhash.h"
12#include "common/common_types.h"
13 10
14namespace Common { 11namespace Common {
15 12
16/**
17 * Computes a 64-bit hash over the specified block of data
18 * @param data Block of data to compute hash over
19 * @param len Length of data (in bytes) to compute hash over
20 * @returns 64-bit hash value that was computed over the data block
21 */
22static inline u64 ComputeHash64(const void* data, std::size_t len) {
23 return CityHash64(static_cast<const char*>(data), len);
24}
25
26/**
27 * Computes a 64-bit hash of a struct. In addition to being trivially copyable, it is also critical
28 * that either the struct includes no padding, or that any padding is initialized to a known value
29 * by memsetting the struct to 0 before filling it in.
30 */
31template <typename T>
32static inline u64 ComputeStructHash64(const T& data) {
33 static_assert(std::is_trivially_copyable_v<T>,
34 "Type passed to ComputeStructHash64 must be trivially copyable");
35 return ComputeHash64(&data, sizeof(data));
36}
37
38struct PairHash { 13struct PairHash {
39 template <class T1, class T2> 14 template <class T1, class T2>
40 std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept { 15 std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept {
diff --git a/src/common/hex_util.cpp b/src/common/hex_util.cpp
index c2f6cf0f6..74f52dd11 100644
--- a/src/common/hex_util.cpp
+++ b/src/common/hex_util.cpp
@@ -3,21 +3,9 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/hex_util.h" 5#include "common/hex_util.h"
6#include "common/logging/log.h"
7 6
8namespace Common { 7namespace Common {
9 8
10u8 ToHexNibble(char c1) {
11 if (c1 >= 65 && c1 <= 70)
12 return c1 - 55;
13 if (c1 >= 97 && c1 <= 102)
14 return c1 - 87;
15 if (c1 >= 48 && c1 <= 57)
16 return c1 - 48;
17 LOG_ERROR(Common, "Invalid hex digit: 0x{:02X}", c1);
18 return 0;
19}
20
21std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) { 9std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
22 std::vector<u8> out(str.size() / 2); 10 std::vector<u8> out(str.size() / 2);
23 if (little_endian) { 11 if (little_endian) {
@@ -30,26 +18,4 @@ std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
30 return out; 18 return out;
31} 19}
32 20
33std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
34 if (len != 32) {
35 LOG_ERROR(Common,
36 "Attempting to parse string to array that is not of correct size (expected=32, "
37 "actual={}).",
38 len);
39 return {};
40 }
41 return HexStringToArray<16>(str);
42}
43
44std::array<u8, 32> operator""_array32(const char* str, std::size_t len) {
45 if (len != 64) {
46 LOG_ERROR(Common,
47 "Attempting to parse string to array that is not of correct size (expected=64, "
48 "actual={}).",
49 len);
50 return {};
51 }
52 return HexStringToArray<32>(str);
53}
54
55} // namespace Common 21} // namespace Common
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index bb4736f96..120f1a5e6 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -14,25 +14,37 @@
14 14
15namespace Common { 15namespace Common {
16 16
17u8 ToHexNibble(char c1); 17[[nodiscard]] constexpr u8 ToHexNibble(char c) {
18 if (c >= 65 && c <= 70) {
19 return c - 55;
20 }
21
22 if (c >= 97 && c <= 102) {
23 return c - 87;
24 }
25
26 return c - 48;
27}
18 28
19std::vector<u8> HexStringToVector(std::string_view str, bool little_endian); 29[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
20 30
21template <std::size_t Size, bool le = false> 31template <std::size_t Size, bool le = false>
22std::array<u8, Size> HexStringToArray(std::string_view str) { 32[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
23 std::array<u8, Size> out{}; 33 std::array<u8, Size> out{};
24 if constexpr (le) { 34 if constexpr (le) {
25 for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) 35 for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
26 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); 36 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
37 }
27 } else { 38 } else {
28 for (std::size_t i = 0; i < 2 * Size; i += 2) 39 for (std::size_t i = 0; i < 2 * Size; i += 2) {
29 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); 40 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
41 }
30 } 42 }
31 return out; 43 return out;
32} 44}
33 45
34template <typename ContiguousContainer> 46template <typename ContiguousContainer>
35std::string HexToString(const ContiguousContainer& data, bool upper = true) { 47[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
36 static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>, 48 static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
37 "Underlying type within the contiguous container must be u8."); 49 "Underlying type within the contiguous container must be u8.");
38 50
@@ -48,7 +60,12 @@ std::string HexToString(const ContiguousContainer& data, bool upper = true) {
48 return out; 60 return out;
49} 61}
50 62
51std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len); 63[[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[17]) {
52std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len); 64 return HexStringToArray<16>(data);
65}
66
67[[nodiscard]] constexpr std::array<u8, 32> AsArray(const char (&data)[65]) {
68 return HexStringToArray<32>(data);
69}
53 70
54} // namespace Common 71} // namespace Common
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index e5d702568..da1c2f185 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -94,7 +94,7 @@ public:
94 void Write(const Entry& entry) override; 94 void Write(const Entry& entry) override;
95 95
96private: 96private:
97 FileUtil::IOFile file; 97 Common::FS::IOFile file;
98 std::size_t bytes_written; 98 std::size_t bytes_written;
99}; 99};
100 100
diff --git a/src/common/lz4_compression.cpp b/src/common/lz4_compression.cpp
index ade6759bb..25700015a 100644
--- a/src/common/lz4_compression.cpp
+++ b/src/common/lz4_compression.cpp
@@ -14,19 +14,19 @@ std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size) {
14 ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size"); 14 ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size");
15 15
16 const auto source_size_int = static_cast<int>(source_size); 16 const auto source_size_int = static_cast<int>(source_size);
17 const int max_compressed_size = LZ4_compressBound(source_size_int); 17 const auto max_compressed_size = static_cast<std::size_t>(LZ4_compressBound(source_size_int));
18 std::vector<u8> compressed(max_compressed_size); 18 std::vector<u8> compressed(max_compressed_size);
19 19
20 const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source), 20 const int compressed_size = LZ4_compress_default(
21 reinterpret_cast<char*>(compressed.data()), 21 reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()),
22 source_size_int, max_compressed_size); 22 source_size_int, static_cast<int>(max_compressed_size));
23 23
24 if (compressed_size <= 0) { 24 if (compressed_size <= 0) {
25 // Compression failed 25 // Compression failed
26 return {}; 26 return {};
27 } 27 }
28 28
29 compressed.resize(compressed_size); 29 compressed.resize(static_cast<std::size_t>(compressed_size));
30 30
31 return compressed; 31 return compressed;
32} 32}
@@ -38,19 +38,19 @@ std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
38 compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX); 38 compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX);
39 39
40 const auto source_size_int = static_cast<int>(source_size); 40 const auto source_size_int = static_cast<int>(source_size);
41 const int max_compressed_size = LZ4_compressBound(source_size_int); 41 const auto max_compressed_size = static_cast<std::size_t>(LZ4_compressBound(source_size_int));
42 std::vector<u8> compressed(max_compressed_size); 42 std::vector<u8> compressed(max_compressed_size);
43 43
44 const int compressed_size = LZ4_compress_HC( 44 const int compressed_size = LZ4_compress_HC(
45 reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()), 45 reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()),
46 source_size_int, max_compressed_size, compression_level); 46 source_size_int, static_cast<int>(max_compressed_size), compression_level);
47 47
48 if (compressed_size <= 0) { 48 if (compressed_size <= 0) {
49 // Compression failed 49 // Compression failed
50 return {}; 50 return {};
51 } 51 }
52 52
53 compressed.resize(compressed_size); 53 compressed.resize(static_cast<std::size_t>(compressed_size));
54 54
55 return compressed; 55 return compressed;
56} 56}
diff --git a/src/common/lz4_compression.h b/src/common/lz4_compression.h
index 4c16f6e03..87a4be1b0 100644
--- a/src/common/lz4_compression.h
+++ b/src/common/lz4_compression.h
@@ -13,12 +13,12 @@ namespace Common::Compression {
13/** 13/**
14 * Compresses a source memory region with LZ4 and returns the compressed data in a vector. 14 * Compresses a source memory region with LZ4 and returns the compressed data in a vector.
15 * 15 *
16 * @param source the uncompressed source memory region. 16 * @param source The uncompressed source memory region.
17 * @param source_size the size in bytes of the uncompressed source memory region. 17 * @param source_size The size of the uncompressed source memory region.
18 * 18 *
19 * @return the compressed data. 19 * @return the compressed data.
20 */ 20 */
21std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size); 21[[nodiscard]] std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
22 22
23/** 23/**
24 * Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression 24 * Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression
@@ -26,23 +26,24 @@ std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
26 * compression level has almost no impact on decompression speed. Data compressed with LZ4HC can 26 * compression level has almost no impact on decompression speed. Data compressed with LZ4HC can
27 * also be decompressed with the default LZ4 decompression. 27 * also be decompressed with the default LZ4 decompression.
28 * 28 *
29 * @param source the uncompressed source memory region. 29 * @param source The uncompressed source memory region.
30 * @param source_size the size in bytes of the uncompressed source memory region. 30 * @param source_size The size of the uncompressed source memory region.
31 * @param compression_level the used compression level. Should be between 3 and 12. 31 * @param compression_level The used compression level. Should be between 3 and 12.
32 * 32 *
33 * @return the compressed data. 33 * @return the compressed data.
34 */ 34 */
35std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32 compression_level); 35[[nodiscard]] std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
36 s32 compression_level);
36 37
37/** 38/**
38 * Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level. 39 * Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level.
39 * 40 *
40 * @param source the uncompressed source memory region. 41 * @param source The uncompressed source memory region.
41 * @param source_size the size in bytes of the uncompressed source memory region. 42 * @param source_size The size of the uncompressed source memory region
42 * 43 *
43 * @return the compressed data. 44 * @return the compressed data.
44 */ 45 */
45std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size); 46[[nodiscard]] std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
46 47
47/** 48/**
48 * Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector. 49 * Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector.
@@ -52,6 +53,7 @@ std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
52 * 53 *
53 * @return the decompressed data. 54 * @return the decompressed data.
54 */ 55 */
55std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, std::size_t uncompressed_size); 56[[nodiscard]] std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed,
57 std::size_t uncompressed_size);
56 58
57} // namespace Common::Compression \ No newline at end of file 59} // namespace Common::Compression \ No newline at end of file
diff --git a/src/common/math_util.h b/src/common/math_util.h
index 83ef0201f..cc35c90ee 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -23,7 +23,7 @@ struct Rectangle {
23 constexpr Rectangle(T left, T top, T right, T bottom) 23 constexpr Rectangle(T left, T top, T right, T bottom)
24 : left(left), top(top), right(right), bottom(bottom) {} 24 : left(left), top(top), right(right), bottom(bottom) {}
25 25
26 T GetWidth() const { 26 [[nodiscard]] T GetWidth() const {
27 if constexpr (std::is_floating_point_v<T>) { 27 if constexpr (std::is_floating_point_v<T>) {
28 return std::abs(right - left); 28 return std::abs(right - left);
29 } else { 29 } else {
@@ -31,7 +31,7 @@ struct Rectangle {
31 } 31 }
32 } 32 }
33 33
34 T GetHeight() const { 34 [[nodiscard]] T GetHeight() const {
35 if constexpr (std::is_floating_point_v<T>) { 35 if constexpr (std::is_floating_point_v<T>) {
36 return std::abs(bottom - top); 36 return std::abs(bottom - top);
37 } else { 37 } else {
@@ -39,21 +39,21 @@ struct Rectangle {
39 } 39 }
40 } 40 }
41 41
42 Rectangle<T> TranslateX(const T x) const { 42 [[nodiscard]] Rectangle<T> TranslateX(const T x) const {
43 return Rectangle{left + x, top, right + x, bottom}; 43 return Rectangle{left + x, top, right + x, bottom};
44 } 44 }
45 45
46 Rectangle<T> TranslateY(const T y) const { 46 [[nodiscard]] Rectangle<T> TranslateY(const T y) const {
47 return Rectangle{left, top + y, right, bottom + y}; 47 return Rectangle{left, top + y, right, bottom + y};
48 } 48 }
49 49
50 Rectangle<T> Scale(const float s) const { 50 [[nodiscard]] Rectangle<T> Scale(const float s) const {
51 return Rectangle{left, top, static_cast<T>(left + GetWidth() * s), 51 return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
52 static_cast<T>(top + GetHeight() * s)}; 52 static_cast<T>(top + GetHeight() * s)};
53 } 53 }
54}; 54};
55 55
56template <typename T> 56template <typename T>
57Rectangle(T, T, T, T)->Rectangle<T>; 57Rectangle(T, T, T, T) -> Rectangle<T>;
58 58
59} // namespace Common 59} // namespace Common
diff --git a/src/common/memory_detect.h b/src/common/memory_detect.h
index a73c0f3f4..0f73751c8 100644
--- a/src/common/memory_detect.h
+++ b/src/common/memory_detect.h
@@ -17,6 +17,6 @@ struct MemoryInfo {
17 * Gets the memory info of the host system 17 * Gets the memory info of the host system
18 * @return Reference to a MemoryInfo struct with the physical and swap memory sizes in bytes 18 * @return Reference to a MemoryInfo struct with the physical and swap memory sizes in bytes
19 */ 19 */
20const MemoryInfo& GetMemInfo(); 20[[nodiscard]] const MemoryInfo& GetMemInfo();
21 21
22} // namespace Common \ No newline at end of file 22} // namespace Common \ No newline at end of file
diff --git a/src/common/multi_level_queue.h b/src/common/multi_level_queue.h
index 50acfdbf2..4b305bf40 100644
--- a/src/common/multi_level_queue.h
+++ b/src/common/multi_level_queue.h
@@ -223,15 +223,15 @@ public:
223 ListShiftForward(levels[priority], n); 223 ListShiftForward(levels[priority], n);
224 } 224 }
225 225
226 std::size_t depth() const { 226 [[nodiscard]] std::size_t depth() const {
227 return Depth; 227 return Depth;
228 } 228 }
229 229
230 std::size_t size(u32 priority) const { 230 [[nodiscard]] std::size_t size(u32 priority) const {
231 return levels[priority].size(); 231 return levels[priority].size();
232 } 232 }
233 233
234 std::size_t size() const { 234 [[nodiscard]] std::size_t size() const {
235 u64 priorities = used_priorities; 235 u64 priorities = used_priorities;
236 std::size_t size = 0; 236 std::size_t size = 0;
237 while (priorities != 0) { 237 while (priorities != 0) {
@@ -242,64 +242,64 @@ public:
242 return size; 242 return size;
243 } 243 }
244 244
245 bool empty() const { 245 [[nodiscard]] bool empty() const {
246 return used_priorities == 0; 246 return used_priorities == 0;
247 } 247 }
248 248
249 bool empty(u32 priority) const { 249 [[nodiscard]] bool empty(u32 priority) const {
250 return (used_priorities & (1ULL << priority)) == 0; 250 return (used_priorities & (1ULL << priority)) == 0;
251 } 251 }
252 252
253 u32 highest_priority_set(u32 max_priority = 0) const { 253 [[nodiscard]] u32 highest_priority_set(u32 max_priority = 0) const {
254 const u64 priorities = 254 const u64 priorities =
255 max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1)); 255 max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
256 return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities)); 256 return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
257 } 257 }
258 258
259 u32 lowest_priority_set(u32 min_priority = Depth - 1) const { 259 [[nodiscard]] u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
260 const u64 priorities = min_priority >= Depth - 1 260 const u64 priorities = min_priority >= Depth - 1
261 ? used_priorities 261 ? used_priorities
262 : (used_priorities & ((1ULL << (min_priority + 1)) - 1)); 262 : (used_priorities & ((1ULL << (min_priority + 1)) - 1));
263 return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities); 263 return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
264 } 264 }
265 265
266 const_iterator cbegin(u32 max_prio = 0) const { 266 [[nodiscard]] const_iterator cbegin(u32 max_prio = 0) const {
267 const u32 priority = highest_priority_set(max_prio); 267 const u32 priority = highest_priority_set(max_prio);
268 return priority == Depth ? cend() 268 return priority == Depth ? cend()
269 : const_iterator{*this, levels[priority].cbegin(), priority}; 269 : const_iterator{*this, levels[priority].cbegin(), priority};
270 } 270 }
271 const_iterator begin(u32 max_prio = 0) const { 271 [[nodiscard]] const_iterator begin(u32 max_prio = 0) const {
272 return cbegin(max_prio); 272 return cbegin(max_prio);
273 } 273 }
274 iterator begin(u32 max_prio = 0) { 274 [[nodiscard]] iterator begin(u32 max_prio = 0) {
275 const u32 priority = highest_priority_set(max_prio); 275 const u32 priority = highest_priority_set(max_prio);
276 return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority}; 276 return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
277 } 277 }
278 278
279 const_iterator cend(u32 min_prio = Depth - 1) const { 279 [[nodiscard]] const_iterator cend(u32 min_prio = Depth - 1) const {
280 return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1); 280 return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
281 } 281 }
282 const_iterator end(u32 min_prio = Depth - 1) const { 282 [[nodiscard]] const_iterator end(u32 min_prio = Depth - 1) const {
283 return cend(min_prio); 283 return cend(min_prio);
284 } 284 }
285 iterator end(u32 min_prio = Depth - 1) { 285 [[nodiscard]] iterator end(u32 min_prio = Depth - 1) {
286 return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1); 286 return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
287 } 287 }
288 288
289 T& front(u32 max_priority = 0) { 289 [[nodiscard]] T& front(u32 max_priority = 0) {
290 const u32 priority = highest_priority_set(max_priority); 290 const u32 priority = highest_priority_set(max_priority);
291 return levels[priority == Depth ? 0 : priority].front(); 291 return levels[priority == Depth ? 0 : priority].front();
292 } 292 }
293 const T& front(u32 max_priority = 0) const { 293 [[nodiscard]] const T& front(u32 max_priority = 0) const {
294 const u32 priority = highest_priority_set(max_priority); 294 const u32 priority = highest_priority_set(max_priority);
295 return levels[priority == Depth ? 0 : priority].front(); 295 return levels[priority == Depth ? 0 : priority].front();
296 } 296 }
297 297
298 T back(u32 min_priority = Depth - 1) { 298 [[nodiscard]] T& back(u32 min_priority = Depth - 1) {
299 const u32 priority = lowest_priority_set(min_priority); // intended 299 const u32 priority = lowest_priority_set(min_priority); // intended
300 return levels[priority == Depth ? 63 : priority].back(); 300 return levels[priority == Depth ? 63 : priority].back();
301 } 301 }
302 const T& back(u32 min_priority = Depth - 1) const { 302 [[nodiscard]] const T& back(u32 min_priority = Depth - 1) const {
303 const u32 priority = lowest_priority_set(min_priority); // intended 303 const u32 priority = lowest_priority_set(min_priority); // intended
304 return levels[priority == Depth ? 63 : priority].back(); 304 return levels[priority == Depth ? 63 : priority].back();
305 } 305 }
@@ -329,7 +329,8 @@ private:
329 in_list.splice(position, out_list, element); 329 in_list.splice(position, out_list, element);
330 } 330 }
331 331
332 static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) { 332 [[nodiscard]] static const_list_iterator ListIterateTo(const std::list<T>& list,
333 const T& element) {
333 auto it = list.cbegin(); 334 auto it = list.cbegin();
334 while (it != list.cend() && *it != element) { 335 while (it != list.cend() && *it != element) {
335 ++it; 336 ++it;
diff --git a/src/common/page_table.h b/src/common/page_table.h
index 1e8bd3187..cf5eed780 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -36,11 +36,11 @@ struct SpecialRegion {
36 36
37 MemoryHookPointer handler; 37 MemoryHookPointer handler;
38 38
39 bool operator<(const SpecialRegion& other) const { 39 [[nodiscard]] bool operator<(const SpecialRegion& other) const {
40 return std::tie(type, handler) < std::tie(other.type, other.handler); 40 return std::tie(type, handler) < std::tie(other.type, other.handler);
41 } 41 }
42 42
43 bool operator==(const SpecialRegion& other) const { 43 [[nodiscard]] bool operator==(const SpecialRegion& other) const {
44 return std::tie(type, handler) == std::tie(other.type, other.handler); 44 return std::tie(type, handler) == std::tie(other.type, other.handler);
45 } 45 }
46}; 46};
diff --git a/src/common/param_package.h b/src/common/param_package.h
index 6a0a9b656..c13e45479 100644
--- a/src/common/param_package.h
+++ b/src/common/param_package.h
@@ -19,19 +19,19 @@ public:
19 explicit ParamPackage(const std::string& serialized); 19 explicit ParamPackage(const std::string& serialized);
20 ParamPackage(std::initializer_list<DataType::value_type> list); 20 ParamPackage(std::initializer_list<DataType::value_type> list);
21 ParamPackage(const ParamPackage& other) = default; 21 ParamPackage(const ParamPackage& other) = default;
22 ParamPackage(ParamPackage&& other) = default; 22 ParamPackage(ParamPackage&& other) noexcept = default;
23 23
24 ParamPackage& operator=(const ParamPackage& other) = default; 24 ParamPackage& operator=(const ParamPackage& other) = default;
25 ParamPackage& operator=(ParamPackage&& other) = default; 25 ParamPackage& operator=(ParamPackage&& other) = default;
26 26
27 std::string Serialize() const; 27 [[nodiscard]] std::string Serialize() const;
28 std::string Get(const std::string& key, const std::string& default_value) const; 28 [[nodiscard]] std::string Get(const std::string& key, const std::string& default_value) const;
29 int Get(const std::string& key, int default_value) const; 29 [[nodiscard]] int Get(const std::string& key, int default_value) const;
30 float Get(const std::string& key, float default_value) const; 30 [[nodiscard]] float Get(const std::string& key, float default_value) const;
31 void Set(const std::string& key, std::string value); 31 void Set(const std::string& key, std::string value);
32 void Set(const std::string& key, int value); 32 void Set(const std::string& key, int value);
33 void Set(const std::string& key, float value); 33 void Set(const std::string& key, float value);
34 bool Has(const std::string& key) const; 34 [[nodiscard]] bool Has(const std::string& key) const;
35 void Erase(const std::string& key); 35 void Erase(const std::string& key);
36 void Clear(); 36 void Clear();
37 37
diff --git a/src/common/quaternion.h b/src/common/quaternion.h
index 370198ae0..da44f35cd 100644
--- a/src/common/quaternion.h
+++ b/src/common/quaternion.h
@@ -14,35 +14,36 @@ public:
14 Vec3<T> xyz; 14 Vec3<T> xyz;
15 T w{}; 15 T w{};
16 16
17 Quaternion<decltype(-T{})> Inverse() const { 17 [[nodiscard]] Quaternion<decltype(-T{})> Inverse() const {
18 return {-xyz, w}; 18 return {-xyz, w};
19 } 19 }
20 20
21 Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const { 21 [[nodiscard]] Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const {
22 return {xyz + other.xyz, w + other.w}; 22 return {xyz + other.xyz, w + other.w};
23 } 23 }
24 24
25 Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const { 25 [[nodiscard]] Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const {
26 return {xyz - other.xyz, w - other.w}; 26 return {xyz - other.xyz, w - other.w};
27 } 27 }
28 28
29 Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(const Quaternion& other) const { 29 [[nodiscard]] Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(
30 const Quaternion& other) const {
30 return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz), 31 return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),
31 w * other.w - Dot(xyz, other.xyz)}; 32 w * other.w - Dot(xyz, other.xyz)};
32 } 33 }
33 34
34 Quaternion<T> Normalized() const { 35 [[nodiscard]] Quaternion<T> Normalized() const {
35 T length = std::sqrt(xyz.Length2() + w * w); 36 T length = std::sqrt(xyz.Length2() + w * w);
36 return {xyz / length, w / length}; 37 return {xyz / length, w / length};
37 } 38 }
38}; 39};
39 40
40template <typename T> 41template <typename T>
41auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) { 42[[nodiscard]] auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {
42 return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w); 43 return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w);
43} 44}
44 45
45inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) { 46[[nodiscard]] inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {
46 return {axis * std::sin(angle / 2), std::cos(angle / 2)}; 47 return {axis * std::sin(angle / 2), std::cos(angle / 2)};
47} 48}
48 49
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h
index abe3b4dc2..138fa0131 100644
--- a/src/common/ring_buffer.h
+++ b/src/common/ring_buffer.h
@@ -91,12 +91,12 @@ public:
91 } 91 }
92 92
93 /// @returns Number of slots used 93 /// @returns Number of slots used
94 std::size_t Size() const { 94 [[nodiscard]] std::size_t Size() const {
95 return m_write_index.load() - m_read_index.load(); 95 return m_write_index.load() - m_read_index.load();
96 } 96 }
97 97
98 /// @returns Maximum size of ring buffer 98 /// @returns Maximum size of ring buffer
99 constexpr std::size_t Capacity() const { 99 [[nodiscard]] constexpr std::size_t Capacity() const {
100 return capacity; 100 return capacity;
101 } 101 }
102 102
diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h
index 1df5528c4..4f946a258 100644
--- a/src/common/spin_lock.h
+++ b/src/common/spin_lock.h
@@ -17,7 +17,7 @@ class SpinLock {
17public: 17public:
18 void lock(); 18 void lock();
19 void unlock(); 19 void unlock();
20 bool try_lock(); 20 [[nodiscard]] bool try_lock();
21 21
22private: 22private:
23 std::atomic_flag lck = ATOMIC_FLAG_INIT; 23 std::atomic_flag lck = ATOMIC_FLAG_INIT;
diff --git a/src/common/string_util.h b/src/common/string_util.h
index 023dff5dc..a32c07c06 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -12,19 +12,19 @@
12namespace Common { 12namespace Common {
13 13
14/// Make a string lowercase 14/// Make a string lowercase
15std::string ToLower(std::string str); 15[[nodiscard]] std::string ToLower(std::string str);
16 16
17/// Make a string uppercase 17/// Make a string uppercase
18std::string ToUpper(std::string str); 18[[nodiscard]] std::string ToUpper(std::string str);
19 19
20std::string StringFromBuffer(const std::vector<u8>& data); 20[[nodiscard]] std::string StringFromBuffer(const std::vector<u8>& data);
21 21
22std::string StripSpaces(const std::string& s); 22[[nodiscard]] std::string StripSpaces(const std::string& s);
23std::string StripQuotes(const std::string& s); 23[[nodiscard]] std::string StripQuotes(const std::string& s);
24 24
25std::string StringFromBool(bool value); 25[[nodiscard]] std::string StringFromBool(bool value);
26 26
27std::string TabsToSpaces(int tab_size, std::string in); 27[[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in);
28 28
29void SplitString(const std::string& str, char delim, std::vector<std::string>& output); 29void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
30 30
@@ -34,14 +34,15 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
34 34
35void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, 35void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path,
36 const std::string& _Filename); 36 const std::string& _Filename);
37std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest); 37[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
38 const std::string& dest);
38 39
39std::string UTF16ToUTF8(const std::u16string& input); 40[[nodiscard]] std::string UTF16ToUTF8(const std::u16string& input);
40std::u16string UTF8ToUTF16(const std::string& input); 41[[nodiscard]] std::u16string UTF8ToUTF16(const std::string& input);
41 42
42#ifdef _WIN32 43#ifdef _WIN32
43std::string UTF16ToUTF8(const std::wstring& input); 44[[nodiscard]] std::string UTF16ToUTF8(const std::wstring& input);
44std::wstring UTF8ToUTF16W(const std::string& str); 45[[nodiscard]] std::wstring UTF8ToUTF16W(const std::string& str);
45 46
46#endif 47#endif
47 48
@@ -50,7 +51,7 @@ std::wstring UTF8ToUTF16W(const std::string& str);
50 * `other` for equality. 51 * `other` for equality.
51 */ 52 */
52template <typename InIt> 53template <typename InIt>
53bool ComparePartialString(InIt begin, InIt end, const char* other) { 54[[nodiscard]] bool ComparePartialString(InIt begin, InIt end, const char* other) {
54 for (; begin != end && *other != '\0'; ++begin, ++other) { 55 for (; begin != end && *other != '\0'; ++begin, ++other) {
55 if (*begin != *other) { 56 if (*begin != *other) {
56 return false; 57 return false;
@@ -64,14 +65,15 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
64 * Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't 65 * Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
65 * NUL-terminated then the string ends at max_len characters. 66 * NUL-terminated then the string ends at max_len characters.
66 */ 67 */
67std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len); 68[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(const char* buffer,
69 std::size_t max_len);
68 70
69/** 71/**
70 * Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't 72 * Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
71 * null-terminated, then the string ends at the greatest multiple of two less then or equal to 73 * null-terminated, then the string ends at the greatest multiple of two less then or equal to
72 * max_len_bytes. 74 * max_len_bytes.
73 */ 75 */
74std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, 76[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
75 std::size_t max_len); 77 std::size_t max_len);
76 78
77} // namespace Common 79} // namespace Common
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp
index 16d42facd..6241d08b3 100644
--- a/src/common/telemetry.cpp
+++ b/src/common/telemetry.cpp
@@ -12,7 +12,7 @@
12#include "common/x64/cpu_detect.h" 12#include "common/x64/cpu_detect.h"
13#endif 13#endif
14 14
15namespace Telemetry { 15namespace Common::Telemetry {
16 16
17void FieldCollection::Accept(VisitorInterface& visitor) const { 17void FieldCollection::Accept(VisitorInterface& visitor) const {
18 for (const auto& field : fields) { 18 for (const auto& field : fields) {
@@ -88,4 +88,4 @@ void AppendOSInfo(FieldCollection& fc) {
88#endif 88#endif
89} 89}
90 90
91} // namespace Telemetry 91} // namespace Common::Telemetry
diff --git a/src/common/telemetry.h b/src/common/telemetry.h
index 854a73fae..a50c5d1de 100644
--- a/src/common/telemetry.h
+++ b/src/common/telemetry.h
@@ -10,7 +10,7 @@
10#include <string> 10#include <string>
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace Telemetry { 13namespace Common::Telemetry {
14 14
15/// Field type, used for grouping fields together in the final submitted telemetry log 15/// Field type, used for grouping fields together in the final submitted telemetry log
16enum class FieldType : u8 { 16enum class FieldType : u8 {
@@ -63,30 +63,30 @@ public:
63 63
64 void Accept(VisitorInterface& visitor) const override; 64 void Accept(VisitorInterface& visitor) const override;
65 65
66 const std::string& GetName() const override { 66 [[nodiscard]] const std::string& GetName() const override {
67 return name; 67 return name;
68 } 68 }
69 69
70 /** 70 /**
71 * Returns the type of the field. 71 * Returns the type of the field.
72 */ 72 */
73 FieldType GetType() const { 73 [[nodiscard]] FieldType GetType() const {
74 return type; 74 return type;
75 } 75 }
76 76
77 /** 77 /**
78 * Returns the value of the field. 78 * Returns the value of the field.
79 */ 79 */
80 const T& GetValue() const { 80 [[nodiscard]] const T& GetValue() const {
81 return value; 81 return value;
82 } 82 }
83 83
84 bool operator==(const Field& other) const { 84 [[nodiscard]] bool operator==(const Field& other) const {
85 return (type == other.type) && (name == other.name) && (value == other.value); 85 return (type == other.type) && (name == other.name) && (value == other.value);
86 } 86 }
87 87
88 bool operator!=(const Field& other) const { 88 [[nodiscard]] bool operator!=(const Field& other) const {
89 return !(*this == other); 89 return !operator==(other);
90 } 90 }
91 91
92private: 92private:
@@ -196,4 +196,4 @@ void AppendCPUInfo(FieldCollection& fc);
196/// such as platform name, etc. 196/// such as platform name, etc.
197void AppendOSInfo(FieldCollection& fc); 197void AppendOSInfo(FieldCollection& fc);
198 198
199} // namespace Telemetry 199} // namespace Common::Telemetry
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index 791f99a8c..def9e5d8d 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -18,14 +18,14 @@ struct ThreadQueueList {
18 using Priority = unsigned int; 18 using Priority = unsigned int;
19 19
20 // Number of priority levels. (Valid levels are [0..NUM_QUEUES).) 20 // Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
21 static const Priority NUM_QUEUES = N; 21 static constexpr Priority NUM_QUEUES = N;
22 22
23 ThreadQueueList() { 23 ThreadQueueList() {
24 first = nullptr; 24 first = nullptr;
25 } 25 }
26 26
27 // Only for debugging, returns priority level. 27 // Only for debugging, returns priority level.
28 Priority contains(const T& uid) const { 28 [[nodiscard]] Priority contains(const T& uid) const {
29 for (Priority i = 0; i < NUM_QUEUES; ++i) { 29 for (Priority i = 0; i < NUM_QUEUES; ++i) {
30 const Queue& cur = queues[i]; 30 const Queue& cur = queues[i];
31 if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) { 31 if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
@@ -36,7 +36,7 @@ struct ThreadQueueList {
36 return -1; 36 return -1;
37 } 37 }
38 38
39 T get_first() const { 39 [[nodiscard]] T get_first() const {
40 const Queue* cur = first; 40 const Queue* cur = first;
41 while (cur != nullptr) { 41 while (cur != nullptr) {
42 if (!cur->data.empty()) { 42 if (!cur->data.empty()) {
@@ -49,7 +49,7 @@ struct ThreadQueueList {
49 } 49 }
50 50
51 template <typename UnaryPredicate> 51 template <typename UnaryPredicate>
52 T get_first_filter(UnaryPredicate filter) const { 52 [[nodiscard]] T get_first_filter(UnaryPredicate filter) const {
53 const Queue* cur = first; 53 const Queue* cur = first;
54 while (cur != nullptr) { 54 while (cur != nullptr) {
55 if (!cur->data.empty()) { 55 if (!cur->data.empty()) {
@@ -129,7 +129,7 @@ struct ThreadQueueList {
129 first = nullptr; 129 first = nullptr;
130 } 130 }
131 131
132 bool empty(Priority priority) const { 132 [[nodiscard]] bool empty(Priority priority) const {
133 const Queue* cur = &queues[priority]; 133 const Queue* cur = &queues[priority];
134 return cur->data.empty(); 134 return cur->data.empty();
135 } 135 }
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index 8268bbd5c..a4647314a 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -25,15 +25,15 @@ public:
25 delete read_ptr; 25 delete read_ptr;
26 } 26 }
27 27
28 std::size_t Size() const { 28 [[nodiscard]] std::size_t Size() const {
29 return size.load(); 29 return size.load();
30 } 30 }
31 31
32 bool Empty() const { 32 [[nodiscard]] bool Empty() const {
33 return Size() == 0; 33 return Size() == 0;
34 } 34 }
35 35
36 T& Front() const { 36 [[nodiscard]] T& Front() const {
37 return read_ptr->current; 37 return read_ptr->current;
38 } 38 }
39 39
@@ -130,15 +130,15 @@ private:
130template <typename T> 130template <typename T>
131class MPSCQueue { 131class MPSCQueue {
132public: 132public:
133 std::size_t Size() const { 133 [[nodiscard]] std::size_t Size() const {
134 return spsc_queue.Size(); 134 return spsc_queue.Size();
135 } 135 }
136 136
137 bool Empty() const { 137 [[nodiscard]] bool Empty() const {
138 return spsc_queue.Empty(); 138 return spsc_queue.Empty();
139 } 139 }
140 140
141 T& Front() const { 141 [[nodiscard]] T& Front() const {
142 return spsc_queue.Front(); 142 return spsc_queue.Front();
143 } 143 }
144 144
diff --git a/src/common/time_zone.h b/src/common/time_zone.h
index 945daa09c..9f5939ca5 100644
--- a/src/common/time_zone.h
+++ b/src/common/time_zone.h
@@ -10,9 +10,9 @@
10namespace Common::TimeZone { 10namespace Common::TimeZone {
11 11
12/// Gets the default timezone, i.e. "GMT" 12/// Gets the default timezone, i.e. "GMT"
13std::string GetDefaultTimeZone(); 13[[nodiscard]] std::string GetDefaultTimeZone();
14 14
15/// Gets the offset of the current timezone (from the default), in seconds 15/// Gets the offset of the current timezone (from the default), in seconds
16std::chrono::seconds GetCurrentOffsetSeconds(); 16[[nodiscard]] std::chrono::seconds GetCurrentOffsetSeconds();
17 17
18} // namespace Common::TimeZone 18} // namespace Common::TimeZone
diff --git a/src/common/timer.h b/src/common/timer.h
index 27b521baa..8894a143d 100644
--- a/src/common/timer.h
+++ b/src/common/timer.h
@@ -19,18 +19,18 @@ public:
19 19
20 // The time difference is always returned in milliseconds, regardless of alternative internal 20 // The time difference is always returned in milliseconds, regardless of alternative internal
21 // representation 21 // representation
22 std::chrono::milliseconds GetTimeDifference(); 22 [[nodiscard]] std::chrono::milliseconds GetTimeDifference();
23 void AddTimeDifference(); 23 void AddTimeDifference();
24 24
25 static std::chrono::seconds GetTimeSinceJan1970(); 25 [[nodiscard]] static std::chrono::seconds GetTimeSinceJan1970();
26 static std::chrono::seconds GetLocalTimeSinceJan1970(); 26 [[nodiscard]] static std::chrono::seconds GetLocalTimeSinceJan1970();
27 static double GetDoubleTime(); 27 [[nodiscard]] static double GetDoubleTime();
28 28
29 static std::string GetTimeFormatted(); 29 [[nodiscard]] static std::string GetTimeFormatted();
30 std::string GetTimeElapsedFormatted() const; 30 [[nodiscard]] std::string GetTimeElapsedFormatted() const;
31 std::chrono::milliseconds GetTimeElapsed(); 31 [[nodiscard]] std::chrono::milliseconds GetTimeElapsed();
32 32
33 static std::chrono::milliseconds GetTimeMs(); 33 [[nodiscard]] static std::chrono::milliseconds GetTimeMs();
34 34
35private: 35private:
36 std::chrono::milliseconds m_LastTime; 36 std::chrono::milliseconds m_LastTime;
diff --git a/src/common/uint128.h b/src/common/uint128.h
index 503cd2d0c..969259ab6 100644
--- a/src/common/uint128.h
+++ b/src/common/uint128.h
@@ -10,13 +10,13 @@
10namespace Common { 10namespace Common {
11 11
12// This function multiplies 2 u64 values and divides it by a u64 value. 12// This function multiplies 2 u64 values and divides it by a u64 value.
13u64 MultiplyAndDivide64(u64 a, u64 b, u64 d); 13[[nodiscard]] u64 MultiplyAndDivide64(u64 a, u64 b, u64 d);
14 14
15// This function multiplies 2 u64 values and produces a u128 value; 15// This function multiplies 2 u64 values and produces a u128 value;
16u128 Multiply64Into128(u64 a, u64 b); 16[[nodiscard]] u128 Multiply64Into128(u64 a, u64 b);
17 17
18// This function divides a u128 by a u32 value and produces two u64 values: 18// This function divides a u128 by a u32 value and produces two u64 values:
19// the result of division and the remainder 19// the result of division and the remainder
20std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor); 20[[nodiscard]] std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
21 21
22} // namespace Common 22} // namespace Common
diff --git a/src/common/uuid.h b/src/common/uuid.h
index 4d3af8cec..4ab9a25f0 100644
--- a/src/common/uuid.h
+++ b/src/common/uuid.h
@@ -19,21 +19,21 @@ struct UUID {
19 constexpr explicit UUID(const u128& id) : uuid{id} {} 19 constexpr explicit UUID(const u128& id) : uuid{id} {}
20 constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} 20 constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
21 21
22 constexpr explicit operator bool() const { 22 [[nodiscard]] constexpr explicit operator bool() const {
23 return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1]; 23 return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1];
24 } 24 }
25 25
26 constexpr bool operator==(const UUID& rhs) const { 26 [[nodiscard]] constexpr bool operator==(const UUID& rhs) const {
27 // TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20 27 // TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20
28 return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1]; 28 return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1];
29 } 29 }
30 30
31 constexpr bool operator!=(const UUID& rhs) const { 31 [[nodiscard]] constexpr bool operator!=(const UUID& rhs) const {
32 return !operator==(rhs); 32 return !operator==(rhs);
33 } 33 }
34 34
35 // TODO(ogniK): Properly generate uuids based on RFC-4122 35 // TODO(ogniK): Properly generate uuids based on RFC-4122
36 static UUID Generate(); 36 [[nodiscard]] static UUID Generate();
37 37
38 // Set the UUID to {0,0} to be considered an invalid user 38 // Set the UUID to {0,0} to be considered an invalid user
39 constexpr void Invalidate() { 39 constexpr void Invalidate() {
@@ -41,12 +41,12 @@ struct UUID {
41 } 41 }
42 42
43 // TODO(ogniK): Properly generate a Nintendo ID 43 // TODO(ogniK): Properly generate a Nintendo ID
44 constexpr u64 GetNintendoID() const { 44 [[nodiscard]] constexpr u64 GetNintendoID() const {
45 return uuid[0]; 45 return uuid[0];
46 } 46 }
47 47
48 std::string Format() const; 48 [[nodiscard]] std::string Format() const;
49 std::string FormatSwitch() const; 49 [[nodiscard]] std::string FormatSwitch() const;
50}; 50};
51static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 51static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
52 52
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index 429485329..2a0fcf541 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -52,15 +52,15 @@ public:
52 constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {} 52 constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
53 53
54 template <typename T2> 54 template <typename T2>
55 constexpr Vec2<T2> Cast() const { 55 [[nodiscard]] constexpr Vec2<T2> Cast() const {
56 return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y)); 56 return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
57 } 57 }
58 58
59 static constexpr Vec2 AssignToAll(const T& f) { 59 [[nodiscard]] static constexpr Vec2 AssignToAll(const T& f) {
60 return Vec2{f, f}; 60 return Vec2{f, f};
61 } 61 }
62 62
63 constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const { 63 [[nodiscard]] constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
64 return {x + other.x, y + other.y}; 64 return {x + other.x, y + other.y};
65 } 65 }
66 constexpr Vec2& operator+=(const Vec2& other) { 66 constexpr Vec2& operator+=(const Vec2& other) {
@@ -68,7 +68,7 @@ public:
68 y += other.y; 68 y += other.y;
69 return *this; 69 return *this;
70 } 70 }
71 constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const { 71 [[nodiscard]] constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
72 return {x - other.x, y - other.y}; 72 return {x - other.x, y - other.y};
73 } 73 }
74 constexpr Vec2& operator-=(const Vec2& other) { 74 constexpr Vec2& operator-=(const Vec2& other) {
@@ -78,15 +78,15 @@ public:
78 } 78 }
79 79
80 template <typename U = T> 80 template <typename U = T>
81 constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const { 81 [[nodiscard]] constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
82 return {-x, -y}; 82 return {-x, -y};
83 } 83 }
84 constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const { 84 [[nodiscard]] constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
85 return {x * other.x, y * other.y}; 85 return {x * other.x, y * other.y};
86 } 86 }
87 87
88 template <typename V> 88 template <typename V>
89 constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const { 89 [[nodiscard]] constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
90 return {x * f, y * f}; 90 return {x * f, y * f};
91 } 91 }
92 92
@@ -97,7 +97,7 @@ public:
97 } 97 }
98 98
99 template <typename V> 99 template <typename V>
100 constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const { 100 [[nodiscard]] constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
101 return {x / f, y / f}; 101 return {x / f, y / f};
102 } 102 }
103 103
@@ -107,18 +107,18 @@ public:
107 return *this; 107 return *this;
108 } 108 }
109 109
110 constexpr T Length2() const { 110 [[nodiscard]] constexpr T Length2() const {
111 return x * x + y * y; 111 return x * x + y * y;
112 } 112 }
113 113
114 // Only implemented for T=float 114 // Only implemented for T=float
115 float Length() const; 115 [[nodiscard]] float Length() const;
116 float Normalize(); // returns the previous length, which is often useful 116 [[nodiscard]] float Normalize(); // returns the previous length, which is often useful
117 117
118 constexpr T& operator[](std::size_t i) { 118 [[nodiscard]] constexpr T& operator[](std::size_t i) {
119 return *((&x) + i); 119 return *((&x) + i);
120 } 120 }
121 constexpr const T& operator[](std::size_t i) const { 121 [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
122 return *((&x) + i); 122 return *((&x) + i);
123 } 123 }
124 124
@@ -128,46 +128,46 @@ public:
128 } 128 }
129 129
130 // Common aliases: UV (texel coordinates), ST (texture coordinates) 130 // Common aliases: UV (texel coordinates), ST (texture coordinates)
131 constexpr T& u() { 131 [[nodiscard]] constexpr T& u() {
132 return x; 132 return x;
133 } 133 }
134 constexpr T& v() { 134 [[nodiscard]] constexpr T& v() {
135 return y; 135 return y;
136 } 136 }
137 constexpr T& s() { 137 [[nodiscard]] constexpr T& s() {
138 return x; 138 return x;
139 } 139 }
140 constexpr T& t() { 140 [[nodiscard]] constexpr T& t() {
141 return y; 141 return y;
142 } 142 }
143 143
144 constexpr const T& u() const { 144 [[nodiscard]] constexpr const T& u() const {
145 return x; 145 return x;
146 } 146 }
147 constexpr const T& v() const { 147 [[nodiscard]] constexpr const T& v() const {
148 return y; 148 return y;
149 } 149 }
150 constexpr const T& s() const { 150 [[nodiscard]] constexpr const T& s() const {
151 return x; 151 return x;
152 } 152 }
153 constexpr const T& t() const { 153 [[nodiscard]] constexpr const T& t() const {
154 return y; 154 return y;
155 } 155 }
156 156
157 // swizzlers - create a subvector of specific components 157 // swizzlers - create a subvector of specific components
158 constexpr Vec2 yx() const { 158 [[nodiscard]] constexpr Vec2 yx() const {
159 return Vec2(y, x); 159 return Vec2(y, x);
160 } 160 }
161 constexpr Vec2 vu() const { 161 [[nodiscard]] constexpr Vec2 vu() const {
162 return Vec2(y, x); 162 return Vec2(y, x);
163 } 163 }
164 constexpr Vec2 ts() const { 164 [[nodiscard]] constexpr Vec2 ts() const {
165 return Vec2(y, x); 165 return Vec2(y, x);
166 } 166 }
167}; 167};
168 168
169template <typename T, typename V> 169template <typename T, typename V>
170constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) { 170[[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
171 return Vec2<T>(f * vec.x, f * vec.y); 171 return Vec2<T>(f * vec.x, f * vec.y);
172} 172}
173 173
@@ -196,15 +196,15 @@ public:
196 constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {} 196 constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
197 197
198 template <typename T2> 198 template <typename T2>
199 constexpr Vec3<T2> Cast() const { 199 [[nodiscard]] constexpr Vec3<T2> Cast() const {
200 return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z)); 200 return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
201 } 201 }
202 202
203 static constexpr Vec3 AssignToAll(const T& f) { 203 [[nodiscard]] static constexpr Vec3 AssignToAll(const T& f) {
204 return Vec3(f, f, f); 204 return Vec3(f, f, f);
205 } 205 }
206 206
207 constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const { 207 [[nodiscard]] constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
208 return {x + other.x, y + other.y, z + other.z}; 208 return {x + other.x, y + other.y, z + other.z};
209 } 209 }
210 210
@@ -215,7 +215,7 @@ public:
215 return *this; 215 return *this;
216 } 216 }
217 217
218 constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const { 218 [[nodiscard]] constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
219 return {x - other.x, y - other.y, z - other.z}; 219 return {x - other.x, y - other.y, z - other.z};
220 } 220 }
221 221
@@ -227,16 +227,16 @@ public:
227 } 227 }
228 228
229 template <typename U = T> 229 template <typename U = T>
230 constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const { 230 [[nodiscard]] constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
231 return {-x, -y, -z}; 231 return {-x, -y, -z};
232 } 232 }
233 233
234 constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const { 234 [[nodiscard]] constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
235 return {x * other.x, y * other.y, z * other.z}; 235 return {x * other.x, y * other.y, z * other.z};
236 } 236 }
237 237
238 template <typename V> 238 template <typename V>
239 constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const { 239 [[nodiscard]] constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
240 return {x * f, y * f, z * f}; 240 return {x * f, y * f, z * f};
241 } 241 }
242 242
@@ -246,7 +246,7 @@ public:
246 return *this; 246 return *this;
247 } 247 }
248 template <typename V> 248 template <typename V>
249 constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const { 249 [[nodiscard]] constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
250 return {x / f, y / f, z / f}; 250 return {x / f, y / f, z / f};
251 } 251 }
252 252
@@ -256,20 +256,20 @@ public:
256 return *this; 256 return *this;
257 } 257 }
258 258
259 constexpr T Length2() const { 259 [[nodiscard]] constexpr T Length2() const {
260 return x * x + y * y + z * z; 260 return x * x + y * y + z * z;
261 } 261 }
262 262
263 // Only implemented for T=float 263 // Only implemented for T=float
264 float Length() const; 264 [[nodiscard]] float Length() const;
265 Vec3 Normalized() const; 265 [[nodiscard]] Vec3 Normalized() const;
266 float Normalize(); // returns the previous length, which is often useful 266 [[nodiscard]] float Normalize(); // returns the previous length, which is often useful
267 267
268 constexpr T& operator[](std::size_t i) { 268 [[nodiscard]] constexpr T& operator[](std::size_t i) {
269 return *((&x) + i); 269 return *((&x) + i);
270 } 270 }
271 271
272 constexpr const T& operator[](std::size_t i) const { 272 [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
273 return *((&x) + i); 273 return *((&x) + i);
274 } 274 }
275 275
@@ -280,63 +280,63 @@ public:
280 } 280 }
281 281
282 // Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates) 282 // Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
283 constexpr T& u() { 283 [[nodiscard]] constexpr T& u() {
284 return x; 284 return x;
285 } 285 }
286 constexpr T& v() { 286 [[nodiscard]] constexpr T& v() {
287 return y; 287 return y;
288 } 288 }
289 constexpr T& w() { 289 [[nodiscard]] constexpr T& w() {
290 return z; 290 return z;
291 } 291 }
292 292
293 constexpr T& r() { 293 [[nodiscard]] constexpr T& r() {
294 return x; 294 return x;
295 } 295 }
296 constexpr T& g() { 296 [[nodiscard]] constexpr T& g() {
297 return y; 297 return y;
298 } 298 }
299 constexpr T& b() { 299 [[nodiscard]] constexpr T& b() {
300 return z; 300 return z;
301 } 301 }
302 302
303 constexpr T& s() { 303 [[nodiscard]] constexpr T& s() {
304 return x; 304 return x;
305 } 305 }
306 constexpr T& t() { 306 [[nodiscard]] constexpr T& t() {
307 return y; 307 return y;
308 } 308 }
309 constexpr T& q() { 309 [[nodiscard]] constexpr T& q() {
310 return z; 310 return z;
311 } 311 }
312 312
313 constexpr const T& u() const { 313 [[nodiscard]] constexpr const T& u() const {
314 return x; 314 return x;
315 } 315 }
316 constexpr const T& v() const { 316 [[nodiscard]] constexpr const T& v() const {
317 return y; 317 return y;
318 } 318 }
319 constexpr const T& w() const { 319 [[nodiscard]] constexpr const T& w() const {
320 return z; 320 return z;
321 } 321 }
322 322
323 constexpr const T& r() const { 323 [[nodiscard]] constexpr const T& r() const {
324 return x; 324 return x;
325 } 325 }
326 constexpr const T& g() const { 326 [[nodiscard]] constexpr const T& g() const {
327 return y; 327 return y;
328 } 328 }
329 constexpr const T& b() const { 329 [[nodiscard]] constexpr const T& b() const {
330 return z; 330 return z;
331 } 331 }
332 332
333 constexpr const T& s() const { 333 [[nodiscard]] constexpr const T& s() const {
334 return x; 334 return x;
335 } 335 }
336 constexpr const T& t() const { 336 [[nodiscard]] constexpr const T& t() const {
337 return y; 337 return y;
338 } 338 }
339 constexpr const T& q() const { 339 [[nodiscard]] constexpr const T& q() const {
340 return z; 340 return z;
341 } 341 }
342 342
@@ -345,7 +345,7 @@ public:
345// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all 345// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
346// component names (x<->r) and permutations (xy<->yx) 346// component names (x<->r) and permutations (xy<->yx)
347#define _DEFINE_SWIZZLER2(a, b, name) \ 347#define _DEFINE_SWIZZLER2(a, b, name) \
348 constexpr Vec2<T> name() const { \ 348 [[nodiscard]] constexpr Vec2<T> name() const { \
349 return Vec2<T>(a, b); \ 349 return Vec2<T>(a, b); \
350 } 350 }
351#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \ 351#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
@@ -366,7 +366,7 @@ public:
366}; 366};
367 367
368template <typename T, typename V> 368template <typename T, typename V>
369constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) { 369[[nodiscard]] constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
370 return Vec3<T>(f * vec.x, f * vec.y, f * vec.z); 370 return Vec3<T>(f * vec.x, f * vec.y, f * vec.z);
371} 371}
372 372
@@ -402,16 +402,16 @@ public:
402 : x(x_), y(y_), z(z_), w(w_) {} 402 : x(x_), y(y_), z(z_), w(w_) {}
403 403
404 template <typename T2> 404 template <typename T2>
405 constexpr Vec4<T2> Cast() const { 405 [[nodiscard]] constexpr Vec4<T2> Cast() const {
406 return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z), 406 return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
407 static_cast<T2>(w)); 407 static_cast<T2>(w));
408 } 408 }
409 409
410 static constexpr Vec4 AssignToAll(const T& f) { 410 [[nodiscard]] static constexpr Vec4 AssignToAll(const T& f) {
411 return Vec4(f, f, f, f); 411 return Vec4(f, f, f, f);
412 } 412 }
413 413
414 constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const { 414 [[nodiscard]] constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
415 return {x + other.x, y + other.y, z + other.z, w + other.w}; 415 return {x + other.x, y + other.y, z + other.z, w + other.w};
416 } 416 }
417 417
@@ -423,7 +423,7 @@ public:
423 return *this; 423 return *this;
424 } 424 }
425 425
426 constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const { 426 [[nodiscard]] constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
427 return {x - other.x, y - other.y, z - other.z, w - other.w}; 427 return {x - other.x, y - other.y, z - other.z, w - other.w};
428 } 428 }
429 429
@@ -436,16 +436,16 @@ public:
436 } 436 }
437 437
438 template <typename U = T> 438 template <typename U = T>
439 constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const { 439 [[nodiscard]] constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
440 return {-x, -y, -z, -w}; 440 return {-x, -y, -z, -w};
441 } 441 }
442 442
443 constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const { 443 [[nodiscard]] constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
444 return {x * other.x, y * other.y, z * other.z, w * other.w}; 444 return {x * other.x, y * other.y, z * other.z, w * other.w};
445 } 445 }
446 446
447 template <typename V> 447 template <typename V>
448 constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const { 448 [[nodiscard]] constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
449 return {x * f, y * f, z * f, w * f}; 449 return {x * f, y * f, z * f, w * f};
450 } 450 }
451 451
@@ -456,7 +456,7 @@ public:
456 } 456 }
457 457
458 template <typename V> 458 template <typename V>
459 constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const { 459 [[nodiscard]] constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
460 return {x / f, y / f, z / f, w / f}; 460 return {x / f, y / f, z / f, w / f};
461 } 461 }
462 462
@@ -466,15 +466,15 @@ public:
466 return *this; 466 return *this;
467 } 467 }
468 468
469 constexpr T Length2() const { 469 [[nodiscard]] constexpr T Length2() const {
470 return x * x + y * y + z * z + w * w; 470 return x * x + y * y + z * z + w * w;
471 } 471 }
472 472
473 constexpr T& operator[](std::size_t i) { 473 [[nodiscard]] constexpr T& operator[](std::size_t i) {
474 return *((&x) + i); 474 return *((&x) + i);
475 } 475 }
476 476
477 constexpr const T& operator[](std::size_t i) const { 477 [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
478 return *((&x) + i); 478 return *((&x) + i);
479 } 479 }
480 480
@@ -486,29 +486,29 @@ public:
486 } 486 }
487 487
488 // Common alias: RGBA (colors) 488 // Common alias: RGBA (colors)
489 constexpr T& r() { 489 [[nodiscard]] constexpr T& r() {
490 return x; 490 return x;
491 } 491 }
492 constexpr T& g() { 492 [[nodiscard]] constexpr T& g() {
493 return y; 493 return y;
494 } 494 }
495 constexpr T& b() { 495 [[nodiscard]] constexpr T& b() {
496 return z; 496 return z;
497 } 497 }
498 constexpr T& a() { 498 [[nodiscard]] constexpr T& a() {
499 return w; 499 return w;
500 } 500 }
501 501
502 constexpr const T& r() const { 502 [[nodiscard]] constexpr const T& r() const {
503 return x; 503 return x;
504 } 504 }
505 constexpr const T& g() const { 505 [[nodiscard]] constexpr const T& g() const {
506 return y; 506 return y;
507 } 507 }
508 constexpr const T& b() const { 508 [[nodiscard]] constexpr const T& b() const {
509 return z; 509 return z;
510 } 510 }
511 constexpr const T& a() const { 511 [[nodiscard]] constexpr const T& a() const {
512 return w; 512 return w;
513 } 513 }
514 514
@@ -520,7 +520,7 @@ public:
520// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and 520// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
521// permutations (xy<->yx) 521// permutations (xy<->yx)
522#define _DEFINE_SWIZZLER2(a, b, name) \ 522#define _DEFINE_SWIZZLER2(a, b, name) \
523 constexpr Vec2<T> name() const { \ 523 [[nodiscard]] constexpr Vec2<T> name() const { \
524 return Vec2<T>(a, b); \ 524 return Vec2<T>(a, b); \
525 } 525 }
526#define DEFINE_SWIZZLER2_COMP1(a, a2) \ 526#define DEFINE_SWIZZLER2_COMP1(a, a2) \
@@ -547,7 +547,7 @@ public:
547#undef _DEFINE_SWIZZLER2 547#undef _DEFINE_SWIZZLER2
548 548
549#define _DEFINE_SWIZZLER3(a, b, c, name) \ 549#define _DEFINE_SWIZZLER3(a, b, c, name) \
550 constexpr Vec3<T> name() const { \ 550 [[nodiscard]] constexpr Vec3<T> name() const { \
551 return Vec3<T>(a, b, c); \ 551 return Vec3<T>(a, b, c); \
552 } 552 }
553#define DEFINE_SWIZZLER3_COMP1(a, a2) \ 553#define DEFINE_SWIZZLER3_COMP1(a, a2) \
@@ -581,7 +581,7 @@ public:
581}; 581};
582 582
583template <typename T, typename V> 583template <typename T, typename V>
584constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) { 584[[nodiscard]] constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
585 return {f * vec.x, f * vec.y, f * vec.z, f * vec.w}; 585 return {f * vec.x, f * vec.y, f * vec.z, f * vec.w};
586} 586}
587 587
@@ -593,39 +593,41 @@ constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b
593} 593}
594 594
595template <typename T> 595template <typename T>
596constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) { 596[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
597 return a.x * b.x + a.y * b.y + a.z * b.z; 597 return a.x * b.x + a.y * b.y + a.z * b.z;
598} 598}
599 599
600template <typename T> 600template <typename T>
601constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) { 601[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
602 return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; 602 return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
603} 603}
604 604
605template <typename T> 605template <typename T>
606constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) { 606[[nodiscard]] constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a,
607 const Vec3<T>& b) {
607 return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; 608 return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
608} 609}
609 610
610// linear interpolation via float: 0.0=begin, 1.0=end 611// linear interpolation via float: 0.0=begin, 1.0=end
611template <typename X> 612template <typename X>
612constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end, 613[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
613 const float t) { 614 const float t) {
614 return begin * (1.f - t) + end * t; 615 return begin * (1.f - t) + end * t;
615} 616}
616 617
617// linear interpolation via int: 0=begin, base=end 618// linear interpolation via int: 0=begin, base=end
618template <typename X, int base> 619template <typename X, int base>
619constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end, 620[[nodiscard]] constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin,
620 const int t) { 621 const X& end,
622 const int t) {
621 return (begin * (base - t) + end * t) / base; 623 return (begin * (base - t) + end * t) / base;
622} 624}
623 625
624// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second 626// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
625// interpolation. 627// interpolation.
626template <typename X> 628template <typename X>
627constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s, 629[[nodiscard]] constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11,
628 const float t) { 630 const float s, const float t) {
629 auto y0 = Lerp(x00, x01, s); 631 auto y0 = Lerp(x00, x01, s);
630 auto y1 = Lerp(x10, x11, s); 632 auto y1 = Lerp(x10, x11, s);
631 return Lerp(y0, y1, t); 633 return Lerp(y0, y1, t);
@@ -633,42 +635,42 @@ constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X&
633 635
634// Utility vector factories 636// Utility vector factories
635template <typename T> 637template <typename T>
636constexpr Vec2<T> MakeVec(const T& x, const T& y) { 638[[nodiscard]] constexpr Vec2<T> MakeVec(const T& x, const T& y) {
637 return Vec2<T>{x, y}; 639 return Vec2<T>{x, y};
638} 640}
639 641
640template <typename T> 642template <typename T>
641constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) { 643[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
642 return Vec3<T>{x, y, z}; 644 return Vec3<T>{x, y, z};
643} 645}
644 646
645template <typename T> 647template <typename T>
646constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) { 648[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
647 return MakeVec(x, y, zw[0], zw[1]); 649 return MakeVec(x, y, zw[0], zw[1]);
648} 650}
649 651
650template <typename T> 652template <typename T>
651constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) { 653[[nodiscard]] constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
652 return MakeVec(xy[0], xy[1], z); 654 return MakeVec(xy[0], xy[1], z);
653} 655}
654 656
655template <typename T> 657template <typename T>
656constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) { 658[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
657 return MakeVec(x, yz[0], yz[1]); 659 return MakeVec(x, yz[0], yz[1]);
658} 660}
659 661
660template <typename T> 662template <typename T>
661constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) { 663[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
662 return Vec4<T>{x, y, z, w}; 664 return Vec4<T>{x, y, z, w};
663} 665}
664 666
665template <typename T> 667template <typename T>
666constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) { 668[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
667 return MakeVec(xy[0], xy[1], z, w); 669 return MakeVec(xy[0], xy[1], z, w);
668} 670}
669 671
670template <typename T> 672template <typename T>
671constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) { 673[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
672 return MakeVec(x, yz[0], yz[1], w); 674 return MakeVec(x, yz[0], yz[1], w);
673} 675}
674 676
@@ -676,17 +678,17 @@ constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
676// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error 678// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
677// out soon enough due to misuse of the returned structure. 679// out soon enough due to misuse of the returned structure.
678template <typename T> 680template <typename T>
679constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) { 681[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
680 return MakeVec(xy[0], xy[1], zw[0], zw[1]); 682 return MakeVec(xy[0], xy[1], zw[0], zw[1]);
681} 683}
682 684
683template <typename T> 685template <typename T>
684constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) { 686[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
685 return MakeVec(xyz[0], xyz[1], xyz[2], w); 687 return MakeVec(xyz[0], xyz[1], xyz[2], w);
686} 688}
687 689
688template <typename T> 690template <typename T>
689constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) { 691[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
690 return MakeVec(x, yzw[0], yzw[1], yzw[2]); 692 return MakeVec(x, yzw[0], yzw[1], yzw[2]);
691} 693}
692 694
diff --git a/src/common/virtual_buffer.cpp b/src/common/virtual_buffer.cpp
index be5b67752..b009cb500 100644
--- a/src/common/virtual_buffer.cpp
+++ b/src/common/virtual_buffer.cpp
@@ -5,16 +5,7 @@
5#ifdef _WIN32 5#ifdef _WIN32
6#include <windows.h> 6#include <windows.h>
7#else 7#else
8#include <stdio.h>
9#include <sys/mman.h> 8#include <sys/mman.h>
10#include <sys/types.h>
11#if defined __APPLE__ || defined __FreeBSD__ || defined __OpenBSD__
12#include <sys/sysctl.h>
13#elif defined __HAIKU__
14#include <OS.h>
15#else
16#include <sys/sysinfo.h>
17#endif
18#endif 9#endif
19 10
20#include "common/assert.h" 11#include "common/assert.h"
diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h
index da064e59e..125cb42f0 100644
--- a/src/common/virtual_buffer.h
+++ b/src/common/virtual_buffer.h
@@ -30,23 +30,23 @@ public:
30 base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size)); 30 base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
31 } 31 }
32 32
33 constexpr const T& operator[](std::size_t index) const { 33 [[nodiscard]] constexpr const T& operator[](std::size_t index) const {
34 return base_ptr[index]; 34 return base_ptr[index];
35 } 35 }
36 36
37 constexpr T& operator[](std::size_t index) { 37 [[nodiscard]] constexpr T& operator[](std::size_t index) {
38 return base_ptr[index]; 38 return base_ptr[index];
39 } 39 }
40 40
41 constexpr T* data() { 41 [[nodiscard]] constexpr T* data() {
42 return base_ptr; 42 return base_ptr;
43 } 43 }
44 44
45 constexpr const T* data() const { 45 [[nodiscard]] constexpr const T* data() const {
46 return base_ptr; 46 return base_ptr;
47 } 47 }
48 48
49 constexpr std::size_t size() const { 49 [[nodiscard]] constexpr std::size_t size() const {
50 return alloc_size / sizeof(T); 50 return alloc_size / sizeof(T);
51 } 51 }
52 52
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h
index 367d72134..5db30083d 100644
--- a/src/common/wall_clock.h
+++ b/src/common/wall_clock.h
@@ -14,24 +14,24 @@ namespace Common {
14class WallClock { 14class WallClock {
15public: 15public:
16 /// Returns current wall time in nanoseconds 16 /// Returns current wall time in nanoseconds
17 virtual std::chrono::nanoseconds GetTimeNS() = 0; 17 [[nodiscard]] virtual std::chrono::nanoseconds GetTimeNS() = 0;
18 18
19 /// Returns current wall time in microseconds 19 /// Returns current wall time in microseconds
20 virtual std::chrono::microseconds GetTimeUS() = 0; 20 [[nodiscard]] virtual std::chrono::microseconds GetTimeUS() = 0;
21 21
22 /// Returns current wall time in milliseconds 22 /// Returns current wall time in milliseconds
23 virtual std::chrono::milliseconds GetTimeMS() = 0; 23 [[nodiscard]] virtual std::chrono::milliseconds GetTimeMS() = 0;
24 24
25 /// Returns current wall time in emulated clock cycles 25 /// Returns current wall time in emulated clock cycles
26 virtual u64 GetClockCycles() = 0; 26 [[nodiscard]] virtual u64 GetClockCycles() = 0;
27 27
28 /// Returns current wall time in emulated cpu cycles 28 /// Returns current wall time in emulated cpu cycles
29 virtual u64 GetCPUCycles() = 0; 29 [[nodiscard]] virtual u64 GetCPUCycles() = 0;
30 30
31 virtual void Pause(bool is_paused) = 0; 31 virtual void Pause(bool is_paused) = 0;
32 32
33 /// Tells if the wall clock, uses the host CPU's hardware clock 33 /// Tells if the wall clock, uses the host CPU's hardware clock
34 bool IsNative() const { 34 [[nodiscard]] bool IsNative() const {
35 return is_native; 35 return is_native;
36 } 36 }
37 37
@@ -47,7 +47,7 @@ private:
47 bool is_native; 47 bool is_native;
48}; 48};
49 49
50std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, 50[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
51 u32 emulated_clock_frequency); 51 u32 emulated_clock_frequency);
52 52
53} // namespace Common 53} // namespace Common
diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp
index 978526492..5f45459da 100644
--- a/src/common/zstd_compression.cpp
+++ b/src/common/zstd_compression.cpp
@@ -5,7 +5,6 @@
5#include <algorithm> 5#include <algorithm>
6#include <zstd.h> 6#include <zstd.h>
7 7
8#include "common/assert.h"
9#include "common/zstd_compression.h" 8#include "common/zstd_compression.h"
10 9
11namespace Common::Compression { 10namespace Common::Compression {
diff --git a/src/common/zstd_compression.h b/src/common/zstd_compression.h
index e9de941c8..c26a30ab9 100644
--- a/src/common/zstd_compression.h
+++ b/src/common/zstd_compression.h
@@ -13,24 +13,25 @@ namespace Common::Compression {
13/** 13/**
14 * Compresses a source memory region with Zstandard and returns the compressed data in a vector. 14 * Compresses a source memory region with Zstandard and returns the compressed data in a vector.
15 * 15 *
16 * @param source the uncompressed source memory region. 16 * @param source The uncompressed source memory region.
17 * @param source_size the size in bytes of the uncompressed source memory region. 17 * @param source_size The size of the uncompressed source memory region.
18 * @param compression_level the used compression level. Should be between 1 and 22. 18 * @param compression_level The used compression level. Should be between 1 and 22.
19 * 19 *
20 * @return the compressed data. 20 * @return the compressed data.
21 */ 21 */
22std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level); 22[[nodiscard]] std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size,
23 s32 compression_level);
23 24
24/** 25/**
25 * Compresses a source memory region with Zstandard with the default compression level and returns 26 * Compresses a source memory region with Zstandard with the default compression level and returns
26 * the compressed data in a vector. 27 * the compressed data in a vector.
27 * 28 *
28 * @param source the uncompressed source memory region. 29 * @param source The uncompressed source memory region.
29 * @param source_size the size in bytes of the uncompressed source memory region. 30 * @param source_size The size of the uncompressed source memory region.
30 * 31 *
31 * @return the compressed data. 32 * @return the compressed data.
32 */ 33 */
33std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size); 34[[nodiscard]] std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size);
34 35
35/** 36/**
36 * Decompresses a source memory region with Zstandard and returns the uncompressed data in a vector. 37 * Decompresses a source memory region with Zstandard and returns the uncompressed data in a vector.
@@ -39,6 +40,6 @@ std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_siz
39 * 40 *
40 * @return the decompressed data. 41 * @return the decompressed data.
41 */ 42 */
42std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed); 43[[nodiscard]] std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed);
43 44
44} // namespace Common::Compression \ No newline at end of file 45} // namespace Common::Compression \ No newline at end of file
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
index df0350881..9c8898700 100644
--- a/src/core/arm/cpu_interrupt_handler.cpp
+++ b/src/core/arm/cpu_interrupt_handler.cpp
@@ -7,9 +7,7 @@
7 7
8namespace Core { 8namespace Core {
9 9
10CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} { 10CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {}
11 interrupt_event = std::make_unique<Common::Event>();
12}
13 11
14CPUInterruptHandler::~CPUInterruptHandler() = default; 12CPUInterruptHandler::~CPUInterruptHandler() = default;
15 13
@@ -17,7 +15,7 @@ void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
17 if (is_interrupted_) { 15 if (is_interrupted_) {
18 interrupt_event->Set(); 16 interrupt_event->Set();
19 } 17 }
20 this->is_interrupted = is_interrupted_; 18 is_interrupted = is_interrupted_;
21} 19}
22 20
23void CPUInterruptHandler::AwaitInterrupt() { 21void CPUInterruptHandler::AwaitInterrupt() {
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
index 3d062d326..71e582f79 100644
--- a/src/core/arm/cpu_interrupt_handler.h
+++ b/src/core/arm/cpu_interrupt_handler.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
8 9
9namespace Common { 10namespace Common {
@@ -32,8 +33,8 @@ public:
32 void AwaitInterrupt(); 33 void AwaitInterrupt();
33 34
34private: 35private:
35 bool is_interrupted{};
36 std::unique_ptr<Common::Event> interrupt_event; 36 std::unique_ptr<Common::Event> interrupt_event;
37 std::atomic_bool is_interrupted{false};
37}; 38};
38 39
39} // namespace Core 40} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 443ca72eb..b5f28a86e 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -143,7 +143,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
143 config.wall_clock_cntpct = uses_wall_clock; 143 config.wall_clock_cntpct = uses_wall_clock;
144 144
145 // Safe optimizations 145 // Safe optimizations
146 if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) { 146 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
147 if (!Settings::values.cpuopt_page_tables) { 147 if (!Settings::values.cpuopt_page_tables) {
148 config.page_table = nullptr; 148 config.page_table = nullptr;
149 } 149 }
@@ -170,6 +170,17 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
170 } 170 }
171 } 171 }
172 172
173 // Unsafe optimizations
174 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) {
175 config.unsafe_optimizations = true;
176 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
177 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
178 }
179 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
180 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
181 }
182 }
183
173 return std::make_unique<Dynarmic::A32::Jit>(config); 184 return std::make_unique<Dynarmic::A32::Jit>(config);
174} 185}
175 186
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index a63a04a25..ce9968724 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -195,7 +195,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
195 config.wall_clock_cntpct = uses_wall_clock; 195 config.wall_clock_cntpct = uses_wall_clock;
196 196
197 // Safe optimizations 197 // Safe optimizations
198 if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) { 198 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
199 if (!Settings::values.cpuopt_page_tables) { 199 if (!Settings::values.cpuopt_page_tables) {
200 config.page_table = nullptr; 200 config.page_table = nullptr;
201 } 201 }
@@ -222,6 +222,17 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
222 } 222 }
223 } 223 }
224 224
225 // Unsafe optimizations
226 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) {
227 config.unsafe_optimizations = true;
228 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
229 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
230 }
231 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
232 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
233 }
234 }
235
225 return std::make_shared<Dynarmic::A64::Jit>(config); 236 return std::make_shared<Dynarmic::A64::Jit>(config);
226} 237}
227 238
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 42277e2cd..c2c0eec0b 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -113,7 +113,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
113 return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName()); 113 return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
114 } 114 }
115 115
116 if (FileUtil::IsDirectory(path)) 116 if (Common::FS::IsDirectory(path))
117 return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read); 117 return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read);
118 118
119 return vfs->OpenFile(path, FileSys::Mode::Read); 119 return vfs->OpenFile(path, FileSys::Mode::Read);
@@ -269,14 +269,14 @@ struct System::Impl {
269 // Log last frame performance stats if game was loded 269 // Log last frame performance stats if game was loded
270 if (perf_stats) { 270 if (perf_stats) {
271 const auto perf_results = GetAndResetPerfStats(); 271 const auto perf_results = GetAndResetPerfStats();
272 telemetry_session->AddField(Telemetry::FieldType::Performance, 272 constexpr auto performance = Common::Telemetry::FieldType::Performance;
273 "Shutdown_EmulationSpeed", 273
274 telemetry_session->AddField(performance, "Shutdown_EmulationSpeed",
274 perf_results.emulation_speed * 100.0); 275 perf_results.emulation_speed * 100.0);
275 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate", 276 telemetry_session->AddField(performance, "Shutdown_Framerate", perf_results.game_fps);
276 perf_results.game_fps); 277 telemetry_session->AddField(performance, "Shutdown_Frametime",
277 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
278 perf_results.frametime * 1000.0); 278 perf_results.frametime * 1000.0);
279 telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS", 279 telemetry_session->AddField(performance, "Mean_Frametime_MS",
280 perf_stats->GetMeanFrametime()); 280 perf_stats->GetMeanFrametime());
281 } 281 }
282 282
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 71af26ec5..e6c8461a5 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -7,14 +7,14 @@
7#include <string> 7#include <string>
8#include <tuple> 8#include <tuple>
9 9
10#include "common/assert.h"
11#include "common/microprofile.h" 10#include "common/microprofile.h"
12#include "core/core_timing.h" 11#include "core/core_timing.h"
13#include "core/core_timing_util.h" 12#include "core/core_timing_util.h"
13#include "core/hardware_properties.h"
14 14
15namespace Core::Timing { 15namespace Core::Timing {
16 16
17constexpr u64 MAX_SLICE_LENGTH = 4000; 17constexpr s64 MAX_SLICE_LENGTH = 4000;
18 18
19std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { 19std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
20 return std::make_shared<EventType>(std::move(callback), std::move(name)); 20 return std::make_shared<EventType>(std::move(callback), std::move(name));
@@ -37,10 +37,8 @@ struct CoreTiming::Event {
37 } 37 }
38}; 38};
39 39
40CoreTiming::CoreTiming() { 40CoreTiming::CoreTiming()
41 clock = 41 : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {}
42 Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ);
43}
44 42
45CoreTiming::~CoreTiming() = default; 43CoreTiming::~CoreTiming() = default;
46 44
@@ -136,7 +134,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
136 134
137void CoreTiming::AddTicks(u64 ticks) { 135void CoreTiming::AddTicks(u64 ticks) {
138 this->ticks += ticks; 136 this->ticks += ticks;
139 downcount -= ticks; 137 downcount -= static_cast<s64>(ticks);
140} 138}
141 139
142void CoreTiming::Idle() { 140void CoreTiming::Idle() {
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp
index aefc63663..8ce8e602e 100644
--- a/src/core/core_timing_util.cpp
+++ b/src/core/core_timing_util.cpp
@@ -8,6 +8,7 @@
8#include <limits> 8#include <limits>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/uint128.h" 10#include "common/uint128.h"
11#include "core/hardware_properties.h"
11 12
12namespace Core::Timing { 13namespace Core::Timing {
13 14
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
index 2ed979e14..e4a046bf9 100644
--- a/src/core/core_timing_util.h
+++ b/src/core/core_timing_util.h
@@ -6,7 +6,6 @@
6 6
7#include <chrono> 7#include <chrono>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hardware_properties.h"
10 9
11namespace Core::Timing { 10namespace Core::Timing {
12 11
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 03651e285..688b99eba 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -41,9 +41,9 @@ void CpuManager::Shutdown() {
41 running_mode = false; 41 running_mode = false;
42 Pause(false); 42 Pause(false);
43 if (is_multicore) { 43 if (is_multicore) {
44 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 44 for (auto& data : core_data) {
45 core_data[core].host_thread->join(); 45 data.host_thread->join();
46 core_data[core].host_thread.reset(); 46 data.host_thread.reset();
47 } 47 }
48 } else { 48 } else {
49 core_data[0].host_thread->join(); 49 core_data[0].host_thread->join();
@@ -166,25 +166,23 @@ void CpuManager::MultiCorePause(bool paused) {
166 bool all_not_barrier = false; 166 bool all_not_barrier = false;
167 while (!all_not_barrier) { 167 while (!all_not_barrier) {
168 all_not_barrier = true; 168 all_not_barrier = true;
169 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 169 for (const auto& data : core_data) {
170 all_not_barrier &= 170 all_not_barrier &= !data.is_running.load() && data.initialized.load();
171 !core_data[core].is_running.load() && core_data[core].initialized.load();
172 } 171 }
173 } 172 }
174 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 173 for (auto& data : core_data) {
175 core_data[core].enter_barrier->Set(); 174 data.enter_barrier->Set();
176 } 175 }
177 if (paused_state.load()) { 176 if (paused_state.load()) {
178 bool all_barrier = false; 177 bool all_barrier = false;
179 while (!all_barrier) { 178 while (!all_barrier) {
180 all_barrier = true; 179 all_barrier = true;
181 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 180 for (const auto& data : core_data) {
182 all_barrier &= 181 all_barrier &= data.is_paused.load() && data.initialized.load();
183 core_data[core].is_paused.load() && core_data[core].initialized.load();
184 } 182 }
185 } 183 }
186 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 184 for (auto& data : core_data) {
187 core_data[core].exit_barrier->Set(); 185 data.exit_barrier->Set();
188 } 186 }
189 } 187 }
190 } else { 188 } else {
@@ -192,9 +190,8 @@ void CpuManager::MultiCorePause(bool paused) {
192 bool all_barrier = false; 190 bool all_barrier = false;
193 while (!all_barrier) { 191 while (!all_barrier) {
194 all_barrier = true; 192 all_barrier = true;
195 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 193 for (const auto& data : core_data) {
196 all_barrier &= 194 all_barrier &= data.is_paused.load() && data.initialized.load();
197 core_data[core].is_paused.load() && core_data[core].initialized.load();
198 } 195 }
199 } 196 }
200 /// Don't release the barrier 197 /// Don't release the barrier
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp
index 330996b24..6a9734812 100644
--- a/src/core/crypto/aes_util.cpp
+++ b/src/core/crypto/aes_util.cpp
@@ -116,7 +116,7 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8*
116 116
117 for (std::size_t i = 0; i < size; i += sector_size) { 117 for (std::size_t i = 0; i < size; i += sector_size) {
118 SetIV(CalculateNintendoTweak(sector_id++)); 118 SetIV(CalculateNintendoTweak(sector_id++));
119 Transcode<u8, u8>(src + i, sector_size, dest + i, op); 119 Transcode(src + i, sector_size, dest + i, op);
120 } 120 }
121} 121}
122 122
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index f87fe0abc..dc591c730 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -36,18 +36,86 @@
36#include "core/settings.h" 36#include "core/settings.h"
37 37
38namespace Core::Crypto { 38namespace Core::Crypto {
39namespace {
39 40
40constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; 41constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
41constexpr u64 FULL_TICKET_SIZE = 0x400; 42constexpr u64 FULL_TICKET_SIZE = 0x400;
42 43
43using namespace Common; 44using Common::AsArray;
44 45
45const std::array<SHA256Hash, 2> eticket_source_hashes{ 46// clang-format off
46 "B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source 47constexpr std::array eticket_source_hashes{
47 "E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source 48 AsArray("B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"), // eticket_rsa_kek_source
49 AsArray("E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"), // eticket_rsa_kekek_source
48}; 50};
51// clang-format on
49 52
50const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ 53constexpr std::array<std::pair<std::string_view, KeyIndex<S128KeyType>>, 30> s128_file_id{{
54 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
55 {"eticket_rsa_kek_source",
56 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
57 {"eticket_rsa_kekek_source",
58 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
59 {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
60 {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
61 {"rsa_oaep_kek_generation_source",
62 {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
63 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
64 {"aes_kek_generation_source",
65 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
66 {"aes_key_generation_source",
67 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
68 {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
69 {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
70 {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
71 {"key_area_key_application_source",
72 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
73 static_cast<u64>(KeyAreaKeyType::Application)}},
74 {"key_area_key_ocean_source",
75 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
76 static_cast<u64>(KeyAreaKeyType::Ocean)}},
77 {"key_area_key_system_source",
78 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
79 static_cast<u64>(KeyAreaKeyType::System)}},
80 {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
81 {"keyblob_mac_key_source",
82 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},
83 {"tsec_key", {S128KeyType::TSEC, 0, 0}},
84 {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
85 {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
86 {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
87 {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
88 {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
89 {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
90 {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
91 {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
92 {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
93 {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
94 {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
95 {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
96}};
97
98auto Find128ByName(std::string_view name) {
99 return std::find_if(s128_file_id.begin(), s128_file_id.end(),
100 [&name](const auto& pair) { return pair.first == name; });
101}
102
103constexpr std::array<std::pair<std::string_view, KeyIndex<S256KeyType>>, 6> s256_file_id{{
104 {"header_key", {S256KeyType::Header, 0, 0}},
105 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
106 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
107 {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
108 {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
109 {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
110}};
111
112auto Find256ByName(std::string_view name) {
113 return std::find_if(s256_file_id.begin(), s256_file_id.end(),
114 [&name](const auto& pair) { return pair.first == name; });
115}
116
117using KeyArray = std::array<std::pair<std::pair<S128KeyType, u64>, std::string_view>, 7>;
118constexpr KeyArray KEYS_VARIABLE_LENGTH{{
51 {{S128KeyType::Master, 0}, "master_key_"}, 119 {{S128KeyType::Master, 0}, "master_key_"},
52 {{S128KeyType::Package1, 0}, "package1_key_"}, 120 {{S128KeyType::Package1, 0}, "package1_key_"},
53 {{S128KeyType::Package2, 0}, "package2_key_"}, 121 {{S128KeyType::Package2, 0}, "package2_key_"},
@@ -55,14 +123,13 @@ const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
55 {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"}, 123 {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"},
56 {{S128KeyType::Keyblob, 0}, "keyblob_key_"}, 124 {{S128KeyType::Keyblob, 0}, "keyblob_key_"},
57 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"}, 125 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
58}; 126}};
59 127
60namespace {
61template <std::size_t Size> 128template <std::size_t Size>
62bool IsAllZeroArray(const std::array<u8, Size>& array) { 129bool IsAllZeroArray(const std::array<u8, Size>& array) {
63 return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; }); 130 return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; });
64} 131}
65} // namespace 132} // Anonymous namespace
66 133
67u64 GetSignatureTypeDataSize(SignatureType type) { 134u64 GetSignatureTypeDataSize(SignatureType type) {
68 switch (type) { 135 switch (type) {
@@ -94,13 +161,13 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
94} 161}
95 162
96SignatureType Ticket::GetSignatureType() const { 163SignatureType Ticket::GetSignatureType() const {
97 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 164 if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
98 return ticket->sig_type; 165 return ticket->sig_type;
99 } 166 }
100 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 167 if (const auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
101 return ticket->sig_type; 168 return ticket->sig_type;
102 } 169 }
103 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 170 if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
104 return ticket->sig_type; 171 return ticket->sig_type;
105 } 172 }
106 173
@@ -108,13 +175,13 @@ SignatureType Ticket::GetSignatureType() const {
108} 175}
109 176
110TicketData& Ticket::GetData() { 177TicketData& Ticket::GetData() {
111 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 178 if (auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
112 return ticket->data; 179 return ticket->data;
113 } 180 }
114 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 181 if (auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
115 return ticket->data; 182 return ticket->data;
116 } 183 }
117 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 184 if (auto* ticket = std::get_if<ECDSATicket>(&data)) {
118 return ticket->data; 185 return ticket->data;
119 } 186 }
120 187
@@ -122,13 +189,13 @@ TicketData& Ticket::GetData() {
122} 189}
123 190
124const TicketData& Ticket::GetData() const { 191const TicketData& Ticket::GetData() const {
125 if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { 192 if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
126 return ticket->data; 193 return ticket->data;
127 } 194 }
128 if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { 195 if (const auto* ticket = std::get_if<RSA2048Ticket>(&data)) {
129 return ticket->data; 196 return ticket->data;
130 } 197 }
131 if (auto ticket = std::get_if<ECDSATicket>(&data)) { 198 if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
132 return ticket->data; 199 return ticket->data;
133 } 200 }
134 201
@@ -231,8 +298,9 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
231} 298}
232 299
233RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { 300RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const {
234 if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) 301 if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) {
235 return {}; 302 return {};
303 }
236 304
237 const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); 305 const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek);
238 306
@@ -259,27 +327,30 @@ Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source)
259} 327}
260 328
261std::optional<Key128> DeriveSDSeed() { 329std::optional<Key128> DeriveSDSeed() {
262 const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 330 const Common::FS::IOFile save_43(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
263 "/system/save/8000000000000043", 331 "/system/save/8000000000000043",
264 "rb+"); 332 "rb+");
265 if (!save_43.IsOpen()) 333 if (!save_43.IsOpen()) {
266 return {}; 334 return std::nullopt;
335 }
267 336
268 const FileUtil::IOFile sd_private( 337 const Common::FS::IOFile sd_private(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
269 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); 338 "/Nintendo/Contents/private",
270 if (!sd_private.IsOpen()) 339 "rb+");
271 return {}; 340 if (!sd_private.IsOpen()) {
341 return std::nullopt;
342 }
272 343
273 std::array<u8, 0x10> private_seed{}; 344 std::array<u8, 0x10> private_seed{};
274 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) { 345 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
275 return {}; 346 return std::nullopt;
276 } 347 }
277 348
278 std::array<u8, 0x10> buffer{}; 349 std::array<u8, 0x10> buffer{};
279 std::size_t offset = 0; 350 std::size_t offset = 0;
280 for (; offset + 0x10 < save_43.GetSize(); ++offset) { 351 for (; offset + 0x10 < save_43.GetSize(); ++offset) {
281 if (!save_43.Seek(offset, SEEK_SET)) { 352 if (!save_43.Seek(offset, SEEK_SET)) {
282 return {}; 353 return std::nullopt;
283 } 354 }
284 355
285 save_43.ReadBytes(buffer.data(), buffer.size()); 356 save_43.ReadBytes(buffer.data(), buffer.size());
@@ -289,23 +360,26 @@ std::optional<Key128> DeriveSDSeed() {
289 } 360 }
290 361
291 if (!save_43.Seek(offset + 0x10, SEEK_SET)) { 362 if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
292 return {}; 363 return std::nullopt;
293 } 364 }
294 365
295 Key128 seed{}; 366 Key128 seed{};
296 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) { 367 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
297 return {}; 368 return std::nullopt;
298 } 369 }
299 return seed; 370 return seed;
300} 371}
301 372
302Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) { 373Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) {
303 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek))) 374 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek))) {
304 return Loader::ResultStatus::ErrorMissingSDKEKSource; 375 return Loader::ResultStatus::ErrorMissingSDKEKSource;
305 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration))) 376 }
377 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration))) {
306 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource; 378 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource;
307 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) 379 }
380 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) {
308 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource; 381 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource;
382 }
309 383
310 const auto sd_kek_source = 384 const auto sd_kek_source =
311 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek)); 385 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek));
@@ -318,14 +392,17 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
318 GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen); 392 GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen);
319 keys.SetKey(S128KeyType::SDKek, sd_kek); 393 keys.SetKey(S128KeyType::SDKek, sd_kek);
320 394
321 if (!keys.HasKey(S128KeyType::SDSeed)) 395 if (!keys.HasKey(S128KeyType::SDSeed)) {
322 return Loader::ResultStatus::ErrorMissingSDSeed; 396 return Loader::ResultStatus::ErrorMissingSDSeed;
397 }
323 const auto sd_seed = keys.GetKey(S128KeyType::SDSeed); 398 const auto sd_seed = keys.GetKey(S128KeyType::SDSeed);
324 399
325 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save))) 400 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save))) {
326 return Loader::ResultStatus::ErrorMissingSDSaveKeySource; 401 return Loader::ResultStatus::ErrorMissingSDSaveKeySource;
327 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA))) 402 }
403 if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA))) {
328 return Loader::ResultStatus::ErrorMissingSDNCAKeySource; 404 return Loader::ResultStatus::ErrorMissingSDNCAKeySource;
405 }
329 406
330 std::array<Key256, 2> sd_key_sources{ 407 std::array<Key256, 2> sd_key_sources{
331 keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)), 408 keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)),
@@ -334,8 +411,9 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
334 411
335 // Combine sources and seed 412 // Combine sources and seed
336 for (auto& source : sd_key_sources) { 413 for (auto& source : sd_key_sources) {
337 for (std::size_t i = 0; i < source.size(); ++i) 414 for (std::size_t i = 0; i < source.size(); ++i) {
338 source[i] ^= sd_seed[i & 0xF]; 415 source[i] ^= sd_seed[i & 0xF];
416 }
339 } 417 }
340 418
341 AESCipher<Key128> cipher(sd_kek, Mode::ECB); 419 AESCipher<Key128> cipher(sd_kek, Mode::ECB);
@@ -353,9 +431,10 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
353 return Loader::ResultStatus::Success; 431 return Loader::ResultStatus::Success;
354} 432}
355 433
356std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save) { 434std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save) {
357 if (!ticket_save.IsOpen()) 435 if (!ticket_save.IsOpen()) {
358 return {}; 436 return {};
437 }
359 438
360 std::vector<u8> buffer(ticket_save.GetSize()); 439 std::vector<u8> buffer(ticket_save.GetSize());
361 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) { 440 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
@@ -415,7 +494,7 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
415 offset = i + 1; 494 offset = i + 1;
416 break; 495 break;
417 } else if (data[i] != 0x0) { 496 } else if (data[i] != 0x0) {
418 return {}; 497 return std::nullopt;
419 } 498 }
420 } 499 }
421 500
@@ -425,16 +504,18 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
425std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, 504std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
426 const RSAKeyPair<2048>& key) { 505 const RSAKeyPair<2048>& key) {
427 const auto issuer = ticket.GetData().issuer; 506 const auto issuer = ticket.GetData().issuer;
428 if (IsAllZeroArray(issuer)) 507 if (IsAllZeroArray(issuer)) {
429 return {}; 508 return std::nullopt;
509 }
430 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { 510 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') {
431 LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); 511 LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority.");
432 } 512 }
433 513
434 Key128 rights_id = ticket.GetData().rights_id; 514 Key128 rights_id = ticket.GetData().rights_id;
435 515
436 if (rights_id == Key128{}) 516 if (rights_id == Key128{}) {
437 return {}; 517 return std::nullopt;
518 }
438 519
439 if (!std::any_of(ticket.GetData().title_key_common_pad.begin(), 520 if (!std::any_of(ticket.GetData().title_key_common_pad.begin(),
440 ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) { 521 ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) {
@@ -466,15 +547,17 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
466 std::array<u8, 0xDF> m_2; 547 std::array<u8, 0xDF> m_2;
467 std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size()); 548 std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
468 549
469 if (m_0 != 0) 550 if (m_0 != 0) {
470 return {}; 551 return std::nullopt;
552 }
471 553
472 m_1 = m_1 ^ MGF1<0x20>(m_2); 554 m_1 = m_1 ^ MGF1<0x20>(m_2);
473 m_2 = m_2 ^ MGF1<0xDF>(m_1); 555 m_2 = m_2 ^ MGF1<0xDF>(m_1);
474 556
475 const auto offset = FindTicketOffset(m_2); 557 const auto offset = FindTicketOffset(m_2);
476 if (!offset) 558 if (!offset) {
477 return {}; 559 return std::nullopt;
560 }
478 ASSERT(*offset > 0); 561 ASSERT(*offset > 0);
479 562
480 Key128 key_temp{}; 563 Key128 key_temp{};
@@ -485,8 +568,8 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
485 568
486KeyManager::KeyManager() { 569KeyManager::KeyManager() {
487 // Initialize keys 570 // Initialize keys
488 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); 571 const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
489 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 572 const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
490 if (Settings::values.use_dev_keys) { 573 if (Settings::values.use_dev_keys) {
491 dev_mode = true; 574 dev_mode = true;
492 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false); 575 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);
@@ -504,34 +587,39 @@ KeyManager::KeyManager() {
504} 587}
505 588
506static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) { 589static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
507 if (base.size() < begin + length) 590 if (base.size() < begin + length) {
508 return false; 591 return false;
592 }
509 return std::all_of(base.begin() + begin, base.begin() + begin + length, 593 return std::all_of(base.begin() + begin, base.begin() + begin + length,
510 [](u8 c) { return std::isxdigit(c); }); 594 [](u8 c) { return std::isxdigit(c); });
511} 595}
512 596
513void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { 597void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
514 std::ifstream file; 598 std::ifstream file;
515 OpenFStream(file, filename, std::ios_base::in); 599 Common::FS::OpenFStream(file, filename, std::ios_base::in);
516 if (!file.is_open()) 600 if (!file.is_open()) {
517 return; 601 return;
602 }
518 603
519 std::string line; 604 std::string line;
520 while (std::getline(file, line)) { 605 while (std::getline(file, line)) {
521 std::vector<std::string> out; 606 std::vector<std::string> out;
522 std::stringstream stream(line); 607 std::stringstream stream(line);
523 std::string item; 608 std::string item;
524 while (std::getline(stream, item, '=')) 609 while (std::getline(stream, item, '=')) {
525 out.push_back(std::move(item)); 610 out.push_back(std::move(item));
611 }
526 612
527 if (out.size() != 2) 613 if (out.size() != 2) {
528 continue; 614 continue;
615 }
529 616
530 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end()); 617 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
531 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end()); 618 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
532 619
533 if (out[0].compare(0, 1, "#") == 0) 620 if (out[0].compare(0, 1, "#") == 0) {
534 continue; 621 continue;
622 }
535 623
536 if (is_title_keys) { 624 if (is_title_keys) {
537 auto rights_id_raw = Common::HexStringToArray<16>(out[0]); 625 auto rights_id_raw = Common::HexStringToArray<16>(out[0]);
@@ -541,24 +629,26 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
541 s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key; 629 s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
542 } else { 630 } else {
543 out[0] = Common::ToLower(out[0]); 631 out[0] = Common::ToLower(out[0]);
544 if (s128_file_id.find(out[0]) != s128_file_id.end()) { 632 if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
545 const auto index = s128_file_id.at(out[0]); 633 const auto& index = iter128->second;
546 Key128 key = Common::HexStringToArray<16>(out[1]); 634 const Key128 key = Common::HexStringToArray<16>(out[1]);
547 s128_keys[{index.type, index.field1, index.field2}] = key; 635 s128_keys[{index.type, index.field1, index.field2}] = key;
548 } else if (s256_file_id.find(out[0]) != s256_file_id.end()) { 636 } else if (const auto iter256 = Find256ByName(out[0]); iter256 != s256_file_id.end()) {
549 const auto index = s256_file_id.at(out[0]); 637 const auto& index = iter256->second;
550 Key256 key = Common::HexStringToArray<32>(out[1]); 638 const Key256 key = Common::HexStringToArray<32>(out[1]);
551 s256_keys[{index.type, index.field1, index.field2}] = key; 639 s256_keys[{index.type, index.field1, index.field2}] = key;
552 } else if (out[0].compare(0, 8, "keyblob_") == 0 && 640 } else if (out[0].compare(0, 8, "keyblob_") == 0 &&
553 out[0].compare(0, 9, "keyblob_k") != 0) { 641 out[0].compare(0, 9, "keyblob_k") != 0) {
554 if (!ValidCryptoRevisionString(out[0], 8, 2)) 642 if (!ValidCryptoRevisionString(out[0], 8, 2)) {
555 continue; 643 continue;
644 }
556 645
557 const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16); 646 const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16);
558 keyblobs[index] = Common::HexStringToArray<0x90>(out[1]); 647 keyblobs[index] = Common::HexStringToArray<0x90>(out[1]);
559 } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) { 648 } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) {
560 if (!ValidCryptoRevisionString(out[0], 18, 2)) 649 if (!ValidCryptoRevisionString(out[0], 18, 2)) {
561 continue; 650 continue;
651 }
562 652
563 const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16); 653 const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16);
564 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); 654 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
@@ -566,8 +656,9 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
566 eticket_extended_kek = Common::HexStringToArray<576>(out[1]); 656 eticket_extended_kek = Common::HexStringToArray<576>(out[1]);
567 } else { 657 } else {
568 for (const auto& kv : KEYS_VARIABLE_LENGTH) { 658 for (const auto& kv : KEYS_VARIABLE_LENGTH) {
569 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) 659 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) {
570 continue; 660 continue;
661 }
571 if (out[0].compare(0, kv.second.size(), kv.second) == 0) { 662 if (out[0].compare(0, kv.second.size(), kv.second) == 0) {
572 const auto index = 663 const auto index =
573 std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16); 664 std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16);
@@ -602,10 +693,11 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
602 693
603void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, 694void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
604 const std::string& filename, bool title) { 695 const std::string& filename, bool title) {
605 if (FileUtil::Exists(dir1 + DIR_SEP + filename)) 696 if (Common::FS::Exists(dir1 + DIR_SEP + filename)) {
606 LoadFromFile(dir1 + DIR_SEP + filename, title); 697 LoadFromFile(dir1 + DIR_SEP + filename, title);
607 else if (FileUtil::Exists(dir2 + DIR_SEP + filename)) 698 } else if (Common::FS::Exists(dir2 + DIR_SEP + filename)) {
608 LoadFromFile(dir2 + DIR_SEP + filename, title); 699 LoadFromFile(dir2 + DIR_SEP + filename, title);
700 }
609} 701}
610 702
611bool KeyManager::BaseDeriveNecessary() const { 703bool KeyManager::BaseDeriveNecessary() const {
@@ -613,8 +705,9 @@ bool KeyManager::BaseDeriveNecessary() const {
613 return !HasKey(key_type, index1, index2); 705 return !HasKey(key_type, index1, index2);
614 }; 706 };
615 707
616 if (check_key_existence(S256KeyType::Header)) 708 if (check_key_existence(S256KeyType::Header)) {
617 return true; 709 return true;
710 }
618 711
619 for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) { 712 for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) {
620 if (check_key_existence(S128KeyType::Master, i) || 713 if (check_key_existence(S128KeyType::Master, i) ||
@@ -639,14 +732,16 @@ bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) const {
639} 732}
640 733
641Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const { 734Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const {
642 if (!HasKey(id, field1, field2)) 735 if (!HasKey(id, field1, field2)) {
643 return {}; 736 return {};
737 }
644 return s128_keys.at({id, field1, field2}); 738 return s128_keys.at({id, field1, field2});
645} 739}
646 740
647Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const { 741Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
648 if (!HasKey(id, field1, field2)) 742 if (!HasKey(id, field1, field2)) {
649 return {}; 743 return {};
744 }
650 return s256_keys.at({id, field1, field2}); 745 return s256_keys.at({id, field1, field2});
651} 746}
652 747
@@ -668,7 +763,7 @@ Key256 KeyManager::GetBISKey(u8 partition_id) const {
668template <size_t Size> 763template <size_t Size>
669void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname, 764void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
670 const std::array<u8, Size>& key) { 765 const std::array<u8, Size>& key) {
671 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 766 const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
672 std::string filename = "title.keys_autogenerated"; 767 std::string filename = "title.keys_autogenerated";
673 if (category == KeyCategory::Standard) { 768 if (category == KeyCategory::Standard) {
674 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated"; 769 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
@@ -677,9 +772,9 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
677 } 772 }
678 773
679 const auto path = yuzu_keys_dir + DIR_SEP + filename; 774 const auto path = yuzu_keys_dir + DIR_SEP + filename;
680 const auto add_info_text = !FileUtil::Exists(path); 775 const auto add_info_text = !Common::FS::Exists(path);
681 FileUtil::CreateFullPath(path); 776 Common::FS::CreateFullPath(path);
682 FileUtil::IOFile file{path, "a"}; 777 Common::FS::IOFile file{path, "a"};
683 if (!file.IsOpen()) { 778 if (!file.IsOpen()) {
684 return; 779 return;
685 } 780 }
@@ -712,8 +807,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
712 } 807 }
713 808
714 const auto iter2 = std::find_if( 809 const auto iter2 = std::find_if(
715 s128_file_id.begin(), s128_file_id.end(), 810 s128_file_id.begin(), s128_file_id.end(), [&id, &field1, &field2](const auto& elem) {
716 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
717 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == 811 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
718 std::tie(id, field1, field2); 812 std::tie(id, field1, field2);
719 }); 813 });
@@ -723,9 +817,11 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
723 817
724 // Variable cases 818 // Variable cases
725 if (id == S128KeyType::KeyArea) { 819 if (id == S128KeyType::KeyArea) {
726 static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}", 820 static constexpr std::array<const char*, 3> kak_names = {
727 "key_area_key_ocean_{:02X}", 821 "key_area_key_application_{:02X}",
728 "key_area_key_system_{:02X}"}; 822 "key_area_key_ocean_{:02X}",
823 "key_area_key_system_{:02X}",
824 };
729 WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key); 825 WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key);
730 } else if (id == S128KeyType::Master) { 826 } else if (id == S128KeyType::Master) {
731 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key); 827 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
@@ -751,8 +847,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
751 return; 847 return;
752 } 848 }
753 const auto iter = std::find_if( 849 const auto iter = std::find_if(
754 s256_file_id.begin(), s256_file_id.end(), 850 s256_file_id.begin(), s256_file_id.end(), [&id, &field1, &field2](const auto& elem) {
755 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
756 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == 851 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
757 std::tie(id, field1, field2); 852 std::tie(id, field1, field2);
758 }); 853 });
@@ -763,29 +858,31 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
763} 858}
764 859
765bool KeyManager::KeyFileExists(bool title) { 860bool KeyManager::KeyFileExists(bool title) {
766 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); 861 const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
767 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 862 const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
768 if (title) { 863 if (title) {
769 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "title.keys") || 864 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "title.keys") ||
770 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "title.keys"); 865 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "title.keys");
771 } 866 }
772 867
773 if (Settings::values.use_dev_keys) { 868 if (Settings::values.use_dev_keys) {
774 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") || 869 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") ||
775 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys"); 870 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys");
776 } 871 }
777 872
778 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") || 873 return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") ||
779 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys"); 874 Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys");
780} 875}
781 876
782void KeyManager::DeriveSDSeedLazy() { 877void KeyManager::DeriveSDSeedLazy() {
783 if (HasKey(S128KeyType::SDSeed)) 878 if (HasKey(S128KeyType::SDSeed)) {
784 return; 879 return;
880 }
785 881
786 const auto res = DeriveSDSeed(); 882 const auto res = DeriveSDSeed();
787 if (res) 883 if (res) {
788 SetKey(S128KeyType::SDSeed, *res); 884 SetKey(S128KeyType::SDSeed, *res);
885 }
789} 886}
790 887
791static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) { 888static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
@@ -797,11 +894,13 @@ static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
797} 894}
798 895
799void KeyManager::DeriveBase() { 896void KeyManager::DeriveBase() {
800 if (!BaseDeriveNecessary()) 897 if (!BaseDeriveNecessary()) {
801 return; 898 return;
899 }
802 900
803 if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC)) 901 if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC)) {
804 return; 902 return;
903 }
805 904
806 const auto has_bis = [this](u64 id) { 905 const auto has_bis = [this](u64 id) {
807 return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) && 906 return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) &&
@@ -818,10 +917,11 @@ void KeyManager::DeriveBase() {
818 static_cast<u64>(BISKeyType::Tweak)); 917 static_cast<u64>(BISKeyType::Tweak));
819 }; 918 };
820 919
821 if (has_bis(2) && !has_bis(3)) 920 if (has_bis(2) && !has_bis(3)) {
822 copy_bis(2, 3); 921 copy_bis(2, 3);
823 else if (has_bis(3) && !has_bis(2)) 922 } else if (has_bis(3) && !has_bis(2)) {
824 copy_bis(3, 2); 923 copy_bis(3, 2);
924 }
825 925
826 std::bitset<32> revisions(0xFFFFFFFF); 926 std::bitset<32> revisions(0xFFFFFFFF);
827 for (size_t i = 0; i < revisions.size(); ++i) { 927 for (size_t i = 0; i < revisions.size(); ++i) {
@@ -831,15 +931,17 @@ void KeyManager::DeriveBase() {
831 } 931 }
832 } 932 }
833 933
834 if (!revisions.any()) 934 if (!revisions.any()) {
835 return; 935 return;
936 }
836 937
837 const auto sbk = GetKey(S128KeyType::SecureBoot); 938 const auto sbk = GetKey(S128KeyType::SecureBoot);
838 const auto tsec = GetKey(S128KeyType::TSEC); 939 const auto tsec = GetKey(S128KeyType::TSEC);
839 940
840 for (size_t i = 0; i < revisions.size(); ++i) { 941 for (size_t i = 0; i < revisions.size(); ++i) {
841 if (!revisions[i]) 942 if (!revisions[i]) {
842 continue; 943 continue;
944 }
843 945
844 // Derive keyblob key 946 // Derive keyblob key
845 const auto key = DeriveKeyblobKey( 947 const auto key = DeriveKeyblobKey(
@@ -848,16 +950,18 @@ void KeyManager::DeriveBase() {
848 SetKey(S128KeyType::Keyblob, key, i); 950 SetKey(S128KeyType::Keyblob, key, i);
849 951
850 // Derive keyblob MAC key 952 // Derive keyblob MAC key
851 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))) 953 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))) {
852 continue; 954 continue;
955 }
853 956
854 const auto mac_key = DeriveKeyblobMACKey( 957 const auto mac_key = DeriveKeyblobMACKey(
855 key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))); 958 key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)));
856 SetKey(S128KeyType::KeyblobMAC, mac_key, i); 959 SetKey(S128KeyType::KeyblobMAC, mac_key, i);
857 960
858 Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key); 961 Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key);
859 if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0) 962 if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0) {
860 continue; 963 continue;
964 }
861 965
862 // Decrypt keyblob 966 // Decrypt keyblob
863 if (keyblobs[i] == std::array<u8, 0x90>{}) { 967 if (keyblobs[i] == std::array<u8, 0x90>{}) {
@@ -881,16 +985,19 @@ void KeyManager::DeriveBase() {
881 985
882 revisions.set(); 986 revisions.set();
883 for (size_t i = 0; i < revisions.size(); ++i) { 987 for (size_t i = 0; i < revisions.size(); ++i) {
884 if (!HasKey(S128KeyType::Master, i)) 988 if (!HasKey(S128KeyType::Master, i)) {
885 revisions.reset(i); 989 revisions.reset(i);
990 }
886 } 991 }
887 992
888 if (!revisions.any()) 993 if (!revisions.any()) {
889 return; 994 return;
995 }
890 996
891 for (size_t i = 0; i < revisions.size(); ++i) { 997 for (size_t i = 0; i < revisions.size(); ++i) {
892 if (!revisions[i]) 998 if (!revisions[i]) {
893 continue; 999 continue;
1000 }
894 1001
895 // Derive general purpose keys 1002 // Derive general purpose keys
896 DeriveGeneralPurposeKeys(i); 1003 DeriveGeneralPurposeKeys(i);
@@ -920,16 +1027,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
920 const auto es = Core::System::GetInstance().GetContentProvider().GetEntry( 1027 const auto es = Core::System::GetInstance().GetContentProvider().GetEntry(
921 0x0100000000000033, FileSys::ContentRecordType::Program); 1028 0x0100000000000033, FileSys::ContentRecordType::Program);
922 1029
923 if (es == nullptr) 1030 if (es == nullptr) {
924 return; 1031 return;
1032 }
925 1033
926 const auto exefs = es->GetExeFS(); 1034 const auto exefs = es->GetExeFS();
927 if (exefs == nullptr) 1035 if (exefs == nullptr) {
928 return; 1036 return;
1037 }
929 1038
930 const auto main = exefs->GetFile("main"); 1039 const auto main = exefs->GetFile("main");
931 if (main == nullptr) 1040 if (main == nullptr) {
932 return; 1041 return;
1042 }
933 1043
934 const auto bytes = main->ReadAllBytes(); 1044 const auto bytes = main->ReadAllBytes();
935 1045
@@ -939,16 +1049,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
939 const auto seed3 = data.GetRSAKekSeed3(); 1049 const auto seed3 = data.GetRSAKekSeed3();
940 const auto mask0 = data.GetRSAKekMask0(); 1050 const auto mask0 = data.GetRSAKekMask0();
941 1051
942 if (eticket_kek != Key128{}) 1052 if (eticket_kek != Key128{}) {
943 SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek)); 1053 SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek));
1054 }
944 if (eticket_kekek != Key128{}) { 1055 if (eticket_kekek != Key128{}) {
945 SetKey(S128KeyType::Source, eticket_kekek, 1056 SetKey(S128KeyType::Source, eticket_kekek,
946 static_cast<size_t>(SourceKeyType::ETicketKekek)); 1057 static_cast<size_t>(SourceKeyType::ETicketKekek));
947 } 1058 }
948 if (seed3 != Key128{}) 1059 if (seed3 != Key128{}) {
949 SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3)); 1060 SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3));
950 if (mask0 != Key128{}) 1061 }
1062 if (mask0 != Key128{}) {
951 SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0)); 1063 SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0));
1064 }
952 if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} || 1065 if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} ||
953 mask0 == Key128{}) { 1066 mask0 == Key128{}) {
954 return; 1067 return;
@@ -974,8 +1087,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
974 AESCipher<Key128> es_kek(temp_kekek, Mode::ECB); 1087 AESCipher<Key128> es_kek(temp_kekek, Mode::ECB);
975 es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt); 1088 es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt);
976 1089
977 if (eticket_final == Key128{}) 1090 if (eticket_final == Key128{}) {
978 return; 1091 return;
1092 }
979 1093
980 SetKey(S128KeyType::ETicketRSAKek, eticket_final); 1094 SetKey(S128KeyType::ETicketRSAKek, eticket_final);
981 1095
@@ -990,18 +1104,20 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
990void KeyManager::PopulateTickets() { 1104void KeyManager::PopulateTickets() {
991 const auto rsa_key = GetETicketRSAKey(); 1105 const auto rsa_key = GetETicketRSAKey();
992 1106
993 if (rsa_key == RSAKeyPair<2048>{}) 1107 if (rsa_key == RSAKeyPair<2048>{}) {
994 return; 1108 return;
1109 }
995 1110
996 if (!common_tickets.empty() && !personal_tickets.empty()) 1111 if (!common_tickets.empty() && !personal_tickets.empty()) {
997 return; 1112 return;
1113 }
998 1114
999 const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1115 const Common::FS::IOFile save1(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1000 "/system/save/80000000000000e1", 1116 "/system/save/80000000000000e1",
1001 "rb+"); 1117 "rb+");
1002 const FileUtil::IOFile save2(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1118 const Common::FS::IOFile save2(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1003 "/system/save/80000000000000e2", 1119 "/system/save/80000000000000e2",
1004 "rb+"); 1120 "rb+");
1005 1121
1006 const auto blob2 = GetTicketblob(save2); 1122 const auto blob2 = GetTicketblob(save2);
1007 auto res = GetTicketblob(save1); 1123 auto res = GetTicketblob(save1);
@@ -1011,8 +1127,10 @@ void KeyManager::PopulateTickets() {
1011 for (std::size_t i = 0; i < res.size(); ++i) { 1127 for (std::size_t i = 0; i < res.size(); ++i) {
1012 const auto common = i < idx; 1128 const auto common = i < idx;
1013 const auto pair = ParseTicket(res[i], rsa_key); 1129 const auto pair = ParseTicket(res[i], rsa_key);
1014 if (!pair) 1130 if (!pair) {
1015 continue; 1131 continue;
1132 }
1133
1016 const auto& [rid, key] = *pair; 1134 const auto& [rid, key] = *pair;
1017 u128 rights_id; 1135 u128 rights_id;
1018 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1136 std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1041,27 +1159,33 @@ void KeyManager::SynthesizeTickets() {
1041} 1159}
1042 1160
1043void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) { 1161void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
1044 if (key == Key128{}) 1162 if (key == Key128{}) {
1045 return; 1163 return;
1164 }
1046 SetKey(id, key, field1, field2); 1165 SetKey(id, key, field1, field2);
1047} 1166}
1048 1167
1049void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) { 1168void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) {
1050 if (key == Key256{}) 1169 if (key == Key256{}) {
1051 return; 1170 return;
1171 }
1172
1052 SetKey(id, key, field1, field2); 1173 SetKey(id, key, field1, field2);
1053} 1174}
1054 1175
1055void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) { 1176void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1056 if (!BaseDeriveNecessary()) 1177 if (!BaseDeriveNecessary()) {
1057 return; 1178 return;
1179 }
1058 1180
1059 if (!data.HasBoot0()) 1181 if (!data.HasBoot0()) {
1060 return; 1182 return;
1183 }
1061 1184
1062 for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) { 1185 for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) {
1063 if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{}) 1186 if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{}) {
1064 continue; 1187 continue;
1188 }
1065 encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i); 1189 encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i);
1066 WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i), 1190 WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i),
1067 encrypted_keyblobs[i]); 1191 encrypted_keyblobs[i]);
@@ -1083,8 +1207,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1083 static_cast<u64>(SourceKeyType::Keyblob), i); 1207 static_cast<u64>(SourceKeyType::Keyblob), i);
1084 } 1208 }
1085 1209
1086 if (data.HasFuses()) 1210 if (data.HasFuses()) {
1087 SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey()); 1211 SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey());
1212 }
1088 1213
1089 DeriveBase(); 1214 DeriveBase();
1090 1215
@@ -1098,8 +1223,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1098 1223
1099 const auto masters = data.GetTZMasterKeys(latest_master); 1224 const auto masters = data.GetTZMasterKeys(latest_master);
1100 for (size_t i = 0; i < masters.size(); ++i) { 1225 for (size_t i = 0; i < masters.size(); ++i) {
1101 if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i)) 1226 if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i)) {
1102 SetKey(S128KeyType::Master, masters[i], i); 1227 SetKey(S128KeyType::Master, masters[i], i);
1228 }
1103 } 1229 }
1104 1230
1105 DeriveBase(); 1231 DeriveBase();
@@ -1109,8 +1235,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1109 1235
1110 std::array<Key128, 0x20> package2_keys{}; 1236 std::array<Key128, 0x20> package2_keys{};
1111 for (size_t i = 0; i < package2_keys.size(); ++i) { 1237 for (size_t i = 0; i < package2_keys.size(); ++i) {
1112 if (HasKey(S128KeyType::Package2, i)) 1238 if (HasKey(S128KeyType::Package2, i)) {
1113 package2_keys[i] = GetKey(S128KeyType::Package2, i); 1239 package2_keys[i] = GetKey(S128KeyType::Package2, i);
1240 }
1114 } 1241 }
1115 data.DecryptPackage2(package2_keys, Package2Type::NormalMain); 1242 data.DecryptPackage2(package2_keys, Package2Type::NormalMain);
1116 1243
@@ -1148,12 +1275,15 @@ const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const {
1148 1275
1149bool KeyManager::AddTicketCommon(Ticket raw) { 1276bool KeyManager::AddTicketCommon(Ticket raw) {
1150 const auto rsa_key = GetETicketRSAKey(); 1277 const auto rsa_key = GetETicketRSAKey();
1151 if (rsa_key == RSAKeyPair<2048>{}) 1278 if (rsa_key == RSAKeyPair<2048>{}) {
1152 return false; 1279 return false;
1280 }
1153 1281
1154 const auto pair = ParseTicket(raw, rsa_key); 1282 const auto pair = ParseTicket(raw, rsa_key);
1155 if (!pair) 1283 if (!pair) {
1156 return false; 1284 return false;
1285 }
1286
1157 const auto& [rid, key] = *pair; 1287 const auto& [rid, key] = *pair;
1158 u128 rights_id; 1288 u128 rights_id;
1159 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1289 std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1164,12 +1294,15 @@ bool KeyManager::AddTicketCommon(Ticket raw) {
1164 1294
1165bool KeyManager::AddTicketPersonalized(Ticket raw) { 1295bool KeyManager::AddTicketPersonalized(Ticket raw) {
1166 const auto rsa_key = GetETicketRSAKey(); 1296 const auto rsa_key = GetETicketRSAKey();
1167 if (rsa_key == RSAKeyPair<2048>{}) 1297 if (rsa_key == RSAKeyPair<2048>{}) {
1168 return false; 1298 return false;
1299 }
1169 1300
1170 const auto pair = ParseTicket(raw, rsa_key); 1301 const auto pair = ParseTicket(raw, rsa_key);
1171 if (!pair) 1302 if (!pair) {
1172 return false; 1303 return false;
1304 }
1305
1173 const auto& [rid, key] = *pair; 1306 const auto& [rid, key] = *pair;
1174 u128 rights_id; 1307 u128 rights_id;
1175 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1308 std::memcpy(rights_id.data(), rid.data(), rid.size());
@@ -1177,58 +1310,4 @@ bool KeyManager::AddTicketPersonalized(Ticket raw) {
1177 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); 1310 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
1178 return true; 1311 return true;
1179} 1312}
1180
1181const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
1182 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
1183 {"eticket_rsa_kek_source",
1184 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
1185 {"eticket_rsa_kekek_source",
1186 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
1187 {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
1188 {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
1189 {"rsa_oaep_kek_generation_source",
1190 {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
1191 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
1192 {"aes_kek_generation_source",
1193 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
1194 {"aes_key_generation_source",
1195 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
1196 {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
1197 {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
1198 {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
1199 {"key_area_key_application_source",
1200 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1201 static_cast<u64>(KeyAreaKeyType::Application)}},
1202 {"key_area_key_ocean_source",
1203 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1204 static_cast<u64>(KeyAreaKeyType::Ocean)}},
1205 {"key_area_key_system_source",
1206 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1207 static_cast<u64>(KeyAreaKeyType::System)}},
1208 {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
1209 {"keyblob_mac_key_source",
1210 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},
1211 {"tsec_key", {S128KeyType::TSEC, 0, 0}},
1212 {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
1213 {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
1214 {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
1215 {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
1216 {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
1217 {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
1218 {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
1219 {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
1220 {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
1221 {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
1222 {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
1223 {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
1224};
1225
1226const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
1227 {"header_key", {S256KeyType::Header, 0, 0}},
1228 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
1229 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
1230 {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
1231 {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
1232 {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
1233};
1234} // namespace Core::Crypto 1313} // namespace Core::Crypto
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 9269a73f2..321b75323 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -10,14 +10,13 @@
10#include <string> 10#include <string>
11 11
12#include <variant> 12#include <variant>
13#include <boost/container/flat_map.hpp>
14#include <fmt/format.h> 13#include <fmt/format.h>
15#include "common/common_funcs.h" 14#include "common/common_funcs.h"
16#include "common/common_types.h" 15#include "common/common_types.h"
17#include "core/crypto/partition_data_manager.h" 16#include "core/crypto/partition_data_manager.h"
18#include "core/file_sys/vfs_types.h" 17#include "core/file_sys/vfs_types.h"
19 18
20namespace FileUtil { 19namespace Common::FS {
21class IOFile; 20class IOFile;
22} 21}
23 22
@@ -293,9 +292,6 @@ private:
293 292
294 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); 293 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
295 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); 294 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
296
297 static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
298 static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
299}; 295};
300 296
301Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); 297Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
@@ -308,7 +304,7 @@ std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblo
308std::optional<Key128> DeriveSDSeed(); 304std::optional<Key128> DeriveSDSeed();
309Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys); 305Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
310 306
311std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save); 307std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save);
312 308
313// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority 309// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority
314// (offset 0x140-0x144 is zero) 310// (offset 0x140-0x144 is zero)
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index b31a81560..46136d04a 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -27,7 +27,7 @@
27#include "core/file_sys/vfs_offset.h" 27#include "core/file_sys/vfs_offset.h"
28#include "core/file_sys/vfs_vector.h" 28#include "core/file_sys/vfs_vector.h"
29 29
30using namespace Common; 30using Common::AsArray;
31 31
32namespace Core::Crypto { 32namespace Core::Crypto {
33 33
@@ -47,105 +47,123 @@ struct Package2Header {
47}; 47};
48static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); 48static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
49 49
50const std::array<SHA256Hash, 0x10> source_hashes{ 50// clang-format off
51 "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source 51constexpr std::array source_hashes{
52 "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source 52 AsArray("B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"), // keyblob_mac_key_source
53 "21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source 53 AsArray("7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"), // master_key_source
54 "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source 54 AsArray("21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"), // package2_key_source
55 "FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source 55 AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // aes_kek_generation_source
56 "C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source 56 AsArray("FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"), // aes_key_generation_source
57 "04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source 57 AsArray("C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"), // titlekek_source
58 "FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source 58 AsArray("04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"), // key_area_key_application_source
59 "1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source 59 AsArray("FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"), // key_area_key_ocean_source
60 "6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source 60 AsArray("1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"), // key_area_key_system_source
61 "D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source 61 AsArray("6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"), // sd_card_kek_source
62 "2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source 62 AsArray("D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"), // sd_card_save_key_source
63 "1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source 63 AsArray("2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"), // sd_card_nca_key_source
64 "8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source 64 AsArray("1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"), // header_kek_source
65 "D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3 65 AsArray("8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"), // header_key_source
66 "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0 66 AsArray("D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"), // rsa_kek_seed3
67 AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // rsa_kek_mask0
67}; 68};
68 69// clang-format on
69const std::array<SHA256Hash, 0x20> keyblob_source_hashes{ 70
70 "8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00 71// clang-format off
71 "2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01 72constexpr std::array keyblob_source_hashes{
72 "61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02 73 AsArray("8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"), // keyblob_key_source_00
73 "8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03 74 AsArray("2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"), // keyblob_key_source_01
74 "95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04 75 AsArray("61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"), // keyblob_key_source_02
75 "3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05 76 AsArray("8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"), // keyblob_key_source_03
76 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06 77 AsArray("95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"), // keyblob_key_source_04
77 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07 78 AsArray("3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"), // keyblob_key_source_05
78 79 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_06
79 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08 80 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_07
80 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09 81
81 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A 82 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_08
82 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B 83 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_09
83 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C 84 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0A
84 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D 85 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0B
85 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E 86 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0C
86 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F 87 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0D
87 88 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0E
88 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10 89 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0F
89 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11 90
90 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12 91 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_10
91 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13 92 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_11
92 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14 93 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_12
93 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15 94 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_13
94 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16 95 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_14
95 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17 96 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_15
96 97 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_16
97 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18 98 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_17
98 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19 99
99 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A 100 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_18
100 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B 101 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_19
101 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C 102 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1A
102 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D 103 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1B
103 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E 104 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1C
104 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F 105 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1D
106 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1E
107 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1F
105}; 108};
106 109// clang-format on
107const std::array<SHA256Hash, 0x20> master_key_hashes{ 110
108 "0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00 111// clang-format off
109 "4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01 112constexpr std::array master_key_hashes{
110 "79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02 113 AsArray("0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"), // master_key_00
111 "4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03 114 AsArray("4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"), // master_key_01
112 "75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04 115 AsArray("79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"), // master_key_02
113 "EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05 116 AsArray("4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"), // master_key_03
114 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06 117 AsArray("75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"), // master_key_04
115 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07 118 AsArray("EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"), // master_key_05
116 119 AsArray("9497E6779F5D840F2BBA1DE4E95BA1D6F21EFC94717D5AE5CA37D7EC5BD37A19"), // master_key_06
117 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08 120 AsArray("4EC96B8CB01B8DCE382149443430B2B6EBCB2983348AFA04A25E53609DABEDF6"), // master_key_07
118 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09 121
119 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A 122 AsArray("2998E2E23609BC2675FF062A2D64AF5B1B78DFF463B24119D64A1B64F01B2D51"), // master_key_08
120 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B 123 AsArray("9D486A98067C44B37CF173D3BF577891EB6081FF6B4A166347D9DBBF7025076B"), // master_key_09
121 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C 124 AsArray("4EC5A237A75A083A9C5F6CF615601522A7F822D06BD4BA32612C9CEBBB29BD45"), // master_key_0A
122 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D 125 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0B
123 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E 126 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0C
124 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F 127 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0D
125 128 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0E
126 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10 129 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0F
127 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11 130
128 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12 131 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_10
129 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13 132 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_11
130 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14 133 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_12
131 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15 134 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_13
132 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16 135 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_14
133 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17 136 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_15
134 137 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_16
135 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18 138 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_17
136 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19 139
137 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A 140 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_18
138 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B 141 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_19
139 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C 142 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1A
140 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D 143 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1B
141 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E 144 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1C
142 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F 145 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1D
146 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1E
147 AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1F
143}; 148};
149// clang-format on
150
151static constexpr u8 CalculateMaxKeyblobSourceHash() {
152 const auto is_zero = [](const auto& data) {
153 // TODO: Replace with std::all_of whenever mingw decides to update their
154 // libraries to include the constexpr variant of it.
155 for (const auto element : data) {
156 if (element != 0) {
157 return false;
158 }
159 }
160 return true;
161 };
144 162
145static u8 CalculateMaxKeyblobSourceHash() {
146 for (s8 i = 0x1F; i >= 0; --i) { 163 for (s8 i = 0x1F; i >= 0; --i) {
147 if (keyblob_source_hashes[i] != SHA256Hash{}) 164 if (!is_zero(keyblob_source_hashes[i])) {
148 return static_cast<u8>(i + 1); 165 return static_cast<u8>(i + 1);
166 }
149 } 167 }
150 168
151 return 0; 169 return 0;
@@ -349,8 +367,8 @@ static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header
349 Package2Header temp = header; 367 Package2Header temp = header;
350 AESCipher<Key128> cipher(key, Mode::CTR); 368 AESCipher<Key128> cipher(key, Mode::CTR);
351 cipher.SetIV(header.header_ctr); 369 cipher.SetIV(header.header_ctr);
352 cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr, 370 cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - sizeof(Package2Header::signature),
353 Op::Decrypt); 371 &temp.header_ctr, Op::Decrypt);
354 if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) { 372 if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
355 header = temp; 373 header = temp;
356 return true; 374 return true;
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 285277ef8..9ffda2e14 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -86,7 +86,7 @@ VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
86 auto& keys = Core::Crypto::KeyManager::Instance(); 86 auto& keys = Core::Crypto::KeyManager::Instance();
87 Core::Crypto::PartitionDataManager pdm{ 87 Core::Crypto::PartitionDataManager pdm{
88 Core::System::GetInstance().GetFilesystem()->OpenDirectory( 88 Core::System::GetInstance().GetFilesystem()->OpenDirectory(
89 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; 89 Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), Mode::Read)};
90 keys.PopulateFromPartitionData(pdm); 90 keys.PopulateFromPartitionData(pdm);
91 91
92 switch (id) { 92 switch (id) {
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index f831487dd..da01002d5 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -257,8 +257,7 @@ std::vector<NcaID> PlaceholderCache::List() const {
257 for (const auto& sdir : dir->GetSubdirectories()) { 257 for (const auto& sdir : dir->GetSubdirectories()) {
258 for (const auto& file : sdir->GetFiles()) { 258 for (const auto& file : sdir->GetFiles()) {
259 const auto name = file->GetName(); 259 const auto name = file->GetName();
260 if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' && 260 if (name.length() == 36 && name.ends_with(".nca")) {
261 name[35] == 'a') {
262 out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32))); 261 out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32)));
263 } 262 }
264 } 263 }
@@ -621,25 +620,25 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
621 620
622InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, 621InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
623 bool overwrite_if_exists, const VfsCopyFunction& copy) { 622 bool overwrite_if_exists, const VfsCopyFunction& copy) {
624 CNMTHeader header{ 623 const CNMTHeader header{
625 nca.GetTitleId(), // Title ID 624 .title_id = nca.GetTitleId(),
626 0, // Ignore/Default title version 625 .title_version = 0,
627 type, // Type 626 .type = type,
628 {}, // Padding 627 .reserved = {},
629 0x10, // Default table offset 628 .table_offset = 0x10,
630 1, // 1 Content Entry 629 .number_content_entries = 1,
631 0, // No Meta Entries 630 .number_meta_entries = 0,
632 {}, // Padding 631 .attributes = 0,
633 {}, // Reserved 1 632 .reserved2 = {},
634 0, // Is committed 633 .is_committed = 0,
635 0, // Required download system version 634 .required_download_system_version = 0,
636 {}, // Reserved 2 635 .reserved3 = {},
637 }; 636 };
638 OptionalHeader opt_header{0, 0}; 637 const OptionalHeader opt_header{0, 0};
639 ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}}; 638 ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}};
640 const auto& data = nca.GetBaseFile()->ReadBytes(0x100000); 639 const auto& data = nca.GetBaseFile()->ReadBytes(0x100000);
641 mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0); 640 mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0);
642 memcpy(&c_rec.nca_id, &c_rec.hash, 16); 641 std::memcpy(&c_rec.nca_id, &c_rec.hash, 16);
643 const CNMT new_cnmt(header, opt_header, {c_rec}, {}); 642 const CNMT new_cnmt(header, opt_header, {c_rec}, {});
644 if (!RawInstallYuzuMeta(new_cnmt)) { 643 if (!RawInstallYuzuMeta(new_cnmt)) {
645 return InstallResult::ErrorMetaFailed; 644 return InstallResult::ErrorMetaFailed;
@@ -728,7 +727,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
728 LOG_WARNING(Loader, "Overwriting existing NCA..."); 727 LOG_WARNING(Loader, "Overwriting existing NCA...");
729 VirtualDir c_dir; 728 VirtualDir c_dir;
730 { c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); } 729 { c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); }
731 c_dir->DeleteFile(FileUtil::GetFilename(path)); 730 c_dir->DeleteFile(Common::FS::GetFilename(path));
732 } 731 }
733 732
734 auto out = dir->CreateFileRelative(path); 733 auto out = dir->CreateFileRelative(path);
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index ec1d54f27..5b414b0f0 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -133,9 +133,9 @@ public:
133 // Parsing function defines the conversion from raw file to NCA. If there are other steps 133 // Parsing function defines the conversion from raw file to NCA. If there are other steps
134 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom 134 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
135 // parsing function. 135 // parsing function.
136 explicit RegisteredCache(VirtualDir dir, 136 explicit RegisteredCache(
137 ContentProviderParsingFunction parsing_function = 137 VirtualDir dir, ContentProviderParsingFunction parsing_function =
138 [](const VirtualFile& file, const NcaID& id) { return file; }); 138 [](const VirtualFile& file, const NcaID& id) { return file; });
139 ~RegisteredCache() override; 139 ~RegisteredCache() override;
140 140
141 void Refresh() override; 141 void Refresh() override;
diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp
index 61bb67945..d65c7d234 100644
--- a/src/core/file_sys/system_archive/mii_model.cpp
+++ b/src/core/file_sys/system_archive/mii_model.cpp
@@ -27,18 +27,12 @@ VirtualDir MiiModel() {
27 auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, 27 auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{},
28 std::vector<VirtualDir>{}, "data"); 28 std::vector<VirtualDir>{}, "data");
29 29
30 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_LINEAR.size()>>( 30 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat"));
31 MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat")); 31 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat"));
32 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_SRGB.size()>>( 32 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat"));
33 MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat")); 33 out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat"));
34 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_LINEAR.size()>>( 34 out->AddFile(MakeArrayFile(MiiModelData::SHAPE_HIGH, "ShapeHigh.dat"));
35 MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat")); 35 out->AddFile(MakeArrayFile(MiiModelData::SHAPE_MID, "ShapeMid.dat"));
36 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_SRGB.size()>>(
37 MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat"));
38 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_HIGH.size()>>(
39 MiiModelData::SHAPE_HIGH, "ShapeHigh.dat"));
40 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>(
41 MiiModelData::SHAPE_MID, "ShapeMid.dat"));
42 36
43 return out; 37 return out;
44} 38}
diff --git a/src/core/file_sys/system_archive/ng_word.cpp b/src/core/file_sys/system_archive/ng_word.cpp
index f4443784d..100d3c5db 100644
--- a/src/core/file_sys/system_archive/ng_word.cpp
+++ b/src/core/file_sys/system_archive/ng_word.cpp
@@ -24,19 +24,18 @@ constexpr std::array<u8, 30> WORD_TXT{
24} // namespace NgWord1Data 24} // namespace NgWord1Data
25 25
26VirtualDir NgWord1() { 26VirtualDir NgWord1() {
27 std::vector<VirtualFile> files(NgWord1Data::NUMBER_WORD_TXT_FILES); 27 std::vector<VirtualFile> files;
28 files.reserve(NgWord1Data::NUMBER_WORD_TXT_FILES);
28 29
29 for (std::size_t i = 0; i < files.size(); ++i) { 30 for (std::size_t i = 0; i < files.size(); ++i) {
30 files[i] = std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( 31 files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, fmt::format("{}.txt", i)));
31 NgWord1Data::WORD_TXT, fmt::format("{}.txt", i));
32 } 32 }
33 33
34 files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( 34 files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, "common.txt"));
35 NgWord1Data::WORD_TXT, "common.txt")); 35 files.push_back(MakeArrayFile(NgWord1Data::VERSION_DAT, "version.dat"));
36 files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::VERSION_DAT.size()>>(
37 NgWord1Data::VERSION_DAT, "version.dat"));
38 36
39 return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); 37 return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{},
38 "data");
40} 39}
41 40
42namespace NgWord2Data { 41namespace NgWord2Data {
@@ -55,27 +54,22 @@ constexpr std::array<u8, 0x2C> AC_NX_DATA{
55} // namespace NgWord2Data 54} // namespace NgWord2Data
56 55
57VirtualDir NgWord2() { 56VirtualDir NgWord2() {
58 std::vector<VirtualFile> files(NgWord2Data::NUMBER_AC_NX_FILES * 3); 57 std::vector<VirtualFile> files;
58 files.reserve(NgWord2Data::NUMBER_AC_NX_FILES * 3);
59 59
60 for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) { 60 for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) {
61 files[3 * i] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 61 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i)));
62 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i)); 62 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i)));
63 files[3 * i + 1] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 63 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i)));
64 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i));
65 files[3 * i + 2] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
66 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i));
67 } 64 }
68 65
69 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 66 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b1_nx"));
70 NgWord2Data::AC_NX_DATA, "ac_common_b1_nx")); 67 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b2_nx"));
71 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( 68 files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx"));
72 NgWord2Data::AC_NX_DATA, "ac_common_b2_nx")); 69 files.push_back(MakeArrayFile(NgWord2Data::VERSION_DAT, "version.dat"));
73 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
74 NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx"));
75 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::VERSION_DAT.size()>>(
76 NgWord2Data::VERSION_DAT, "version.dat"));
77 70
78 return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); 71 return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{},
72 "data");
79} 73}
80 74
81} // namespace FileSys::SystemArchive 75} // namespace FileSys::SystemArchive
diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp
index d1de63f20..8fd005012 100644
--- a/src/core/file_sys/system_archive/time_zone_binary.cpp
+++ b/src/core/file_sys/system_archive/time_zone_binary.cpp
@@ -654,12 +654,13 @@ static VirtualFile GenerateDefaultTimeZoneFile() {
654} 654}
655 655
656VirtualDir TimeZoneBinary() { 656VirtualDir TimeZoneBinary() {
657 const std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>( 657 std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>(
658 std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{}, 658 std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{},
659 "zoneinfo")}; 659 "zoneinfo")};
660 const std::vector<VirtualFile> root_files{ 660 std::vector<VirtualFile> root_files{MakeArrayFile(LOCATION_NAMES, "binaryList.txt")};
661 std::make_shared<ArrayVfsFile<LOCATION_NAMES.size()>>(LOCATION_NAMES, "binaryList.txt")}; 661
662 return std::make_shared<VectorVfsDirectory>(root_files, root_dirs, "data"); 662 return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(root_dirs),
663 "data");
663} 664}
664 665
665} // namespace FileSys::SystemArchive 666} // namespace FileSys::SystemArchive
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index e33327ef0..a4c3f67c4 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -30,7 +30,7 @@ bool VfsFilesystem::IsWritable() const {
30} 30}
31 31
32VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { 32VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
33 const auto path = FileUtil::SanitizePath(path_); 33 const auto path = Common::FS::SanitizePath(path_);
34 if (root->GetFileRelative(path) != nullptr) 34 if (root->GetFileRelative(path) != nullptr)
35 return VfsEntryType::File; 35 return VfsEntryType::File;
36 if (root->GetDirectoryRelative(path) != nullptr) 36 if (root->GetDirectoryRelative(path) != nullptr)
@@ -40,22 +40,22 @@ VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
40} 40}
41 41
42VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { 42VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
43 const auto path = FileUtil::SanitizePath(path_); 43 const auto path = Common::FS::SanitizePath(path_);
44 return root->GetFileRelative(path); 44 return root->GetFileRelative(path);
45} 45}
46 46
47VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { 47VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
48 const auto path = FileUtil::SanitizePath(path_); 48 const auto path = Common::FS::SanitizePath(path_);
49 return root->CreateFileRelative(path); 49 return root->CreateFileRelative(path);
50} 50}
51 51
52VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { 52VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
53 const auto old_path = FileUtil::SanitizePath(old_path_); 53 const auto old_path = Common::FS::SanitizePath(old_path_);
54 const auto new_path = FileUtil::SanitizePath(new_path_); 54 const auto new_path = Common::FS::SanitizePath(new_path_);
55 55
56 // VfsDirectory impls are only required to implement copy across the current directory. 56 // VfsDirectory impls are only required to implement copy across the current directory.
57 if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) { 57 if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) {
58 if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path))) 58 if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path)))
59 return nullptr; 59 return nullptr;
60 return OpenFile(new_path, Mode::ReadWrite); 60 return OpenFile(new_path, Mode::ReadWrite);
61 } 61 }
@@ -76,8 +76,8 @@ VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view
76} 76}
77 77
78VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) { 78VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) {
79 const auto sanitized_old_path = FileUtil::SanitizePath(old_path); 79 const auto sanitized_old_path = Common::FS::SanitizePath(old_path);
80 const auto sanitized_new_path = FileUtil::SanitizePath(new_path); 80 const auto sanitized_new_path = Common::FS::SanitizePath(new_path);
81 81
82 // Again, non-default impls are highly encouraged to provide a more optimized version of this. 82 // Again, non-default impls are highly encouraged to provide a more optimized version of this.
83 auto out = CopyFile(sanitized_old_path, sanitized_new_path); 83 auto out = CopyFile(sanitized_old_path, sanitized_new_path);
@@ -89,26 +89,26 @@ VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view
89} 89}
90 90
91bool VfsFilesystem::DeleteFile(std::string_view path_) { 91bool VfsFilesystem::DeleteFile(std::string_view path_) {
92 const auto path = FileUtil::SanitizePath(path_); 92 const auto path = Common::FS::SanitizePath(path_);
93 auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); 93 auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write);
94 if (parent == nullptr) 94 if (parent == nullptr)
95 return false; 95 return false;
96 return parent->DeleteFile(FileUtil::GetFilename(path)); 96 return parent->DeleteFile(Common::FS::GetFilename(path));
97} 97}
98 98
99VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { 99VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
100 const auto path = FileUtil::SanitizePath(path_); 100 const auto path = Common::FS::SanitizePath(path_);
101 return root->GetDirectoryRelative(path); 101 return root->GetDirectoryRelative(path);
102} 102}
103 103
104VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { 104VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
105 const auto path = FileUtil::SanitizePath(path_); 105 const auto path = Common::FS::SanitizePath(path_);
106 return root->CreateDirectoryRelative(path); 106 return root->CreateDirectoryRelative(path);
107} 107}
108 108
109VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { 109VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) {
110 const auto old_path = FileUtil::SanitizePath(old_path_); 110 const auto old_path = Common::FS::SanitizePath(old_path_);
111 const auto new_path = FileUtil::SanitizePath(new_path_); 111 const auto new_path = Common::FS::SanitizePath(new_path_);
112 112
113 // Non-default impls are highly encouraged to provide a more optimized version of this. 113 // Non-default impls are highly encouraged to provide a more optimized version of this.
114 auto old_dir = OpenDirectory(old_path, Mode::Read); 114 auto old_dir = OpenDirectory(old_path, Mode::Read);
@@ -139,8 +139,8 @@ VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_
139} 139}
140 140
141VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) { 141VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) {
142 const auto sanitized_old_path = FileUtil::SanitizePath(old_path); 142 const auto sanitized_old_path = Common::FS::SanitizePath(old_path);
143 const auto sanitized_new_path = FileUtil::SanitizePath(new_path); 143 const auto sanitized_new_path = Common::FS::SanitizePath(new_path);
144 144
145 // Non-default impls are highly encouraged to provide a more optimized version of this. 145 // Non-default impls are highly encouraged to provide a more optimized version of this.
146 auto out = CopyDirectory(sanitized_old_path, sanitized_new_path); 146 auto out = CopyDirectory(sanitized_old_path, sanitized_new_path);
@@ -152,17 +152,17 @@ VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_v
152} 152}
153 153
154bool VfsFilesystem::DeleteDirectory(std::string_view path_) { 154bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
155 const auto path = FileUtil::SanitizePath(path_); 155 const auto path = Common::FS::SanitizePath(path_);
156 auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); 156 auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write);
157 if (parent == nullptr) 157 if (parent == nullptr)
158 return false; 158 return false;
159 return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path)); 159 return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path));
160} 160}
161 161
162VfsFile::~VfsFile() = default; 162VfsFile::~VfsFile() = default;
163 163
164std::string VfsFile::GetExtension() const { 164std::string VfsFile::GetExtension() const {
165 return std::string(FileUtil::GetExtensionFromFilename(GetName())); 165 return std::string(Common::FS::GetExtensionFromFilename(GetName()));
166} 166}
167 167
168VfsDirectory::~VfsDirectory() = default; 168VfsDirectory::~VfsDirectory() = default;
@@ -203,7 +203,7 @@ std::string VfsFile::GetFullPath() const {
203} 203}
204 204
205std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const { 205std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const {
206 auto vec = FileUtil::SplitPathComponents(path); 206 auto vec = Common::FS::SplitPathComponents(path);
207 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 207 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
208 vec.end()); 208 vec.end());
209 if (vec.empty()) { 209 if (vec.empty()) {
@@ -239,7 +239,7 @@ std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(std::string_view path) co
239} 239}
240 240
241std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const { 241std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const {
242 auto vec = FileUtil::SplitPathComponents(path); 242 auto vec = Common::FS::SplitPathComponents(path);
243 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 243 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
244 vec.end()); 244 vec.end());
245 if (vec.empty()) { 245 if (vec.empty()) {
@@ -301,7 +301,7 @@ std::size_t VfsDirectory::GetSize() const {
301} 301}
302 302
303std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) { 303std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) {
304 auto vec = FileUtil::SplitPathComponents(path); 304 auto vec = Common::FS::SplitPathComponents(path);
305 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 305 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
306 vec.end()); 306 vec.end());
307 if (vec.empty()) { 307 if (vec.empty()) {
@@ -320,7 +320,7 @@ std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path)
320 } 320 }
321 } 321 }
322 322
323 return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path)); 323 return dir->CreateFileRelative(Common::FS::GetPathWithoutTop(path));
324} 324}
325 325
326std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) { 326std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) {
@@ -332,7 +332,7 @@ std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path)
332} 332}
333 333
334std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) { 334std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) {
335 auto vec = FileUtil::SplitPathComponents(path); 335 auto vec = Common::FS::SplitPathComponents(path);
336 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 336 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
337 vec.end()); 337 vec.end());
338 if (vec.empty()) { 338 if (vec.empty()) {
@@ -351,7 +351,7 @@ std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_
351 } 351 }
352 } 352 }
353 353
354 return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path)); 354 return dir->CreateDirectoryRelative(Common::FS::GetPathWithoutTop(path));
355} 355}
356 356
357std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(std::string_view path) { 357std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(std::string_view path) {
diff --git a/src/core/file_sys/vfs_libzip.cpp b/src/core/file_sys/vfs_libzip.cpp
index d69952940..429d7bc8b 100644
--- a/src/core/file_sys/vfs_libzip.cpp
+++ b/src/core/file_sys/vfs_libzip.cpp
@@ -49,7 +49,7 @@ VirtualDir ExtractZIP(VirtualFile file) {
49 if (zip_fread(file2.get(), buf.data(), buf.size()) != s64(buf.size())) 49 if (zip_fread(file2.get(), buf.data(), buf.size()) != s64(buf.size()))
50 return nullptr; 50 return nullptr;
51 51
52 const auto parts = FileUtil::SplitPathComponents(stat.name); 52 const auto parts = Common::FS::SplitPathComponents(stat.name);
53 const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back()); 53 const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back());
54 54
55 std::shared_ptr<VectorVfsDirectory> dtrv = out; 55 std::shared_ptr<VectorVfsDirectory> dtrv = out;
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 0db0091f6..488687ba9 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -14,6 +14,8 @@
14 14
15namespace FileSys { 15namespace FileSys {
16 16
17namespace FS = Common::FS;
18
17static std::string ModeFlagsToString(Mode mode) { 19static std::string ModeFlagsToString(Mode mode) {
18 std::string mode_str; 20 std::string mode_str;
19 21
@@ -57,79 +59,82 @@ bool RealVfsFilesystem::IsWritable() const {
57} 59}
58 60
59VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { 61VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
60 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 62 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
61 if (!FileUtil::Exists(path)) 63 if (!FS::Exists(path)) {
62 return VfsEntryType::None; 64 return VfsEntryType::None;
63 if (FileUtil::IsDirectory(path)) 65 }
66 if (FS::IsDirectory(path)) {
64 return VfsEntryType::Directory; 67 return VfsEntryType::Directory;
68 }
65 69
66 return VfsEntryType::File; 70 return VfsEntryType::File;
67} 71}
68 72
69VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { 73VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
70 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 74 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
71 if (cache.find(path) != cache.end()) { 75
72 auto weak = cache[path]; 76 if (const auto weak_iter = cache.find(path); weak_iter != cache.cend()) {
77 const auto& weak = weak_iter->second;
78
73 if (!weak.expired()) { 79 if (!weak.expired()) {
74 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms)); 80 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms));
75 } 81 }
76 } 82 }
77 83
78 if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) { 84 if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) {
79 FileUtil::CreateEmptyFile(path); 85 FS::CreateEmptyFile(path);
80 } 86 }
81 87
82 auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str()); 88 auto backing = std::make_shared<FS::IOFile>(path, ModeFlagsToString(perms).c_str());
83 cache[path] = backing; 89 cache.insert_or_assign(path, backing);
84 90
85 // Cannot use make_shared as RealVfsFile constructor is private 91 // Cannot use make_shared as RealVfsFile constructor is private
86 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); 92 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms));
87} 93}
88 94
89VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { 95VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
90 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 96 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
91 const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash); 97 const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash);
92 if (!FileUtil::Exists(path)) { 98 if (!FS::Exists(path)) {
93 FileUtil::CreateFullPath(path_fwd); 99 FS::CreateFullPath(path_fwd);
94 if (!FileUtil::CreateEmptyFile(path)) 100 if (!FS::CreateEmptyFile(path)) {
95 return nullptr; 101 return nullptr;
102 }
96 } 103 }
97 return OpenFile(path, perms); 104 return OpenFile(path, perms);
98} 105}
99 106
100VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { 107VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
101 const auto old_path = 108 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
102 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 109 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
103 const auto new_path =
104 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
105 110
106 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || 111 if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
107 FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path)) 112 !FS::Copy(old_path, new_path)) {
108 return nullptr; 113 return nullptr;
114 }
109 return OpenFile(new_path, Mode::ReadWrite); 115 return OpenFile(new_path, Mode::ReadWrite);
110} 116}
111 117
112VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { 118VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
113 const auto old_path = 119 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
114 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 120 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
115 const auto new_path = 121 const auto cached_file_iter = cache.find(old_path);
116 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
117 122
118 if (cache.find(old_path) != cache.end()) { 123 if (cached_file_iter != cache.cend()) {
119 auto file = cache[old_path].lock(); 124 auto file = cached_file_iter->second.lock();
120 125
121 if (!cache[old_path].expired()) { 126 if (!cached_file_iter->second.expired()) {
122 file->Close(); 127 file->Close();
123 } 128 }
124 129
125 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || 130 if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
126 FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) { 131 !FS::Rename(old_path, new_path)) {
127 return nullptr; 132 return nullptr;
128 } 133 }
129 134
130 cache.erase(old_path); 135 cache.erase(old_path);
131 file->Open(new_path, "r+b"); 136 file->Open(new_path, "r+b");
132 cache[new_path] = file; 137 cache.insert_or_assign(new_path, std::move(file));
133 } else { 138 } else {
134 UNREACHABLE(); 139 UNREACHABLE();
135 return nullptr; 140 return nullptr;
@@ -139,28 +144,33 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
139} 144}
140 145
141bool RealVfsFilesystem::DeleteFile(std::string_view path_) { 146bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
142 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 147 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
143 if (cache.find(path) != cache.end()) { 148 const auto cached_iter = cache.find(path);
144 if (!cache[path].expired()) 149
145 cache[path].lock()->Close(); 150 if (cached_iter != cache.cend()) {
151 if (!cached_iter->second.expired()) {
152 cached_iter->second.lock()->Close();
153 }
146 cache.erase(path); 154 cache.erase(path);
147 } 155 }
148 return FileUtil::Delete(path); 156
157 return FS::Delete(path);
149} 158}
150 159
151VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { 160VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
152 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 161 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
153 // Cannot use make_shared as RealVfsDirectory constructor is private 162 // Cannot use make_shared as RealVfsDirectory constructor is private
154 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); 163 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
155} 164}
156 165
157VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { 166VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
158 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 167 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
159 const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash); 168 const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash);
160 if (!FileUtil::Exists(path)) { 169 if (!FS::Exists(path)) {
161 FileUtil::CreateFullPath(path_fwd); 170 FS::CreateFullPath(path_fwd);
162 if (!FileUtil::CreateDir(path)) 171 if (!FS::CreateDir(path)) {
163 return nullptr; 172 return nullptr;
173 }
164 } 174 }
165 // Cannot use make_shared as RealVfsDirectory constructor is private 175 // Cannot use make_shared as RealVfsDirectory constructor is private
166 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); 176 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
@@ -168,67 +178,75 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms
168 178
169VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, 179VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
170 std::string_view new_path_) { 180 std::string_view new_path_) {
171 const auto old_path = 181 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
172 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 182 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
173 const auto new_path = 183 if (!FS::Exists(old_path) || FS::Exists(new_path) || !FS::IsDirectory(old_path)) {
174 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
175 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
176 !FileUtil::IsDirectory(old_path))
177 return nullptr; 184 return nullptr;
178 FileUtil::CopyDir(old_path, new_path); 185 }
186 FS::CopyDir(old_path, new_path);
179 return OpenDirectory(new_path, Mode::ReadWrite); 187 return OpenDirectory(new_path, Mode::ReadWrite);
180} 188}
181 189
182VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, 190VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
183 std::string_view new_path_) { 191 std::string_view new_path_) {
184 const auto old_path = 192 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
185 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); 193 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
186 const auto new_path = 194
187 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); 195 if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
188 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || 196 !FS::Rename(old_path, new_path)) {
189 FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
190 return nullptr; 197 return nullptr;
198 }
191 199
192 for (auto& kv : cache) { 200 for (auto& kv : cache) {
193 // Path in cache starts with old_path 201 // If the path in the cache doesn't start with old_path, then bail on this file.
194 if (kv.first.rfind(old_path, 0) == 0) { 202 if (kv.first.rfind(old_path, 0) != 0) {
195 const auto file_old_path = 203 continue;
196 FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault); 204 }
197 const auto file_new_path = 205
198 FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), 206 const auto file_old_path =
199 FileUtil::DirectorySeparator::PlatformDefault); 207 FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault);
200 auto cached = cache[file_old_path]; 208 auto file_new_path = FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
201 if (!cached.expired()) { 209 FS::DirectorySeparator::PlatformDefault);
202 auto file = cached.lock(); 210 const auto& cached = cache[file_old_path];
203 file->Open(file_new_path, "r+b"); 211
204 cache.erase(file_old_path); 212 if (cached.expired()) {
205 cache[file_new_path] = file; 213 continue;
206 }
207 } 214 }
215
216 auto file = cached.lock();
217 file->Open(file_new_path, "r+b");
218 cache.erase(file_old_path);
219 cache.insert_or_assign(std::move(file_new_path), std::move(file));
208 } 220 }
209 221
210 return OpenDirectory(new_path, Mode::ReadWrite); 222 return OpenDirectory(new_path, Mode::ReadWrite);
211} 223}
212 224
213bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { 225bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
214 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); 226 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
227
215 for (auto& kv : cache) { 228 for (auto& kv : cache) {
216 // Path in cache starts with old_path 229 // If the path in the cache doesn't start with path, then bail on this file.
217 if (kv.first.rfind(path, 0) == 0) { 230 if (kv.first.rfind(path, 0) != 0) {
218 if (!cache[kv.first].expired()) 231 continue;
219 cache[kv.first].lock()->Close(); 232 }
220 cache.erase(kv.first); 233
234 const auto& entry = cache[kv.first];
235 if (!entry.expired()) {
236 entry.lock()->Close();
221 } 237 }
238
239 cache.erase(kv.first);
222 } 240 }
223 return FileUtil::DeleteDirRecursively(path); 241
242 return FS::DeleteDirRecursively(path);
224} 243}
225 244
226RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_, 245RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FS::IOFile> backing_,
227 const std::string& path_, Mode perms_) 246 const std::string& path_, Mode perms_)
228 : base(base_), backing(std::move(backing_)), path(path_), 247 : base(base_), backing(std::move(backing_)), path(path_), parent_path(FS::GetParentPath(path_)),
229 parent_path(FileUtil::GetParentPath(path_)), 248 path_components(FS::SplitPathComponents(path_)),
230 path_components(FileUtil::SplitPathComponents(path_)), 249 parent_components(FS::SliceVector(path_components, 0, path_components.size() - 1)),
231 parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
232 perms(perms_) {} 250 perms(perms_) {}
233 251
234RealVfsFile::~RealVfsFile() = default; 252RealVfsFile::~RealVfsFile() = default;
@@ -258,14 +276,16 @@ bool RealVfsFile::IsReadable() const {
258} 276}
259 277
260std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { 278std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
261 if (!backing->Seek(offset, SEEK_SET)) 279 if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) {
262 return 0; 280 return 0;
281 }
263 return backing->ReadBytes(data, length); 282 return backing->ReadBytes(data, length);
264} 283}
265 284
266std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { 285std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
267 if (!backing->Seek(offset, SEEK_SET)) 286 if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) {
268 return 0; 287 return 0;
288 }
269 return backing->WriteBytes(data, length); 289 return backing->WriteBytes(data, length);
270} 290}
271 291
@@ -282,16 +302,18 @@ bool RealVfsFile::Close() {
282 302
283template <> 303template <>
284std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const { 304std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const {
285 if (perms == Mode::Append) 305 if (perms == Mode::Append) {
286 return {}; 306 return {};
307 }
287 308
288 std::vector<VirtualFile> out; 309 std::vector<VirtualFile> out;
289 FileUtil::ForeachDirectoryEntry( 310 FS::ForeachDirectoryEntry(
290 nullptr, path, 311 nullptr, path,
291 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { 312 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
292 const std::string full_path = directory + DIR_SEP + filename; 313 const std::string full_path = directory + DIR_SEP + filename;
293 if (!FileUtil::IsDirectory(full_path)) 314 if (!FS::IsDirectory(full_path)) {
294 out.emplace_back(base.OpenFile(full_path, perms)); 315 out.emplace_back(base.OpenFile(full_path, perms));
316 }
295 return true; 317 return true;
296 }); 318 });
297 319
@@ -300,16 +322,18 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>(
300 322
301template <> 323template <>
302std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const { 324std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const {
303 if (perms == Mode::Append) 325 if (perms == Mode::Append) {
304 return {}; 326 return {};
327 }
305 328
306 std::vector<VirtualDir> out; 329 std::vector<VirtualDir> out;
307 FileUtil::ForeachDirectoryEntry( 330 FS::ForeachDirectoryEntry(
308 nullptr, path, 331 nullptr, path,
309 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { 332 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
310 const std::string full_path = directory + DIR_SEP + filename; 333 const std::string full_path = directory + DIR_SEP + filename;
311 if (FileUtil::IsDirectory(full_path)) 334 if (FS::IsDirectory(full_path)) {
312 out.emplace_back(base.OpenDirectory(full_path, perms)); 335 out.emplace_back(base.OpenDirectory(full_path, perms));
336 }
313 return true; 337 return true;
314 }); 338 });
315 339
@@ -317,29 +341,30 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi
317} 341}
318 342
319RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) 343RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
320 : base(base_), path(FileUtil::RemoveTrailingSlash(path_)), 344 : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)),
321 parent_path(FileUtil::GetParentPath(path)), 345 path_components(FS::SplitPathComponents(path)),
322 path_components(FileUtil::SplitPathComponents(path)), 346 parent_components(FS::SliceVector(path_components, 0, path_components.size() - 1)),
323 parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
324 perms(perms_) { 347 perms(perms_) {
325 if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) { 348 if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) {
326 FileUtil::CreateDir(path); 349 FS::CreateDir(path);
327 } 350 }
328} 351}
329 352
330RealVfsDirectory::~RealVfsDirectory() = default; 353RealVfsDirectory::~RealVfsDirectory() = default;
331 354
332std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const { 355std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
333 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 356 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
334 if (!FileUtil::Exists(full_path) || FileUtil::IsDirectory(full_path)) 357 if (!FS::Exists(full_path) || FS::IsDirectory(full_path)) {
335 return nullptr; 358 return nullptr;
359 }
336 return base.OpenFile(full_path, perms); 360 return base.OpenFile(full_path, perms);
337} 361}
338 362
339std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const { 363std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const {
340 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 364 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
341 if (!FileUtil::Exists(full_path) || !FileUtil::IsDirectory(full_path)) 365 if (!FS::Exists(full_path) || !FS::IsDirectory(full_path)) {
342 return nullptr; 366 return nullptr;
367 }
343 return base.OpenDirectory(full_path, perms); 368 return base.OpenDirectory(full_path, perms);
344} 369}
345 370
@@ -352,17 +377,17 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view
352} 377}
353 378
354std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) { 379std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) {
355 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 380 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
356 return base.CreateFile(full_path, perms); 381 return base.CreateFile(full_path, perms);
357} 382}
358 383
359std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) { 384std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) {
360 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 385 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path));
361 return base.CreateDirectory(full_path, perms); 386 return base.CreateDirectory(full_path, perms);
362} 387}
363 388
364bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { 389bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
365 auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name)); 390 const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(name));
366 return base.DeleteDirectory(full_path); 391 return base.DeleteDirectory(full_path);
367} 392}
368 393
@@ -387,8 +412,9 @@ std::string RealVfsDirectory::GetName() const {
387} 412}
388 413
389std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const { 414std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
390 if (path_components.size() <= 1) 415 if (path_components.size() <= 1) {
391 return nullptr; 416 return nullptr;
417 }
392 418
393 return base.OpenDirectory(parent_path, perms); 419 return base.OpenDirectory(parent_path, perms);
394} 420}
@@ -425,16 +451,17 @@ std::string RealVfsDirectory::GetFullPath() const {
425} 451}
426 452
427std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const { 453std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const {
428 if (perms == Mode::Append) 454 if (perms == Mode::Append) {
429 return {}; 455 return {};
456 }
430 457
431 std::map<std::string, VfsEntryType, std::less<>> out; 458 std::map<std::string, VfsEntryType, std::less<>> out;
432 FileUtil::ForeachDirectoryEntry( 459 FS::ForeachDirectoryEntry(
433 nullptr, path, 460 nullptr, path,
434 [&out](u64* entries_out, const std::string& directory, const std::string& filename) { 461 [&out](u64* entries_out, const std::string& directory, const std::string& filename) {
435 const std::string full_path = directory + DIR_SEP + filename; 462 const std::string full_path = directory + DIR_SEP + filename;
436 out.emplace(filename, FileUtil::IsDirectory(full_path) ? VfsEntryType::Directory 463 out.emplace(filename,
437 : VfsEntryType::File); 464 FS::IsDirectory(full_path) ? VfsEntryType::Directory : VfsEntryType::File);
438 return true; 465 return true;
439 }); 466 });
440 467
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index a0a857a31..0b537b22c 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -9,7 +9,7 @@
9#include "core/file_sys/mode.h" 9#include "core/file_sys/mode.h"
10#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs.h"
11 11
12namespace FileUtil { 12namespace Common::FS {
13class IOFile; 13class IOFile;
14} 14}
15 15
@@ -36,7 +36,7 @@ public:
36 bool DeleteDirectory(std::string_view path) override; 36 bool DeleteDirectory(std::string_view path) override;
37 37
38private: 38private:
39 boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache; 39 boost::container::flat_map<std::string, std::weak_ptr<Common::FS::IOFile>> cache;
40}; 40};
41 41
42// An implmentation of VfsFile that represents a file on the user's computer. 42// An implmentation of VfsFile that represents a file on the user's computer.
@@ -58,13 +58,13 @@ public:
58 bool Rename(std::string_view name) override; 58 bool Rename(std::string_view name) override;
59 59
60private: 60private:
61 RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing, 61 RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<Common::FS::IOFile> backing,
62 const std::string& path, Mode perms = Mode::Read); 62 const std::string& path, Mode perms = Mode::Read);
63 63
64 bool Close(); 64 bool Close();
65 65
66 RealVfsFilesystem& base; 66 RealVfsFilesystem& base;
67 std::shared_ptr<FileUtil::IOFile> backing; 67 std::shared_ptr<Common::FS::IOFile> backing;
68 std::string path; 68 std::string path;
69 std::string parent_path; 69 std::string parent_path;
70 std::vector<std::string> path_components; 70 std::vector<std::string> path_components;
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
index ac36cb2ee..95d3da2f2 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs_vector.h
@@ -4,7 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <cstring> 8#include <cstring>
9#include <memory>
10#include <string>
11#include <vector>
8#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
9 13
10namespace FileSys { 14namespace FileSys {
@@ -13,7 +17,8 @@ namespace FileSys {
13template <std::size_t size> 17template <std::size_t size>
14class ArrayVfsFile : public VfsFile { 18class ArrayVfsFile : public VfsFile {
15public: 19public:
16 ArrayVfsFile(std::array<u8, size> data, std::string name = "", VirtualDir parent = nullptr) 20 explicit ArrayVfsFile(const std::array<u8, size>& data, std::string name = "",
21 VirtualDir parent = nullptr)
17 : data(data), name(std::move(name)), parent(std::move(parent)) {} 22 : data(data), name(std::move(name)), parent(std::move(parent)) {}
18 23
19 std::string GetName() const override { 24 std::string GetName() const override {
@@ -61,6 +66,12 @@ private:
61 VirtualDir parent; 66 VirtualDir parent;
62}; 67};
63 68
69template <std::size_t Size, typename... Args>
70std::shared_ptr<ArrayVfsFile<Size>> MakeArrayFile(const std::array<u8, Size>& data,
71 Args&&... args) {
72 return std::make_shared<ArrayVfsFile<Size>>(data, std::forward<Args>(args)...);
73}
74
64// An implementation of VfsFile that is backed by a vector optionally supplied upon construction 75// An implementation of VfsFile that is backed by a vector optionally supplied upon construction
65class VectorVfsFile : public VfsFile { 76class VectorVfsFile : public VfsFile {
66public: 77public:
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index 81413c684..ccf5966d0 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -44,7 +44,7 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t
44} 44}
45 45
46NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) { 46NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
47 std::string path = FileUtil::SanitizePath(file->GetFullPath()); 47 std::string path = Common::FS::SanitizePath(file->GetFullPath());
48 static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca", 48 static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca",
49 std::regex_constants::ECMAScript | 49 std::regex_constants::ECMAScript |
50 std::regex_constants::icase); 50 std::regex_constants::icase);
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 13aa14934..3e8780243 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -39,7 +39,7 @@ public:
39 39
40 class Scoped { 40 class Scoped {
41 public: 41 public:
42 explicit Scoped(GraphicsContext& context_) : context(context_) { 42 [[nodiscard]] explicit Scoped(GraphicsContext& context_) : context(context_) {
43 context.MakeCurrent(); 43 context.MakeCurrent();
44 } 44 }
45 ~Scoped() { 45 ~Scoped() {
@@ -52,7 +52,7 @@ public:
52 52
53 /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value 53 /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
54 /// ends 54 /// ends
55 Scoped Acquire() { 55 [[nodiscard]] Scoped Acquire() {
56 return Scoped{*this}; 56 return Scoped{*this};
57 } 57 }
58}; 58};
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index df0debe1b..b882eaa0f 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -81,7 +81,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32
81 do { 81 do {
82 current_value = monitor.ExclusiveRead32(current_core, address); 82 current_value = monitor.ExclusiveRead32(current_core, address);
83 83
84 if (current_value != value) { 84 if (current_value != static_cast<u32>(value)) {
85 return ERR_INVALID_STATE; 85 return ERR_INVALID_STATE;
86 } 86 }
87 current_value++; 87 current_value++;
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 9277b5d08..81f85643b 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -293,13 +293,15 @@ std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
293 BufferDescriptorA()[buffer_index].Size()}; 293 BufferDescriptorA()[buffer_index].Size()};
294 294
295 if (is_buffer_a) { 295 if (is_buffer_a) {
296 ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return buffer; }, 296 ASSERT_OR_EXECUTE_MSG(
297 "BufferDescriptorA invalid buffer_index {}", buffer_index); 297 BufferDescriptorA().size() > buffer_index, { return buffer; },
298 "BufferDescriptorA invalid buffer_index {}", buffer_index);
298 buffer.resize(BufferDescriptorA()[buffer_index].Size()); 299 buffer.resize(BufferDescriptorA()[buffer_index].Size());
299 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); 300 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
300 } else { 301 } else {
301 ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return buffer; }, 302 ASSERT_OR_EXECUTE_MSG(
302 "BufferDescriptorX invalid buffer_index {}", buffer_index); 303 BufferDescriptorX().size() > buffer_index, { return buffer; },
304 "BufferDescriptorX invalid buffer_index {}", buffer_index);
303 buffer.resize(BufferDescriptorX()[buffer_index].Size()); 305 buffer.resize(BufferDescriptorX()[buffer_index].Size());
304 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); 306 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
305 } 307 }
@@ -324,16 +326,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
324 } 326 }
325 327
326 if (is_buffer_b) { 328 if (is_buffer_b) {
327 ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index && 329 ASSERT_OR_EXECUTE_MSG(
328 BufferDescriptorB()[buffer_index].Size() >= size, 330 BufferDescriptorB().size() > buffer_index &&
329 { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", 331 BufferDescriptorB()[buffer_index].Size() >= size,
330 buffer_index, size); 332 { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size);
331 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); 333 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
332 } else { 334 } else {
333 ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index && 335 ASSERT_OR_EXECUTE_MSG(
334 BufferDescriptorC()[buffer_index].Size() >= size, 336 BufferDescriptorC().size() > buffer_index &&
335 { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", 337 BufferDescriptorC()[buffer_index].Size() >= size,
336 buffer_index, size); 338 { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size);
337 memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); 339 memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
338 } 340 }
339 341
@@ -344,12 +346,14 @@ std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const
344 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && 346 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
345 BufferDescriptorA()[buffer_index].Size()}; 347 BufferDescriptorA()[buffer_index].Size()};
346 if (is_buffer_a) { 348 if (is_buffer_a) {
347 ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return 0; }, 349 ASSERT_OR_EXECUTE_MSG(
348 "BufferDescriptorA invalid buffer_index {}", buffer_index); 350 BufferDescriptorA().size() > buffer_index, { return 0; },
351 "BufferDescriptorA invalid buffer_index {}", buffer_index);
349 return BufferDescriptorA()[buffer_index].Size(); 352 return BufferDescriptorA()[buffer_index].Size();
350 } else { 353 } else {
351 ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return 0; }, 354 ASSERT_OR_EXECUTE_MSG(
352 "BufferDescriptorX invalid buffer_index {}", buffer_index); 355 BufferDescriptorX().size() > buffer_index, { return 0; },
356 "BufferDescriptorX invalid buffer_index {}", buffer_index);
353 return BufferDescriptorX()[buffer_index].Size(); 357 return BufferDescriptorX()[buffer_index].Size();
354 } 358 }
355} 359}
@@ -358,12 +362,14 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons
358 const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && 362 const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
359 BufferDescriptorB()[buffer_index].Size()}; 363 BufferDescriptorB()[buffer_index].Size()};
360 if (is_buffer_b) { 364 if (is_buffer_b) {
361 ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index, { return 0; }, 365 ASSERT_OR_EXECUTE_MSG(
362 "BufferDescriptorB invalid buffer_index {}", buffer_index); 366 BufferDescriptorB().size() > buffer_index, { return 0; },
367 "BufferDescriptorB invalid buffer_index {}", buffer_index);
363 return BufferDescriptorB()[buffer_index].Size(); 368 return BufferDescriptorB()[buffer_index].Size();
364 } else { 369 } else {
365 ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index, { return 0; }, 370 ASSERT_OR_EXECUTE_MSG(
366 "BufferDescriptorC invalid buffer_index {}", buffer_index); 371 BufferDescriptorC().size() > buffer_index, { return 0; },
372 "BufferDescriptorC invalid buffer_index {}", buffer_index);
367 return BufferDescriptorC()[buffer_index].Size(); 373 return BufferDescriptorC()[buffer_index].Size();
368 } 374 }
369 return 0; 375 return 0;
diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp
index 5d6aac00f..a3fadb533 100644
--- a/src/core/hle/kernel/memory/page_table.cpp
+++ b/src/core/hle/kernel/memory/page_table.cpp
@@ -604,7 +604,6 @@ ResultCode PageTable::MapPages(VAddr addr, const PageLinkedList& page_linked_lis
604 if (const auto result{ 604 if (const auto result{
605 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; 605 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())};
606 result.IsError()) { 606 result.IsError()) {
607 const MemoryInfo info{block_manager->FindBlock(cur_addr).GetMemoryInfo()};
608 const std::size_t num_pages{(addr - cur_addr) / PageSize}; 607 const std::size_t num_pages{(addr - cur_addr) / PageSize};
609 608
610 ASSERT( 609 ASSERT(
@@ -852,11 +851,12 @@ ResultCode PageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
852 return result; 851 return result;
853 } 852 }
854 853
855 block_manager->UpdateLock(addr, size / PageSize, 854 block_manager->UpdateLock(
856 [](MemoryBlockManager::iterator block, MemoryPermission perm) { 855 addr, size / PageSize,
857 block->ShareToDevice(perm); 856 [](MemoryBlockManager::iterator block, MemoryPermission perm) {
858 }, 857 block->ShareToDevice(perm);
859 perm); 858 },
859 perm);
860 860
861 return RESULT_SUCCESS; 861 return RESULT_SUCCESS;
862} 862}
@@ -874,11 +874,12 @@ ResultCode PageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
874 return result; 874 return result;
875 } 875 }
876 876
877 block_manager->UpdateLock(addr, size / PageSize, 877 block_manager->UpdateLock(
878 [](MemoryBlockManager::iterator block, MemoryPermission perm) { 878 addr, size / PageSize,
879 block->UnshareToDevice(perm); 879 [](MemoryBlockManager::iterator block, MemoryPermission perm) {
880 }, 880 block->UnshareToDevice(perm);
881 perm); 881 },
882 perm);
882 883
883 return RESULT_SUCCESS; 884 return RESULT_SUCCESS;
884} 885}
diff --git a/src/core/hle/kernel/memory/system_control.cpp b/src/core/hle/kernel/memory/system_control.cpp
index 2f98e9c4c..11d204bc2 100644
--- a/src/core/hle/kernel/memory/system_control.cpp
+++ b/src/core/hle/kernel/memory/system_control.cpp
@@ -7,22 +7,15 @@
7#include "core/hle/kernel/memory/system_control.h" 7#include "core/hle/kernel/memory/system_control.h"
8 8
9namespace Kernel::Memory::SystemControl { 9namespace Kernel::Memory::SystemControl {
10 10namespace {
11u64 GenerateRandomU64ForInit() {
12 static std::random_device device;
13 static std::mt19937 gen(device());
14 static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
15 return distribution(gen);
16}
17
18template <typename F> 11template <typename F>
19u64 GenerateUniformRange(u64 min, u64 max, F f) { 12u64 GenerateUniformRange(u64 min, u64 max, F f) {
20 /* Handle the case where the difference is too large to represent. */ 13 // Handle the case where the difference is too large to represent.
21 if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) { 14 if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
22 return f(); 15 return f();
23 } 16 }
24 17
25 /* Iterate until we get a value in range. */ 18 // Iterate until we get a value in range.
26 const u64 range_size = ((max + 1) - min); 19 const u64 range_size = ((max + 1) - min);
27 const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size; 20 const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
28 while (true) { 21 while (true) {
@@ -32,6 +25,14 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) {
32 } 25 }
33} 26}
34 27
28u64 GenerateRandomU64ForInit() {
29 static std::random_device device;
30 static std::mt19937 gen(device());
31 static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
32 return distribution(gen);
33}
34} // Anonymous namespace
35
35u64 GenerateRandomRange(u64 min, u64 max) { 36u64 GenerateRandomRange(u64 min, u64 max) {
36 return GenerateUniformRange(min, max, GenerateRandomU64ForInit); 37 return GenerateUniformRange(min, max, GenerateRandomU64ForInit);
37} 38}
diff --git a/src/core/hle/kernel/memory/system_control.h b/src/core/hle/kernel/memory/system_control.h
index 3fa93111d..19cab8cbc 100644
--- a/src/core/hle/kernel/memory/system_control.h
+++ b/src/core/hle/kernel/memory/system_control.h
@@ -8,11 +8,6 @@
8 8
9namespace Kernel::Memory::SystemControl { 9namespace Kernel::Memory::SystemControl {
10 10
11u64 GenerateRandomU64ForInit();
12
13template <typename F>
14u64 GenerateUniformRange(u64 min, u64 max, F f);
15
16u64 GenerateRandomRange(u64 min, u64 max); 11u64 GenerateRandomRange(u64 min, u64 max);
17 12
18} // namespace Kernel::Memory::SystemControl 13} // namespace Kernel::Memory::SystemControl
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index f93e5e4b0..a4b234424 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -131,7 +131,8 @@ u32 GlobalScheduler::SelectThreads() {
131 u32 cores_needing_context_switch{}; 131 u32 cores_needing_context_switch{};
132 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 132 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
133 Scheduler& sched = kernel.Scheduler(core); 133 Scheduler& sched = kernel.Scheduler(core);
134 ASSERT(top_threads[core] == nullptr || top_threads[core]->GetProcessorID() == core); 134 ASSERT(top_threads[core] == nullptr ||
135 static_cast<u32>(top_threads[core]->GetProcessorID()) == core);
135 if (update_thread(top_threads[core], sched)) { 136 if (update_thread(top_threads[core], sched)) {
136 cores_needing_context_switch |= (1ul << core); 137 cores_needing_context_switch |= (1ul << core);
137 } 138 }
@@ -663,32 +664,26 @@ void Scheduler::Reload() {
663} 664}
664 665
665void Scheduler::SwitchContextStep2() { 666void Scheduler::SwitchContextStep2() {
666 Thread* previous_thread = current_thread_prev.get();
667 Thread* new_thread = selected_thread.get();
668
669 // Load context of new thread 667 // Load context of new thread
670 Process* const previous_process = 668 if (selected_thread) {
671 previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr; 669 ASSERT_MSG(selected_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
672
673 if (new_thread) {
674 ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
675 "Thread must be runnable."); 670 "Thread must be runnable.");
676 671
677 // Cancel any outstanding wakeup events for this thread 672 // Cancel any outstanding wakeup events for this thread
678 new_thread->SetIsRunning(true); 673 selected_thread->SetIsRunning(true);
679 new_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); 674 selected_thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
680 new_thread->SetWasRunning(false); 675 selected_thread->SetWasRunning(false);
681 676
682 auto* const thread_owner_process = current_thread->GetOwnerProcess(); 677 auto* const thread_owner_process = current_thread->GetOwnerProcess();
683 if (thread_owner_process != nullptr) { 678 if (thread_owner_process != nullptr) {
684 system.Kernel().MakeCurrentProcess(thread_owner_process); 679 system.Kernel().MakeCurrentProcess(thread_owner_process);
685 } 680 }
686 if (!new_thread->IsHLEThread()) { 681 if (!selected_thread->IsHLEThread()) {
687 Core::ARM_Interface& cpu_core = new_thread->ArmInterface(); 682 Core::ARM_Interface& cpu_core = selected_thread->ArmInterface();
688 cpu_core.LoadContext(new_thread->GetContext32()); 683 cpu_core.LoadContext(selected_thread->GetContext32());
689 cpu_core.LoadContext(new_thread->GetContext64()); 684 cpu_core.LoadContext(selected_thread->GetContext64());
690 cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); 685 cpu_core.SetTlsAddress(selected_thread->GetTLSAddress());
691 cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); 686 cpu_core.SetTPIDR_EL0(selected_thread->GetTPIDR_EL0());
692 cpu_core.ChangeProcessorID(this->core_id); 687 cpu_core.ChangeProcessorID(this->core_id);
693 cpu_core.ClearExclusiveState(); 688 cpu_core.ClearExclusiveState();
694 } 689 }
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index b3b4b5169..36e3c26fb 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -289,7 +289,7 @@ private:
289 289
290class SchedulerLock { 290class SchedulerLock {
291public: 291public:
292 explicit SchedulerLock(KernelCore& kernel); 292 [[nodiscard]] explicit SchedulerLock(KernelCore& kernel);
293 ~SchedulerLock(); 293 ~SchedulerLock();
294 294
295protected: 295protected:
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 450f61fea..b6bdbd988 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -342,8 +342,9 @@ ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) {
342 */ 342 */
343#define CASCADE_RESULT(target, source) \ 343#define CASCADE_RESULT(target, source) \
344 auto CONCAT2(check_result_L, __LINE__) = source; \ 344 auto CONCAT2(check_result_L, __LINE__) = source; \
345 if (CONCAT2(check_result_L, __LINE__).Failed()) \ 345 if (CONCAT2(check_result_L, __LINE__).Failed()) { \
346 return CONCAT2(check_result_L, __LINE__).Code(); \ 346 return CONCAT2(check_result_L, __LINE__).Code(); \
347 } \
347 target = std::move(*CONCAT2(check_result_L, __LINE__)) 348 target = std::move(*CONCAT2(check_result_L, __LINE__))
348 349
349/** 350/**
@@ -351,6 +352,9 @@ ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) {
351 * non-success, or discarded otherwise. 352 * non-success, or discarded otherwise.
352 */ 353 */
353#define CASCADE_CODE(source) \ 354#define CASCADE_CODE(source) \
354 auto CONCAT2(check_result_L, __LINE__) = source; \ 355 do { \
355 if (CONCAT2(check_result_L, __LINE__).IsError()) \ 356 auto CONCAT2(check_result_L, __LINE__) = source; \
356 return CONCAT2(check_result_L, __LINE__); 357 if (CONCAT2(check_result_L, __LINE__).IsError()) { \
358 return CONCAT2(check_result_L, __LINE__); \
359 } \
360 } while (false)
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 63e4aeca0..eb54cb123 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -35,7 +35,7 @@ constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 30};
35constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; 35constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
36 36
37static std::string GetImagePath(Common::UUID uuid) { 37static std::string GetImagePath(Common::UUID uuid) {
38 return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 38 return Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
39 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; 39 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
40} 40}
41 41
@@ -318,7 +318,7 @@ protected:
318 IPC::ResponseBuilder rb{ctx, 3}; 318 IPC::ResponseBuilder rb{ctx, 3};
319 rb.Push(RESULT_SUCCESS); 319 rb.Push(RESULT_SUCCESS);
320 320
321 const FileUtil::IOFile image(GetImagePath(user_id), "rb"); 321 const Common::FS::IOFile image(GetImagePath(user_id), "rb");
322 if (!image.IsOpen()) { 322 if (!image.IsOpen()) {
323 LOG_WARNING(Service_ACC, 323 LOG_WARNING(Service_ACC,
324 "Failed to load user provided image! Falling back to built-in backup..."); 324 "Failed to load user provided image! Falling back to built-in backup...");
@@ -340,7 +340,7 @@ protected:
340 IPC::ResponseBuilder rb{ctx, 3}; 340 IPC::ResponseBuilder rb{ctx, 3};
341 rb.Push(RESULT_SUCCESS); 341 rb.Push(RESULT_SUCCESS);
342 342
343 const FileUtil::IOFile image(GetImagePath(user_id), "rb"); 343 const Common::FS::IOFile image(GetImagePath(user_id), "rb");
344 344
345 if (!image.IsOpen()) { 345 if (!image.IsOpen()) {
346 LOG_WARNING(Service_ACC, 346 LOG_WARNING(Service_ACC,
@@ -405,7 +405,7 @@ protected:
405 ProfileData data; 405 ProfileData data;
406 std::memcpy(&data, user_data.data(), sizeof(ProfileData)); 406 std::memcpy(&data, user_data.data(), sizeof(ProfileData));
407 407
408 FileUtil::IOFile image(GetImagePath(user_id), "wb"); 408 Common::FS::IOFile image(GetImagePath(user_id), "wb");
409 409
410 if (!image.IsOpen() || !image.Resize(image_data.size()) || 410 if (!image.IsOpen() || !image.Resize(image_data.size()) ||
411 image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() || 411 image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() ||
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index a98d57b5c..9b829e957 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -13,6 +13,7 @@
13 13
14namespace Service::Account { 14namespace Service::Account {
15 15
16namespace FS = Common::FS;
16using Common::UUID; 17using Common::UUID;
17 18
18struct UserRaw { 19struct UserRaw {
@@ -318,9 +319,8 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase&
318} 319}
319 320
320void ProfileManager::ParseUserSaveFile() { 321void ProfileManager::ParseUserSaveFile() {
321 FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 322 const FS::IOFile save(
322 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", 323 FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", "rb");
323 "rb");
324 324
325 if (!save.IsOpen()) { 325 if (!save.IsOpen()) {
326 LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new " 326 LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
@@ -366,22 +366,22 @@ void ProfileManager::WriteUserSaveFile() {
366 }; 366 };
367 } 367 }
368 368
369 const auto raw_path = 369 const auto raw_path = FS::GetUserPath(FS::UserPath::NANDDir) + "/system/save/8000000000000010";
370 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; 370 if (FS::Exists(raw_path) && !FS::IsDirectory(raw_path)) {
371 if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) 371 FS::Delete(raw_path);
372 FileUtil::Delete(raw_path); 372 }
373 373
374 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 374 const auto path =
375 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat"; 375 FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
376 376
377 if (!FileUtil::CreateFullPath(path)) { 377 if (!FS::CreateFullPath(path)) {
378 LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory " 378 LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory "
379 "nand/system/save/8000000000000010/su/avators to mitigate this " 379 "nand/system/save/8000000000000010/su/avators to mitigate this "
380 "issue."); 380 "issue.");
381 return; 381 return;
382 } 382 }
383 383
384 FileUtil::IOFile save(path, "wb"); 384 FS::IOFile save(path, "wb");
385 385
386 if (!save.IsOpen()) { 386 if (!save.IsOpen()) {
387 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " 387 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 55a1edf1a..7d92b25a3 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -378,7 +378,11 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext&
378} 378}
379 379
380void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { 380void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
381 LOG_WARNING(Service_AM, "(STUBBED) called"); 381 IPC::RequestParser rp{ctx};
382 const auto permission = rp.PopEnum<ScreenshotPermission>();
383 LOG_DEBUG(Service_AM, "called, permission={}", permission);
384
385 screenshot_permission = permission;
382 386
383 IPC::ResponseBuilder rb{ctx, 2}; 387 IPC::ResponseBuilder rb{ctx, 2};
384 rb.Push(RESULT_SUCCESS); 388 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 6cfb11b48..6e69796ec 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -149,6 +149,12 @@ private:
149 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); 149 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
150 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); 150 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
151 151
152 enum class ScreenshotPermission : u32 {
153 Inherit = 0,
154 Enable = 1,
155 Disable = 2,
156 };
157
152 Core::System& system; 158 Core::System& system;
153 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 159 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
154 Kernel::EventPair launchable_event; 160 Kernel::EventPair launchable_event;
@@ -157,6 +163,7 @@ private:
157 u32 idle_time_detection_extension = 0; 163 u32 idle_time_detection_extension = 0;
158 u64 num_fatal_sections_entered = 0; 164 u64 num_fatal_sections_entered = 0;
159 bool is_auto_sleep_disabled = false; 165 bool is_auto_sleep_disabled = false;
166 ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit;
160}; 167};
161 168
162class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { 169class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index 289da2619..bdeb0737a 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -122,8 +122,7 @@ void SoftwareKeyboard::ExecuteInteractive() {
122 122
123 switch (request) { 123 switch (request) {
124 case Request::Calc: { 124 case Request::Calc: {
125 broker.PushNormalDataFromApplet( 125 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{1}));
126 std::make_shared<IStorage>(std::move(std::vector<u8>{1})));
127 broker.SignalStateChanged(); 126 broker.SignalStateChanged();
128 break; 127 break;
129 } 128 }
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 9f30e167d..efe595c4f 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -293,8 +293,8 @@ void WebBrowser::Finalize() {
293 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data))); 293 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data)));
294 broker.SignalStateChanged(); 294 broker.SignalStateChanged();
295 295
296 if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) { 296 if (!temporary_dir.empty() && Common::FS::IsDirectory(temporary_dir)) {
297 FileUtil::DeleteDirRecursively(temporary_dir); 297 Common::FS::DeleteDirRecursively(temporary_dir);
298 } 298 }
299} 299}
300 300
@@ -452,10 +452,10 @@ void WebBrowser::InitializeOffline() {
452 }; 452 };
453 453
454 temporary_dir = 454 temporary_dir =
455 FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + "web_applet_" + 455 Common::FS::SanitizePath(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
456 WEB_SOURCE_NAMES[static_cast<u32>(source) - 1], 456 "web_applet_" + WEB_SOURCE_NAMES[static_cast<u32>(source) - 1],
457 FileUtil::DirectorySeparator::PlatformDefault); 457 Common::FS::DirectorySeparator::PlatformDefault);
458 FileUtil::DeleteDirRecursively(temporary_dir); 458 Common::FS::DeleteDirRecursively(temporary_dir);
459 459
460 u64 title_id = 0; // 0 corresponds to current process 460 u64 title_id = 0; // 0 corresponds to current process
461 ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8); 461 ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8);
@@ -492,8 +492,8 @@ void WebBrowser::InitializeOffline() {
492 } 492 }
493 493
494 filename = 494 filename =
495 FileUtil::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename, 495 Common::FS::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename,
496 FileUtil::DirectorySeparator::PlatformDefault); 496 Common::FS::DirectorySeparator::PlatformDefault);
497} 497}
498 498
499void WebBrowser::ExecuteShop() { 499void WebBrowser::ExecuteShop() {
@@ -551,7 +551,8 @@ void WebBrowser::ExecuteShop() {
551} 551}
552 552
553void WebBrowser::ExecuteOffline() { 553void WebBrowser::ExecuteOffline() {
554 frontend.OpenPageLocal(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); 554 frontend.OpenPageLocal(
555 filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
555} 556}
556 557
557} // namespace Service::AM::Applets 558} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index dd80dd1dc..9b4910e53 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -206,7 +206,7 @@ private:
206 AudioCore::StreamPtr stream; 206 AudioCore::StreamPtr stream;
207 std::string device_name; 207 std::string device_name;
208 208
209 [[maybe_unused]] AudoutParams audio_params {}; 209 [[maybe_unused]] AudoutParams audio_params{};
210 210
211 /// This is the event handle used to check if the audio buffer was released 211 /// This is the event handle used to check if the audio buffer was released
212 Kernel::EventPair buffer_event; 212 Kernel::EventPair buffer_event;
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp
index d29e78d7e..ca021a99f 100644
--- a/src/core/hle/service/bcat/backend/boxcat.cpp
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -89,12 +89,12 @@ constexpr u32 TIMEOUT_SECONDS = 30;
89 89
90std::string GetBINFilePath(u64 title_id) { 90std::string GetBINFilePath(u64 title_id) {
91 return fmt::format("{}bcat/{:016X}/launchparam.bin", 91 return fmt::format("{}bcat/{:016X}/launchparam.bin",
92 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); 92 Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id);
93} 93}
94 94
95std::string GetZIPFilePath(u64 title_id) { 95std::string GetZIPFilePath(u64 title_id) {
96 return fmt::format("{}bcat/{:016X}/data.zip", 96 return fmt::format("{}bcat/{:016X}/data.zip",
97 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); 97 Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id);
98} 98}
99 99
100// If the error is something the user should know about (build ID mismatch, bad client version), 100// If the error is something the user should know about (build ID mismatch, bad client version),
@@ -205,8 +205,8 @@ private:
205 {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)}, 205 {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)},
206 }; 206 };
207 207
208 if (FileUtil::Exists(path)) { 208 if (Common::FS::Exists(path)) {
209 FileUtil::IOFile file{path, "rb"}; 209 Common::FS::IOFile file{path, "rb"};
210 if (file.IsOpen()) { 210 if (file.IsOpen()) {
211 std::vector<u8> bytes(file.GetSize()); 211 std::vector<u8> bytes(file.GetSize());
212 file.ReadBytes(bytes.data(), bytes.size()); 212 file.ReadBytes(bytes.data(), bytes.size());
@@ -236,8 +236,8 @@ private:
236 return DownloadResult::InvalidContentType; 236 return DownloadResult::InvalidContentType;
237 } 237 }
238 238
239 FileUtil::CreateFullPath(path); 239 Common::FS::CreateFullPath(path);
240 FileUtil::IOFile file{path, "wb"}; 240 Common::FS::IOFile file{path, "wb"};
241 if (!file.IsOpen()) 241 if (!file.IsOpen())
242 return DownloadResult::GeneralFSError; 242 return DownloadResult::GeneralFSError;
243 if (!file.Resize(response->body.size())) 243 if (!file.Resize(response->body.size()))
@@ -290,7 +290,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
290 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); 290 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
291 291
292 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { 292 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
293 FileUtil::Delete(zip_path); 293 Common::FS::Delete(zip_path);
294 } 294 }
295 295
296 HandleDownloadDisplayResult(applet_manager, res); 296 HandleDownloadDisplayResult(applet_manager, res);
@@ -300,7 +300,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
300 300
301 progress.StartProcessingDataList(); 301 progress.StartProcessingDataList();
302 302
303 FileUtil::IOFile zip{zip_path, "rb"}; 303 Common::FS::IOFile zip{zip_path, "rb"};
304 const auto size = zip.GetSize(); 304 const auto size = zip.GetSize();
305 std::vector<u8> bytes(size); 305 std::vector<u8> bytes(size);
306 if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { 306 if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
@@ -365,8 +365,7 @@ bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress)
365 365
366 std::thread([this, title, &progress] { 366 std::thread([this, title, &progress] {
367 SynchronizeInternal(applet_manager, dir_getter, title, progress); 367 SynchronizeInternal(applet_manager, dir_getter, title, progress);
368 }) 368 }).detach();
369 .detach();
370 369
371 return true; 370 return true;
372} 371}
@@ -377,8 +376,7 @@ bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name,
377 376
378 std::thread([this, title, name, &progress] { 377 std::thread([this, title, name, &progress] {
379 SynchronizeInternal(applet_manager, dir_getter, title, progress, name); 378 SynchronizeInternal(applet_manager, dir_getter, title, progress, name);
380 }) 379 }).detach();
381 .detach();
382 380
383 return true; 381 return true;
384} 382}
@@ -422,7 +420,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
422 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); 420 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
423 421
424 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { 422 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
425 FileUtil::Delete(path); 423 Common::FS::Delete(path);
426 } 424 }
427 425
428 HandleDownloadDisplayResult(applet_manager, res); 426 HandleDownloadDisplayResult(applet_manager, res);
@@ -430,7 +428,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
430 } 428 }
431 } 429 }
432 430
433 FileUtil::IOFile bin{path, "rb"}; 431 Common::FS::IOFile bin{path, "rb"};
434 const auto size = bin.GetSize(); 432 const auto size = bin.GetSize();
435 std::vector<u8> bytes(size); 433 std::vector<u8> bytes(size);
436 if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { 434 if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 4490f8e4c..2cee1193c 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -36,7 +36,7 @@ constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
36 36
37static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, 37static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
38 std::string_view dir_name_) { 38 std::string_view dir_name_) {
39 std::string dir_name(FileUtil::SanitizePath(dir_name_)); 39 std::string dir_name(Common::FS::SanitizePath(dir_name_));
40 if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\") 40 if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
41 return base; 41 return base;
42 42
@@ -53,13 +53,13 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
53} 53}
54 54
55ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const { 55ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
56 std::string path(FileUtil::SanitizePath(path_)); 56 std::string path(Common::FS::SanitizePath(path_));
57 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 57 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
58 // dir can be nullptr if path contains subdirectories, create those prior to creating the file. 58 // dir can be nullptr if path contains subdirectories, create those prior to creating the file.
59 if (dir == nullptr) { 59 if (dir == nullptr) {
60 dir = backing->CreateSubdirectory(FileUtil::GetParentPath(path)); 60 dir = backing->CreateSubdirectory(Common::FS::GetParentPath(path));
61 } 61 }
62 auto file = dir->CreateFile(FileUtil::GetFilename(path)); 62 auto file = dir->CreateFile(Common::FS::GetFilename(path));
63 if (file == nullptr) { 63 if (file == nullptr) {
64 // TODO(DarkLordZach): Find a better error code for this 64 // TODO(DarkLordZach): Find a better error code for this
65 return RESULT_UNKNOWN; 65 return RESULT_UNKNOWN;
@@ -72,17 +72,17 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
72} 72}
73 73
74ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { 74ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
75 std::string path(FileUtil::SanitizePath(path_)); 75 std::string path(Common::FS::SanitizePath(path_));
76 if (path.empty()) { 76 if (path.empty()) {
77 // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but... 77 // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
78 return RESULT_SUCCESS; 78 return RESULT_SUCCESS;
79 } 79 }
80 80
81 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 81 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
82 if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) { 82 if (dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
83 return FileSys::ERROR_PATH_NOT_FOUND; 83 return FileSys::ERROR_PATH_NOT_FOUND;
84 } 84 }
85 if (!dir->DeleteFile(FileUtil::GetFilename(path))) { 85 if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
86 // TODO(DarkLordZach): Find a better error code for this 86 // TODO(DarkLordZach): Find a better error code for this
87 return RESULT_UNKNOWN; 87 return RESULT_UNKNOWN;
88 } 88 }
@@ -91,11 +91,11 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
91} 91}
92 92
93ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const { 93ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
94 std::string path(FileUtil::SanitizePath(path_)); 94 std::string path(Common::FS::SanitizePath(path_));
95 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 95 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
96 if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty()) 96 if (dir == nullptr && Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty())
97 dir = backing; 97 dir = backing;
98 auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path)); 98 auto new_dir = dir->CreateSubdirectory(Common::FS::GetFilename(path));
99 if (new_dir == nullptr) { 99 if (new_dir == nullptr) {
100 // TODO(DarkLordZach): Find a better error code for this 100 // TODO(DarkLordZach): Find a better error code for this
101 return RESULT_UNKNOWN; 101 return RESULT_UNKNOWN;
@@ -104,9 +104,9 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_)
104} 104}
105 105
106ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const { 106ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
107 std::string path(FileUtil::SanitizePath(path_)); 107 std::string path(Common::FS::SanitizePath(path_));
108 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 108 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
109 if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) { 109 if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
110 // TODO(DarkLordZach): Find a better error code for this 110 // TODO(DarkLordZach): Find a better error code for this
111 return RESULT_UNKNOWN; 111 return RESULT_UNKNOWN;
112 } 112 }
@@ -114,9 +114,9 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_)
114} 114}
115 115
116ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const { 116ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
117 std::string path(FileUtil::SanitizePath(path_)); 117 std::string path(Common::FS::SanitizePath(path_));
118 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 118 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
119 if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) { 119 if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
120 // TODO(DarkLordZach): Find a better error code for this 120 // TODO(DarkLordZach): Find a better error code for this
121 return RESULT_UNKNOWN; 121 return RESULT_UNKNOWN;
122 } 122 }
@@ -124,10 +124,10 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
124} 124}
125 125
126ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const { 126ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
127 const std::string sanitized_path(FileUtil::SanitizePath(path)); 127 const std::string sanitized_path(Common::FS::SanitizePath(path));
128 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(sanitized_path)); 128 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));
129 129
130 if (!dir->CleanSubdirectoryRecursive(FileUtil::GetFilename(sanitized_path))) { 130 if (!dir->CleanSubdirectoryRecursive(Common::FS::GetFilename(sanitized_path))) {
131 // TODO(DarkLordZach): Find a better error code for this 131 // TODO(DarkLordZach): Find a better error code for this
132 return RESULT_UNKNOWN; 132 return RESULT_UNKNOWN;
133 } 133 }
@@ -137,14 +137,14 @@ ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::stri
137 137
138ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, 138ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
139 const std::string& dest_path_) const { 139 const std::string& dest_path_) const {
140 std::string src_path(FileUtil::SanitizePath(src_path_)); 140 std::string src_path(Common::FS::SanitizePath(src_path_));
141 std::string dest_path(FileUtil::SanitizePath(dest_path_)); 141 std::string dest_path(Common::FS::SanitizePath(dest_path_));
142 auto src = backing->GetFileRelative(src_path); 142 auto src = backing->GetFileRelative(src_path);
143 if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { 143 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
144 // Use more-optimized vfs implementation rename. 144 // Use more-optimized vfs implementation rename.
145 if (src == nullptr) 145 if (src == nullptr)
146 return FileSys::ERROR_PATH_NOT_FOUND; 146 return FileSys::ERROR_PATH_NOT_FOUND;
147 if (!src->Rename(FileUtil::GetFilename(dest_path))) { 147 if (!src->Rename(Common::FS::GetFilename(dest_path))) {
148 // TODO(DarkLordZach): Find a better error code for this 148 // TODO(DarkLordZach): Find a better error code for this
149 return RESULT_UNKNOWN; 149 return RESULT_UNKNOWN;
150 } 150 }
@@ -162,7 +162,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
162 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), 162 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
163 "Could not write all of the bytes but everything else has succeded."); 163 "Could not write all of the bytes but everything else has succeded.");
164 164
165 if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) { 165 if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
166 // TODO(DarkLordZach): Find a better error code for this 166 // TODO(DarkLordZach): Find a better error code for this
167 return RESULT_UNKNOWN; 167 return RESULT_UNKNOWN;
168 } 168 }
@@ -172,14 +172,14 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
172 172
173ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, 173ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
174 const std::string& dest_path_) const { 174 const std::string& dest_path_) const {
175 std::string src_path(FileUtil::SanitizePath(src_path_)); 175 std::string src_path(Common::FS::SanitizePath(src_path_));
176 std::string dest_path(FileUtil::SanitizePath(dest_path_)); 176 std::string dest_path(Common::FS::SanitizePath(dest_path_));
177 auto src = GetDirectoryRelativeWrapped(backing, src_path); 177 auto src = GetDirectoryRelativeWrapped(backing, src_path);
178 if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { 178 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
179 // Use more-optimized vfs implementation rename. 179 // Use more-optimized vfs implementation rename.
180 if (src == nullptr) 180 if (src == nullptr)
181 return FileSys::ERROR_PATH_NOT_FOUND; 181 return FileSys::ERROR_PATH_NOT_FOUND;
182 if (!src->Rename(FileUtil::GetFilename(dest_path))) { 182 if (!src->Rename(Common::FS::GetFilename(dest_path))) {
183 // TODO(DarkLordZach): Find a better error code for this 183 // TODO(DarkLordZach): Find a better error code for this
184 return RESULT_UNKNOWN; 184 return RESULT_UNKNOWN;
185 } 185 }
@@ -198,7 +198,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
198 198
199ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_, 199ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
200 FileSys::Mode mode) const { 200 FileSys::Mode mode) const {
201 const std::string path(FileUtil::SanitizePath(path_)); 201 const std::string path(Common::FS::SanitizePath(path_));
202 std::string_view npath = path; 202 std::string_view npath = path;
203 while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) { 203 while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
204 npath.remove_prefix(1); 204 npath.remove_prefix(1);
@@ -218,7 +218,7 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::
218} 218}
219 219
220ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) { 220ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) {
221 std::string path(FileUtil::SanitizePath(path_)); 221 std::string path(Common::FS::SanitizePath(path_));
222 auto dir = GetDirectoryRelativeWrapped(backing, path); 222 auto dir = GetDirectoryRelativeWrapped(backing, path);
223 if (dir == nullptr) { 223 if (dir == nullptr) {
224 // TODO(DarkLordZach): Find a better error code for this 224 // TODO(DarkLordZach): Find a better error code for this
@@ -229,11 +229,11 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s
229 229
230ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( 230ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
231 const std::string& path_) const { 231 const std::string& path_) const {
232 std::string path(FileUtil::SanitizePath(path_)); 232 std::string path(Common::FS::SanitizePath(path_));
233 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 233 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
234 if (dir == nullptr) 234 if (dir == nullptr)
235 return FileSys::ERROR_PATH_NOT_FOUND; 235 return FileSys::ERROR_PATH_NOT_FOUND;
236 auto filename = FileUtil::GetFilename(path); 236 auto filename = Common::FS::GetFilename(path);
237 // TODO(Subv): Some games use the '/' path, find out what this means. 237 // TODO(Subv): Some games use the '/' path, find out what this means.
238 if (filename.empty()) 238 if (filename.empty())
239 return MakeResult(FileSys::EntryType::Directory); 239 return MakeResult(FileSys::EntryType::Directory);
@@ -695,13 +695,13 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
695 sdmc_factory = nullptr; 695 sdmc_factory = nullptr;
696 } 696 }
697 697
698 auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), 698 auto nand_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir),
699 FileSys::Mode::ReadWrite); 699 FileSys::Mode::ReadWrite);
700 auto sd_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), 700 auto sd_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir),
701 FileSys::Mode::ReadWrite); 701 FileSys::Mode::ReadWrite);
702 auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), 702 auto load_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
703 FileSys::Mode::ReadWrite); 703 FileSys::Mode::ReadWrite);
704 auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), 704 auto dump_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir),
705 FileSys::Mode::ReadWrite); 705 FileSys::Mode::ReadWrite);
706 706
707 if (bis_factory == nullptr) { 707 if (bis_factory == nullptr) {
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index ef67ad690..45fde8df2 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -24,6 +24,7 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
24constexpr std::size_t NPAD_OFFSET = 0x9A00; 24constexpr std::size_t NPAD_OFFSET = 0x9A00;
25constexpr u32 BATTERY_FULL = 2; 25constexpr u32 BATTERY_FULL = 2;
26constexpr u32 MAX_NPAD_ID = 7; 26constexpr u32 MAX_NPAD_ID = 7;
27constexpr std::size_t HANDHELD_INDEX = 8;
27constexpr std::array<u32, 10> npad_id_list{ 28constexpr std::array<u32, 10> npad_id_list{
28 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, 29 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
29}; 30};
@@ -33,19 +34,41 @@ enum class JoystickId : std::size_t {
33 Joystick_Right, 34 Joystick_Right,
34}; 35};
35 36
36static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) { 37Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
38 Settings::ControllerType type) {
37 switch (type) { 39 switch (type) {
38 case Settings::ControllerType::ProController: 40 case Settings::ControllerType::ProController:
39 return Controller_NPad::NPadControllerType::ProController; 41 return NPadControllerType::ProController;
40 case Settings::ControllerType::DualJoycon: 42 case Settings::ControllerType::DualJoyconDetached:
41 return Controller_NPad::NPadControllerType::JoyDual; 43 return NPadControllerType::JoyDual;
42 case Settings::ControllerType::LeftJoycon: 44 case Settings::ControllerType::LeftJoycon:
43 return Controller_NPad::NPadControllerType::JoyLeft; 45 return NPadControllerType::JoyLeft;
44 case Settings::ControllerType::RightJoycon: 46 case Settings::ControllerType::RightJoycon:
45 return Controller_NPad::NPadControllerType::JoyRight; 47 return NPadControllerType::JoyRight;
48 case Settings::ControllerType::Handheld:
49 return NPadControllerType::Handheld;
46 default: 50 default:
47 UNREACHABLE(); 51 UNREACHABLE();
48 return Controller_NPad::NPadControllerType::JoyDual; 52 return NPadControllerType::ProController;
53 }
54}
55
56Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
57 Controller_NPad::NPadControllerType type) {
58 switch (type) {
59 case NPadControllerType::ProController:
60 return Settings::ControllerType::ProController;
61 case NPadControllerType::JoyDual:
62 return Settings::ControllerType::DualJoyconDetached;
63 case NPadControllerType::JoyLeft:
64 return Settings::ControllerType::LeftJoycon;
65 case NPadControllerType::JoyRight:
66 return Settings::ControllerType::RightJoycon;
67 case NPadControllerType::Handheld:
68 return Settings::ControllerType::Handheld;
69 default:
70 UNREACHABLE();
71 return Settings::ControllerType::ProController;
49 } 72 }
50} 73}
51 74
@@ -60,9 +83,9 @@ std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
60 case 6: 83 case 6:
61 case 7: 84 case 7:
62 return npad_id; 85 return npad_id;
63 case 8: 86 case HANDHELD_INDEX:
64 case NPAD_HANDHELD: 87 case NPAD_HANDHELD:
65 return 8; 88 return HANDHELD_INDEX;
66 case 9: 89 case 9:
67 case NPAD_UNKNOWN: 90 case NPAD_UNKNOWN:
68 return 9; 91 return 9;
@@ -83,38 +106,48 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
83 case 6: 106 case 6:
84 case 7: 107 case 7:
85 return static_cast<u32>(index); 108 return static_cast<u32>(index);
86 case 8: 109 case HANDHELD_INDEX:
87 return NPAD_HANDHELD; 110 return NPAD_HANDHELD;
88 case 9: 111 case 9:
89 return NPAD_UNKNOWN; 112 return NPAD_UNKNOWN;
90 default: 113 default:
91 UNIMPLEMENTED_MSG("Unknown npad index {}", index); 114 UNIMPLEMENTED_MSG("Unknown npad index {}", index);
92 return 0; 115 return 0;
93 }; 116 }
94} 117}
95 118
96Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} 119Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
97Controller_NPad::~Controller_NPad() = default; 120Controller_NPad::~Controller_NPad() = default;
98 121
99void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { 122void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
100 const auto controller_type = connected_controllers[controller_idx].type; 123 const auto controller_type = connected_controllers[controller_idx].type;
101 auto& controller = shared_memory_entries[controller_idx]; 124 auto& controller = shared_memory_entries[controller_idx];
102 if (controller_type == NPadControllerType::None) { 125 if (controller_type == NPadControllerType::None) {
126 styleset_changed_events[controller_idx].writable->Signal();
103 return; 127 return;
104 } 128 }
105 controller.joy_styles.raw = 0; // Zero out 129 controller.joy_styles.raw = 0; // Zero out
106 controller.device_type.raw = 0; 130 controller.device_type.raw = 0;
131 controller.properties.raw = 0;
107 switch (controller_type) { 132 switch (controller_type) {
108 case NPadControllerType::None: 133 case NPadControllerType::None:
109 UNREACHABLE(); 134 UNREACHABLE();
110 break; 135 break;
136 case NPadControllerType::ProController:
137 controller.joy_styles.pro_controller.Assign(1);
138 controller.device_type.pro_controller.Assign(1);
139 controller.properties.is_vertical.Assign(1);
140 controller.properties.use_plus.Assign(1);
141 controller.properties.use_minus.Assign(1);
142 controller.pad_assignment = NPadAssignments::Single;
143 break;
111 case NPadControllerType::Handheld: 144 case NPadControllerType::Handheld:
112 controller.joy_styles.handheld.Assign(1); 145 controller.joy_styles.handheld.Assign(1);
113 controller.device_type.handheld.Assign(1); 146 controller.device_type.handheld.Assign(1);
114 controller.pad_assignment = NPadAssignments::Dual;
115 controller.properties.is_vertical.Assign(1); 147 controller.properties.is_vertical.Assign(1);
116 controller.properties.use_plus.Assign(1); 148 controller.properties.use_plus.Assign(1);
117 controller.properties.use_minus.Assign(1); 149 controller.properties.use_minus.Assign(1);
150 controller.pad_assignment = NPadAssignments::Dual;
118 break; 151 break;
119 case NPadControllerType::JoyDual: 152 case NPadControllerType::JoyDual:
120 controller.joy_styles.joycon_dual.Assign(1); 153 controller.joy_styles.joycon_dual.Assign(1);
@@ -144,14 +177,6 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
144 controller.device_type.pokeball.Assign(1); 177 controller.device_type.pokeball.Assign(1);
145 controller.pad_assignment = NPadAssignments::Single; 178 controller.pad_assignment = NPadAssignments::Single;
146 break; 179 break;
147 case NPadControllerType::ProController:
148 controller.joy_styles.pro_controller.Assign(1);
149 controller.device_type.pro_controller.Assign(1);
150 controller.properties.is_vertical.Assign(1);
151 controller.properties.use_plus.Assign(1);
152 controller.properties.use_minus.Assign(1);
153 controller.pad_assignment = NPadAssignments::Single;
154 break;
155 } 180 }
156 181
157 controller.single_color_error = ColorReadError::ReadOk; 182 controller.single_color_error = ColorReadError::ReadOk;
@@ -192,36 +217,25 @@ void Controller_NPad::OnInit() {
192 style.pokeball.Assign(1); 217 style.pokeball.Assign(1);
193 } 218 }
194 219
195 std::transform( 220 std::transform(Settings::values.players.begin(), Settings::values.players.end(),
196 Settings::values.players.begin(), Settings::values.players.end(), 221 connected_controllers.begin(), [](const Settings::PlayerInput& player) {
197 connected_controllers.begin(), [](const Settings::PlayerInput& player) { 222 return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
198 return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected}; 223 player.connected};
199 }); 224 });
200
201 std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
202 [](const ControllerHolder& holder) { return holder.is_connected; });
203 225
204 // Account for handheld 226 // Account for handheld
205 if (connected_controllers[8].is_connected) 227 if (connected_controllers[HANDHELD_INDEX].is_connected) {
206 connected_controllers[8].type = NPadControllerType::Handheld; 228 connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
229 }
207 230
208 supported_npad_id_types.resize(npad_id_list.size()); 231 supported_npad_id_types.resize(npad_id_list.size());
209 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 232 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
210 npad_id_list.size() * sizeof(u32)); 233 npad_id_list.size() * sizeof(u32));
211 234
212 // Add a default dual joycon controller if none are present.
213 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
214 [](const ControllerHolder& controller) { return controller.is_connected; })) {
215 supported_npad_id_types.resize(npad_id_list.size());
216 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
217 npad_id_list.size() * sizeof(u32));
218 AddNewController(NPadControllerType::JoyDual);
219 }
220
221 for (std::size_t i = 0; i < connected_controllers.size(); ++i) { 235 for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
222 const auto& controller = connected_controllers[i]; 236 const auto& controller = connected_controllers[i];
223 if (controller.is_connected) { 237 if (controller.is_connected) {
224 AddNewControllerAt(controller.type, IndexToNPad(i)); 238 AddNewControllerAt(controller.type, i);
225 } 239 }
226 } 240 }
227} 241}
@@ -309,8 +323,9 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
309 323
310void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 324void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
311 std::size_t data_len) { 325 std::size_t data_len) {
312 if (!IsControllerActivated()) 326 if (!IsControllerActivated()) {
313 return; 327 return;
328 }
314 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { 329 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
315 auto& npad = shared_memory_entries[i]; 330 auto& npad = shared_memory_entries[i];
316 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states, 331 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
@@ -360,13 +375,25 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
360 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; 375 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
361 376
362 libnx_entry.connection_status.raw = 0; 377 libnx_entry.connection_status.raw = 0;
378 libnx_entry.connection_status.IsConnected.Assign(1);
363 379
364 switch (controller_type) { 380 switch (controller_type) {
365 case NPadControllerType::None: 381 case NPadControllerType::None:
366 UNREACHABLE(); 382 UNREACHABLE();
367 break; 383 break;
384 case NPadControllerType::ProController:
385 main_controller.connection_status.raw = 0;
386 main_controller.connection_status.IsConnected.Assign(1);
387 main_controller.connection_status.IsWired.Assign(1);
388 main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
389 main_controller.pad.l_stick = pad_state.l_stick;
390 main_controller.pad.r_stick = pad_state.r_stick;
391
392 libnx_entry.connection_status.IsWired.Assign(1);
393 break;
368 case NPadControllerType::Handheld: 394 case NPadControllerType::Handheld:
369 handheld_entry.connection_status.raw = 0; 395 handheld_entry.connection_status.raw = 0;
396 handheld_entry.connection_status.IsConnected.Assign(1);
370 handheld_entry.connection_status.IsWired.Assign(1); 397 handheld_entry.connection_status.IsWired.Assign(1);
371 handheld_entry.connection_status.IsLeftJoyConnected.Assign(1); 398 handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
372 handheld_entry.connection_status.IsRightJoyConnected.Assign(1); 399 handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
@@ -375,57 +402,52 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
375 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw; 402 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
376 handheld_entry.pad.l_stick = pad_state.l_stick; 403 handheld_entry.pad.l_stick = pad_state.l_stick;
377 handheld_entry.pad.r_stick = pad_state.r_stick; 404 handheld_entry.pad.r_stick = pad_state.r_stick;
405
406 libnx_entry.connection_status.IsWired.Assign(1);
407 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
408 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
409 libnx_entry.connection_status.IsLeftJoyWired.Assign(1);
410 libnx_entry.connection_status.IsRightJoyWired.Assign(1);
378 break; 411 break;
379 case NPadControllerType::JoyDual: 412 case NPadControllerType::JoyDual:
380 dual_entry.connection_status.raw = 0; 413 dual_entry.connection_status.raw = 0;
381 414 dual_entry.connection_status.IsConnected.Assign(1);
382 dual_entry.connection_status.IsLeftJoyConnected.Assign(1); 415 dual_entry.connection_status.IsLeftJoyConnected.Assign(1);
383 dual_entry.connection_status.IsRightJoyConnected.Assign(1); 416 dual_entry.connection_status.IsRightJoyConnected.Assign(1);
384 dual_entry.connection_status.IsConnected.Assign(1);
385
386 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
387 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
388 libnx_entry.connection_status.IsConnected.Assign(1);
389
390 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; 417 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
391 dual_entry.pad.l_stick = pad_state.l_stick; 418 dual_entry.pad.l_stick = pad_state.l_stick;
392 dual_entry.pad.r_stick = pad_state.r_stick; 419 dual_entry.pad.r_stick = pad_state.r_stick;
420
421 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
422 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
393 break; 423 break;
394 case NPadControllerType::JoyLeft: 424 case NPadControllerType::JoyLeft:
395 left_entry.connection_status.raw = 0; 425 left_entry.connection_status.raw = 0;
396
397 left_entry.connection_status.IsConnected.Assign(1); 426 left_entry.connection_status.IsConnected.Assign(1);
427 left_entry.connection_status.IsLeftJoyConnected.Assign(1);
398 left_entry.pad.pad_states.raw = pad_state.pad_states.raw; 428 left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
399 left_entry.pad.l_stick = pad_state.l_stick; 429 left_entry.pad.l_stick = pad_state.l_stick;
400 left_entry.pad.r_stick = pad_state.r_stick; 430 left_entry.pad.r_stick = pad_state.r_stick;
431
432 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
401 break; 433 break;
402 case NPadControllerType::JoyRight: 434 case NPadControllerType::JoyRight:
403 right_entry.connection_status.raw = 0; 435 right_entry.connection_status.raw = 0;
404
405 right_entry.connection_status.IsConnected.Assign(1); 436 right_entry.connection_status.IsConnected.Assign(1);
437 right_entry.connection_status.IsRightJoyConnected.Assign(1);
406 right_entry.pad.pad_states.raw = pad_state.pad_states.raw; 438 right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
407 right_entry.pad.l_stick = pad_state.l_stick; 439 right_entry.pad.l_stick = pad_state.l_stick;
408 right_entry.pad.r_stick = pad_state.r_stick; 440 right_entry.pad.r_stick = pad_state.r_stick;
441
442 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
409 break; 443 break;
410 case NPadControllerType::Pokeball: 444 case NPadControllerType::Pokeball:
411 pokeball_entry.connection_status.raw = 0; 445 pokeball_entry.connection_status.raw = 0;
412
413 pokeball_entry.connection_status.IsConnected.Assign(1); 446 pokeball_entry.connection_status.IsConnected.Assign(1);
414 pokeball_entry.connection_status.IsWired.Assign(1);
415
416 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw; 447 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
417 pokeball_entry.pad.l_stick = pad_state.l_stick; 448 pokeball_entry.pad.l_stick = pad_state.l_stick;
418 pokeball_entry.pad.r_stick = pad_state.r_stick; 449 pokeball_entry.pad.r_stick = pad_state.r_stick;
419 break; 450 break;
420 case NPadControllerType::ProController:
421 main_controller.connection_status.raw = 0;
422
423 main_controller.connection_status.IsConnected.Assign(1);
424 main_controller.connection_status.IsWired.Assign(1);
425 main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
426 main_controller.pad.l_stick = pad_state.l_stick;
427 main_controller.pad.r_stick = pad_state.r_stick;
428 break;
429 } 451 }
430 452
431 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate 453 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
@@ -453,26 +475,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
453 supported_npad_id_types.clear(); 475 supported_npad_id_types.clear();
454 supported_npad_id_types.resize(length / sizeof(u32)); 476 supported_npad_id_types.resize(length / sizeof(u32));
455 std::memcpy(supported_npad_id_types.data(), data, length); 477 std::memcpy(supported_npad_id_types.data(), data, length);
456 for (std::size_t i = 0; i < connected_controllers.size(); i++) {
457 auto& controller = connected_controllers[i];
458 if (!controller.is_connected) {
459 continue;
460 }
461 const auto requested_controller =
462 i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
463 : NPadControllerType::Handheld;
464 if (!IsControllerSupported(requested_controller)) {
465 const auto is_handheld = requested_controller == NPadControllerType::Handheld;
466 if (is_handheld) {
467 controller.type = NPadControllerType::None;
468 controller.is_connected = false;
469 AddNewController(requested_controller);
470 } else {
471 controller.type = requested_controller;
472 InitNewlyAddedControler(i);
473 }
474 }
475 }
476} 478}
477 479
478void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 480void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
@@ -504,7 +506,7 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
504 const std::vector<Vibration>& vibrations) { 506 const std::vector<Vibration>& vibrations) {
505 LOG_DEBUG(Service_HID, "(STUBBED) called"); 507 LOG_DEBUG(Service_HID, "(STUBBED) called");
506 508
507 if (!can_controllers_vibrate) { 509 if (!Settings::values.vibration_enabled || !can_controllers_vibrate) {
508 return; 510 return;
509 } 511 }
510 for (std::size_t i = 0; i < controller_ids.size(); i++) { 512 for (std::size_t i = 0; i < controller_ids.size(); i++) {
@@ -517,8 +519,6 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
517} 519}
518 520
519std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { 521std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
520 // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
521 // be signalled at least once, and signaled after a new controller is connected?
522 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; 522 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
523 return styleset_event.readable; 523 return styleset_event.readable;
524} 524}
@@ -527,43 +527,43 @@ Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
527 return last_processed_vibration; 527 return last_processed_vibration;
528} 528}
529 529
530void Controller_NPad::AddNewController(NPadControllerType controller) { 530void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
531 controller = DecideBestController(controller); 531 UpdateControllerAt(controller, npad_index, true);
532 if (controller == NPadControllerType::Handheld) { 532}
533 connected_controllers[8] = {controller, true}; 533
534 InitNewlyAddedControler(8); 534void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
535 return; 535 bool connected) {
536 } 536 if (!connected) {
537 const auto pos = 537 DisconnectNPad(IndexToNPad(npad_index));
538 std::find_if(connected_controllers.begin(), connected_controllers.end() - 2,
539 [](const ControllerHolder& holder) { return !holder.is_connected; });
540 if (pos == connected_controllers.end() - 2) {
541 LOG_ERROR(Service_HID, "Cannot connect any more controllers!");
542 return; 538 return;
543 } 539 }
544 const auto controller_id = std::distance(connected_controllers.begin(), pos);
545 connected_controllers[controller_id] = {controller, true};
546 InitNewlyAddedControler(controller_id);
547}
548 540
549void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
550 controller = DecideBestController(controller);
551 if (controller == NPadControllerType::Handheld) { 541 if (controller == NPadControllerType::Handheld) {
552 connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true}; 542 Settings::values.players[HANDHELD_INDEX].controller_type =
553 InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD)); 543 MapNPadToSettingsType(controller);
544 Settings::values.players[HANDHELD_INDEX].connected = true;
545 connected_controllers[HANDHELD_INDEX] = {controller, true};
546 InitNewlyAddedController(HANDHELD_INDEX);
554 return; 547 return;
555 } 548 }
556 549
557 connected_controllers[NPadIdToIndex(npad_id)] = {controller, true}; 550 Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller);
558 InitNewlyAddedControler(NPadIdToIndex(npad_id)); 551 Settings::values.players[npad_index].connected = true;
559} 552 connected_controllers[npad_index] = {controller, true};
560 553 InitNewlyAddedController(npad_index);
561void Controller_NPad::ConnectNPad(u32 npad_id) {
562 connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
563} 554}
564 555
565void Controller_NPad::DisconnectNPad(u32 npad_id) { 556void Controller_NPad::DisconnectNPad(u32 npad_id) {
566 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; 557 const auto npad_index = NPadIdToIndex(npad_id);
558 connected_controllers[npad_index].is_connected = false;
559 Settings::values.players[npad_index].connected = false;
560
561 auto& controller = shared_memory_entries[npad_index];
562 controller.joy_styles.raw = 0; // Zero out
563 controller.device_type.raw = 0;
564 controller.properties.raw = 0;
565
566 styleset_changed_events[npad_index].writable->Signal();
567} 567}
568 568
569void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { 569void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
@@ -599,8 +599,8 @@ bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) {
599 599
600 std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); 600 std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type);
601 601
602 InitNewlyAddedControler(npad_index_1); 602 AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1);
603 InitNewlyAddedControler(npad_index_2); 603 AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2);
604 604
605 return true; 605 return true;
606} 606}
@@ -614,11 +614,11 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
614 case 0: 614 case 0:
615 return LedPattern{1, 0, 0, 0}; 615 return LedPattern{1, 0, 0, 0};
616 case 1: 616 case 1:
617 return LedPattern{0, 1, 0, 0}; 617 return LedPattern{1, 1, 0, 0};
618 case 2: 618 case 2:
619 return LedPattern{0, 0, 1, 0}; 619 return LedPattern{1, 1, 1, 0};
620 case 3: 620 case 3:
621 return LedPattern{0, 0, 0, 1}; 621 return LedPattern{1, 1, 1, 1};
622 case 4: 622 case 4:
623 return LedPattern{1, 0, 0, 1}; 623 return LedPattern{1, 0, 0, 1};
624 case 5: 624 case 5:
@@ -628,9 +628,8 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
628 case 7: 628 case 7:
629 return LedPattern{0, 1, 1, 0}; 629 return LedPattern{0, 1, 1, 0};
630 default: 630 default:
631 UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
632 return LedPattern{0, 0, 0, 0}; 631 return LedPattern{0, 0, 0, 0};
633 }; 632 }
634} 633}
635 634
636void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { 635void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 5d4c58a43..75ce5b731 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -118,10 +118,11 @@ public:
118 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const; 118 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
119 Vibration GetLastVibration() const; 119 Vibration GetLastVibration() const;
120 120
121 void AddNewController(NPadControllerType controller); 121 // Adds a new controller at an index.
122 void AddNewControllerAt(NPadControllerType controller, u32 npad_id); 122 void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index);
123 // Adds a new controller at an index with connection status.
124 void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
123 125
124 void ConnectNPad(u32 npad_id);
125 void DisconnectNPad(u32 npad_id); 126 void DisconnectNPad(u32 npad_id);
126 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); 127 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
127 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; 128 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
@@ -141,6 +142,8 @@ public:
141 // Specifically for cheat engine and other features. 142 // Specifically for cheat engine and other features.
142 u32 GetAndResetPressState(); 143 u32 GetAndResetPressState();
143 144
145 static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type);
146 static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type);
144 static std::size_t NPadIdToIndex(u32 npad_id); 147 static std::size_t NPadIdToIndex(u32 npad_id);
145 static u32 IndexToNPad(std::size_t index); 148 static u32 IndexToNPad(std::size_t index);
146 149
@@ -309,7 +312,7 @@ private:
309 bool is_connected; 312 bool is_connected;
310 }; 313 };
311 314
312 void InitNewlyAddedControler(std::size_t controller_idx); 315 void InitNewlyAddedController(std::size_t controller_idx);
313 bool IsControllerSupported(NPadControllerType controller) const; 316 bool IsControllerSupported(NPadControllerType controller) const;
314 NPadControllerType DecideBestController(NPadControllerType priority) const; 317 NPadControllerType DecideBestController(NPadControllerType priority) const;
315 void RequestPadStateUpdate(u32 npad_id); 318 void RequestPadStateUpdate(u32 npad_id);
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 1e95b7580..33416b5dd 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -38,11 +38,9 @@
38namespace Service::HID { 38namespace Service::HID {
39 39
40// Updating period for each HID device. 40// Updating period for each HID device.
41// TODO(ogniK): Find actual polling rate of hid 41// HID is polled every 15ms, this value was derived from
42constexpr auto pad_update_ns = std::chrono::nanoseconds{1000000000 / 66}; 42// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
43[[maybe_unused]] constexpr auto accelerometer_update_ns = 43constexpr auto pad_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.6Hz)
44 std::chrono::nanoseconds{1000000000 / 100};
45[[maybe_unused]] constexpr auto gyroscope_update_ticks = std::chrono::nanoseconds{1000000000 / 100};
46constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 44constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
47 45
48IAppletResource::IAppletResource(Core::System& system) 46IAppletResource::IAppletResource(Core::System& system)
@@ -845,8 +843,7 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
845void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { 843void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
846 IPC::RequestParser rp{ctx}; 844 IPC::RequestParser rp{ctx};
847 const auto can_vibrate{rp.Pop<bool>()}; 845 const auto can_vibrate{rp.Pop<bool>()};
848 applet_resource->GetController<Controller_NPad>(HidController::NPad) 846 Settings::values.vibration_enabled = can_vibrate;
849 .SetVibrationEnabled(can_vibrate);
850 847
851 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); 848 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
852 849
@@ -859,8 +856,7 @@ void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
859 856
860 IPC::ResponseBuilder rb{ctx, 3}; 857 IPC::ResponseBuilder rb{ctx, 3};
861 rb.Push(RESULT_SUCCESS); 858 rb.Push(RESULT_SUCCESS);
862 rb.Push( 859 rb.Push(Settings::values.vibration_enabled);
863 applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled());
864} 860}
865 861
866void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 862void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 1b52511a5..0240d6643 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -21,8 +21,9 @@ namespace Service::Nvidia::Devices {
21/// implement the ioctl interface. 21/// implement the ioctl interface.
22class nvdevice { 22class nvdevice {
23public: 23public:
24 explicit nvdevice(Core::System& system) : system{system} {}; 24 explicit nvdevice(Core::System& system) : system{system} {}
25 virtual ~nvdevice() = default; 25 virtual ~nvdevice() = default;
26
26 union Ioctl { 27 union Ioctl {
27 u32_le raw; 28 u32_le raw;
28 BitField<0, 8, u32> cmd; 29 BitField<0, 8, u32> cmd;
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index ff85cbba6..1ebe949c0 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -86,11 +86,13 @@ public:
86 86
87 [[nodiscard]] s64 GetNextTicks() const; 87 [[nodiscard]] s64 GetNextTicks() const;
88 88
89 [[nodiscard]] std::unique_lock<std::mutex> Lock() const { return std::unique_lock{*guard}; } 89 [[nodiscard]] std::unique_lock<std::mutex> Lock() const {
90 return std::unique_lock{*guard};
91 }
90 92
91 private : 93private:
92 /// Finds the display identified by the specified ID. 94 /// Finds the display identified by the specified ID.
93 [[nodiscard]] VI::Display* FindDisplay(u64 display_id); 95 [[nodiscard]] VI::Display* FindDisplay(u64 display_id);
94 96
95 /// Finds the display identified by the specified ID. 97 /// Finds the display identified by the specified ID.
96 [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const; 98 [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const;
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index b526a94fe..aabf166b7 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -57,7 +57,7 @@ public:
57 ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name); 57 ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name);
58 ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name); 58 ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name);
59 59
60 template <Common::IsBaseOf<Kernel::SessionRequestHandler> T> 60 template <Common::DerivedFrom<Kernel::SessionRequestHandler> T>
61 std::shared_ptr<T> GetService(const std::string& service_name) const { 61 std::shared_ptr<T> GetService(const std::string& service_name) const {
62 auto service = registered_services.find(service_name); 62 auto service = registered_services.find(service_name);
63 if (service == registered_services.end()) { 63 if (service == registered_services.end()) {
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
index c070d6e97..320672add 100644
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -73,10 +73,8 @@ TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::
73 73
74 std::string location_name; 74 std::string location_name;
75 const auto timezone_setting = Settings::GetTimeZoneString(); 75 const auto timezone_setting = Settings::GetTimeZoneString();
76 if (timezone_setting == "auto") { 76 if (timezone_setting == "auto" || timezone_setting == "default") {
77 location_name = Common::TimeZone::GetDefaultTimeZone(); 77 location_name = Common::TimeZone::GetDefaultTimeZone();
78 } else if (timezone_setting == "default") {
79 location_name = location_name;
80 } else { 78 } else {
81 location_name = timezone_setting; 79 location_name = timezone_setting;
82 } 80 }
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index b8f8f1448..9bc3a8840 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -25,7 +25,7 @@ namespace Loader {
25 25
26namespace { 26namespace {
27 27
28template <Common::IsBaseOf<AppLoader> T> 28template <Common::DerivedFrom<AppLoader> T>
29std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) { 29std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
30 const auto file_type = T::IdentifyType(file); 30 const auto file_type = T::IdentifyType(file);
31 if (file_type != FileType::Error) { 31 if (file_type != FileType::Error) {
@@ -67,7 +67,7 @@ FileType GuessFromFilename(const std::string& name) {
67 return FileType::NCA; 67 return FileType::NCA;
68 68
69 const std::string extension = 69 const std::string extension =
70 Common::ToLower(std::string(FileUtil::GetExtensionFromFilename(name))); 70 Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
71 71
72 if (extension == "elf") 72 if (extension == "elf")
73 return FileType::ELF; 73 return FileType::ELF;
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index eeebdf02e..e503118dd 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -42,7 +42,7 @@ u64 StandardVmCallbacks::HidKeysDown() {
42 if (applet_resource == nullptr) { 42 if (applet_resource == nullptr) {
43 LOG_WARNING(CheatEngine, 43 LOG_WARNING(CheatEngine,
44 "Attempted to read input state, but applet resource is not initialized!"); 44 "Attempted to read input state, but applet resource is not initialized!");
45 return false; 45 return 0;
46 } 46 }
47 47
48 const auto press_state = 48 const auto press_state =
@@ -199,17 +199,29 @@ void CheatEngine::Initialize() {
199 metadata.title_id = system.CurrentProcess()->GetTitleID(); 199 metadata.title_id = system.CurrentProcess()->GetTitleID();
200 200
201 const auto& page_table = system.CurrentProcess()->PageTable(); 201 const auto& page_table = system.CurrentProcess()->PageTable();
202 metadata.heap_extents = {page_table.GetHeapRegionStart(), page_table.GetHeapRegionSize()}; 202 metadata.heap_extents = {
203 metadata.address_space_extents = {page_table.GetAddressSpaceStart(), 203 .base = page_table.GetHeapRegionStart(),
204 page_table.GetAddressSpaceSize()}; 204 .size = page_table.GetHeapRegionSize(),
205 metadata.alias_extents = {page_table.GetAliasCodeRegionStart(), 205 };
206 page_table.GetAliasCodeRegionSize()}; 206
207 metadata.address_space_extents = {
208 .base = page_table.GetAddressSpaceStart(),
209 .size = page_table.GetAddressSpaceSize(),
210 };
211
212 metadata.alias_extents = {
213 .base = page_table.GetAliasCodeRegionStart(),
214 .size = page_table.GetAliasCodeRegionSize(),
215 };
207 216
208 is_pending_reload.exchange(true); 217 is_pending_reload.exchange(true);
209} 218}
210 219
211void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) { 220void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
212 metadata.main_nso_extents = {main_region_begin, main_region_size}; 221 metadata.main_nso_extents = {
222 .base = main_region_begin,
223 .size = main_region_size,
224 };
213} 225}
214 226
215void CheatEngine::Reload(std::vector<CheatEntry> cheats) { 227void CheatEngine::Reload(std::vector<CheatEntry> cheats) {
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index b899ac884..b93396a80 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -38,11 +38,11 @@ PerfStats::~PerfStats() {
38 std::ostringstream stream; 38 std::ostringstream stream;
39 std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index, 39 std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index,
40 std::ostream_iterator<double>(stream, "\n")); 40 std::ostream_iterator<double>(stream, "\n"));
41 const std::string& path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 41 const std::string& path = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
42 // %F Date format expanded is "%Y-%m-%d" 42 // %F Date format expanded is "%Y-%m-%d"
43 const std::string filename = 43 const std::string filename =
44 fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id); 44 fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id);
45 FileUtil::IOFile file(filename, "w"); 45 Common::FS::IOFile file(filename, "w");
46 file.WriteString(stream.str()); 46 file.WriteString(stream.str());
47} 47}
48 48
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index 76cfa5a17..0becdf642 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -28,8 +28,9 @@
28namespace { 28namespace {
29 29
30std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) { 30std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) {
31 return fmt::format("{}{}/{:016X}_{}.json", FileUtil::GetUserPath(FileUtil::UserPath::LogDir), 31 return fmt::format("{}{}/{:016X}_{}.json",
32 type, title_id, timestamp); 32 Common::FS::GetUserPath(Common::FS::UserPath::LogDir), type, title_id,
33 timestamp);
33} 34}
34 35
35std::string GetTimestamp() { 36std::string GetTimestamp() {
@@ -40,13 +41,13 @@ std::string GetTimestamp() {
40using namespace nlohmann; 41using namespace nlohmann;
41 42
42void SaveToFile(json json, const std::string& filename) { 43void SaveToFile(json json, const std::string& filename) {
43 if (!FileUtil::CreateFullPath(filename)) { 44 if (!Common::FS::CreateFullPath(filename)) {
44 LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename); 45 LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename);
45 return; 46 return;
46 } 47 }
47 48
48 std::ofstream file( 49 std::ofstream file(
49 FileUtil::SanitizePath(filename, FileUtil::DirectorySeparator::PlatformDefault)); 50 Common::FS::SanitizePath(filename, Common::FS::DirectorySeparator::PlatformDefault));
50 file << std::setw(4) << json << std::endl; 51 file << std::setw(4) << json << std::endl;
51} 52}
52 53
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 416b2d866..28d3f9099 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -13,56 +13,6 @@
13 13
14namespace Settings { 14namespace Settings {
15 15
16namespace NativeButton {
17const std::array<const char*, NumButtons> mapping = {{
18 "button_a",
19 "button_b",
20 "button_x",
21 "button_y",
22 "button_lstick",
23 "button_rstick",
24 "button_l",
25 "button_r",
26 "button_zl",
27 "button_zr",
28 "button_plus",
29 "button_minus",
30 "button_dleft",
31 "button_dup",
32 "button_dright",
33 "button_ddown",
34 "button_lstick_left",
35 "button_lstick_up",
36 "button_lstick_right",
37 "button_lstick_down",
38 "button_rstick_left",
39 "button_rstick_up",
40 "button_rstick_right",
41 "button_rstick_down",
42 "button_sl",
43 "button_sr",
44 "button_home",
45 "button_screenshot",
46}};
47}
48
49namespace NativeAnalog {
50const std::array<const char*, NumAnalogs> mapping = {{
51 "lstick",
52 "rstick",
53}};
54}
55
56namespace NativeMouseButton {
57const std::array<const char*, NumMouseButtons> mapping = {{
58 "left",
59 "right",
60 "middle",
61 "forward",
62 "back",
63}};
64}
65
66Values values = {}; 16Values values = {};
67bool configuring_global = true; 17bool configuring_global = true;
68 18
@@ -121,8 +71,8 @@ void LogSettings() {
121 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); 71 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
122 log_setting("Audio_OutputDevice", values.audio_device_id); 72 log_setting("Audio_OutputDevice", values.audio_device_id);
123 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd); 73 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd);
124 log_setting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); 74 log_setting("DataStorage_NandDir", Common::FS::GetUserPath(Common::FS::UserPath::NANDDir));
125 log_setting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)); 75 log_setting("DataStorage_SdmcDir", Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir));
126 log_setting("Debugging_UseGdbstub", values.use_gdbstub); 76 log_setting("Debugging_UseGdbstub", values.use_gdbstub);
127 log_setting("Debugging_GdbstubPort", values.gdbstub_port); 77 log_setting("Debugging_GdbstubPort", values.gdbstub_port);
128 log_setting("Debugging_ProgramArgs", values.program_args); 78 log_setting("Debugging_ProgramArgs", values.program_args);
diff --git a/src/core/settings.h b/src/core/settings.h
index bb145f193..732c6a894 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -12,340 +12,10 @@
12#include <string> 12#include <string>
13#include <vector> 13#include <vector>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "input_common/settings.h"
15 16
16namespace Settings { 17namespace Settings {
17 18
18namespace NativeButton {
19enum Values {
20 A,
21 B,
22 X,
23 Y,
24 LStick,
25 RStick,
26 L,
27 R,
28 ZL,
29 ZR,
30 Plus,
31 Minus,
32
33 DLeft,
34 DUp,
35 DRight,
36 DDown,
37
38 LStick_Left,
39 LStick_Up,
40 LStick_Right,
41 LStick_Down,
42
43 RStick_Left,
44 RStick_Up,
45 RStick_Right,
46 RStick_Down,
47
48 SL,
49 SR,
50
51 Home,
52 Screenshot,
53
54 NumButtons,
55};
56
57constexpr int BUTTON_HID_BEGIN = A;
58constexpr int BUTTON_NS_BEGIN = Home;
59
60constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
61constexpr int BUTTON_NS_END = NumButtons;
62
63constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
64constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
65
66extern const std::array<const char*, NumButtons> mapping;
67
68} // namespace NativeButton
69
70namespace NativeAnalog {
71enum Values {
72 LStick,
73 RStick,
74
75 NumAnalogs,
76};
77
78constexpr int STICK_HID_BEGIN = LStick;
79constexpr int STICK_HID_END = NumAnalogs;
80constexpr int NUM_STICKS_HID = NumAnalogs;
81
82extern const std::array<const char*, NumAnalogs> mapping;
83} // namespace NativeAnalog
84
85namespace NativeMouseButton {
86enum Values {
87 Left,
88 Right,
89 Middle,
90 Forward,
91 Back,
92
93 NumMouseButtons,
94};
95
96constexpr int MOUSE_HID_BEGIN = Left;
97constexpr int MOUSE_HID_END = NumMouseButtons;
98constexpr int NUM_MOUSE_HID = NumMouseButtons;
99
100extern const std::array<const char*, NumMouseButtons> mapping;
101} // namespace NativeMouseButton
102
103namespace NativeKeyboard {
104enum Keys {
105 None,
106 Error,
107
108 A = 4,
109 B,
110 C,
111 D,
112 E,
113 F,
114 G,
115 H,
116 I,
117 J,
118 K,
119 L,
120 M,
121 N,
122 O,
123 P,
124 Q,
125 R,
126 S,
127 T,
128 U,
129 V,
130 W,
131 X,
132 Y,
133 Z,
134 N1,
135 N2,
136 N3,
137 N4,
138 N5,
139 N6,
140 N7,
141 N8,
142 N9,
143 N0,
144 Enter,
145 Escape,
146 Backspace,
147 Tab,
148 Space,
149 Minus,
150 Equal,
151 LeftBrace,
152 RightBrace,
153 Backslash,
154 Tilde,
155 Semicolon,
156 Apostrophe,
157 Grave,
158 Comma,
159 Dot,
160 Slash,
161 CapsLockKey,
162
163 F1,
164 F2,
165 F3,
166 F4,
167 F5,
168 F6,
169 F7,
170 F8,
171 F9,
172 F10,
173 F11,
174 F12,
175
176 SystemRequest,
177 ScrollLockKey,
178 Pause,
179 Insert,
180 Home,
181 PageUp,
182 Delete,
183 End,
184 PageDown,
185 Right,
186 Left,
187 Down,
188 Up,
189
190 NumLockKey,
191 KPSlash,
192 KPAsterisk,
193 KPMinus,
194 KPPlus,
195 KPEnter,
196 KP1,
197 KP2,
198 KP3,
199 KP4,
200 KP5,
201 KP6,
202 KP7,
203 KP8,
204 KP9,
205 KP0,
206 KPDot,
207
208 Key102,
209 Compose,
210 Power,
211 KPEqual,
212
213 F13,
214 F14,
215 F15,
216 F16,
217 F17,
218 F18,
219 F19,
220 F20,
221 F21,
222 F22,
223 F23,
224 F24,
225
226 Open,
227 Help,
228 Properties,
229 Front,
230 Stop,
231 Repeat,
232 Undo,
233 Cut,
234 Copy,
235 Paste,
236 Find,
237 Mute,
238 VolumeUp,
239 VolumeDown,
240 CapsLockActive,
241 NumLockActive,
242 ScrollLockActive,
243 KPComma,
244
245 KPLeftParenthesis,
246 KPRightParenthesis,
247
248 LeftControlKey = 0xE0,
249 LeftShiftKey,
250 LeftAltKey,
251 LeftMetaKey,
252 RightControlKey,
253 RightShiftKey,
254 RightAltKey,
255 RightMetaKey,
256
257 MediaPlayPause,
258 MediaStopCD,
259 MediaPrevious,
260 MediaNext,
261 MediaEject,
262 MediaVolumeUp,
263 MediaVolumeDown,
264 MediaMute,
265 MediaWebsite,
266 MediaBack,
267 MediaForward,
268 MediaStop,
269 MediaFind,
270 MediaScrollUp,
271 MediaScrollDown,
272 MediaEdit,
273 MediaSleep,
274 MediaCoffee,
275 MediaRefresh,
276 MediaCalculator,
277
278 NumKeyboardKeys,
279};
280
281static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
282
283enum Modifiers {
284 LeftControl,
285 LeftShift,
286 LeftAlt,
287 LeftMeta,
288 RightControl,
289 RightShift,
290 RightAlt,
291 RightMeta,
292 CapsLock,
293 ScrollLock,
294 NumLock,
295
296 NumKeyboardMods,
297};
298
299constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
300constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
301constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
302
303constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
304constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
305constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
306
307} // namespace NativeKeyboard
308
309using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
310using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
311using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
312using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
313using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
314
315constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
316constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
317constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
318constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
319
320enum class ControllerType {
321 ProController,
322 DualJoycon,
323 RightJoycon,
324 LeftJoycon,
325};
326
327struct PlayerInput {
328 bool connected;
329 ControllerType type;
330 ButtonsRaw buttons;
331 AnalogsRaw analogs;
332
333 u32 body_color_right;
334 u32 button_color_right;
335 u32 body_color_left;
336 u32 button_color_left;
337};
338
339struct TouchscreenInput {
340 bool enabled;
341 std::string device;
342
343 u32 finger;
344 u32 diameter_x;
345 u32 diameter_y;
346 u32 rotation_angle;
347};
348
349enum class RendererBackend { 19enum class RendererBackend {
350 OpenGL = 0, 20 OpenGL = 0,
351 Vulkan = 1, 21 Vulkan = 1,
@@ -359,7 +29,8 @@ enum class GPUAccuracy : u32 {
359 29
360enum class CPUAccuracy { 30enum class CPUAccuracy {
361 Accurate = 0, 31 Accurate = 0,
362 DebugMode = 1, 32 Unsafe = 1,
33 DebugMode = 2,
363}; 34};
364 35
365extern bool configuring_global; 36extern bool configuring_global;
@@ -419,6 +90,9 @@ struct Values {
419 bool cpuopt_misc_ir; 90 bool cpuopt_misc_ir;
420 bool cpuopt_reduce_misalign_checks; 91 bool cpuopt_reduce_misalign_checks;
421 92
93 bool cpuopt_unsafe_unfuse_fma;
94 bool cpuopt_unsafe_reduce_fp_error;
95
422 // Renderer 96 // Renderer
423 Setting<RendererBackend> renderer_backend; 97 Setting<RendererBackend> renderer_backend;
424 bool renderer_debug; 98 bool renderer_debug;
@@ -457,6 +131,8 @@ struct Values {
457 // Controls 131 // Controls
458 std::array<PlayerInput, 10> players; 132 std::array<PlayerInput, 10> players;
459 133
134 bool use_docked_mode;
135
460 bool mouse_enabled; 136 bool mouse_enabled;
461 std::string mouse_device; 137 std::string mouse_device;
462 MouseButtonsRaw mouse_buttons; 138 MouseButtonsRaw mouse_buttons;
@@ -470,14 +146,15 @@ struct Values {
470 AnalogsRaw debug_pad_analogs; 146 AnalogsRaw debug_pad_analogs;
471 147
472 std::string motion_device; 148 std::string motion_device;
149
150 bool vibration_enabled;
151
473 TouchscreenInput touchscreen; 152 TouchscreenInput touchscreen;
474 std::atomic_bool is_device_reload_pending{true}; 153 std::atomic_bool is_device_reload_pending{true};
475 std::string udp_input_address; 154 std::string udp_input_address;
476 u16 udp_input_port; 155 u16 udp_input_port;
477 u8 udp_pad_index; 156 u8 udp_pad_index;
478 157
479 bool use_docked_mode;
480
481 // Data Storage 158 // Data Storage
482 bool use_virtual_sd; 159 bool use_virtual_sd;
483 bool gamecard_inserted; 160 bool gamecard_inserted;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 5a30c75da..da09c0dbc 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -25,6 +25,8 @@
25 25
26namespace Core { 26namespace Core {
27 27
28namespace Telemetry = Common::Telemetry;
29
28static u64 GenerateTelemetryId() { 30static u64 GenerateTelemetryId() {
29 u64 telemetry_id{}; 31 u64 telemetry_id{};
30 32
@@ -70,12 +72,12 @@ static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) {
70 72
71u64 GetTelemetryId() { 73u64 GetTelemetryId() {
72 u64 telemetry_id{}; 74 u64 telemetry_id{};
73 const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + 75 const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) +
74 "telemetry_id"}; 76 "telemetry_id"};
75 77
76 bool generate_new_id = !FileUtil::Exists(filename); 78 bool generate_new_id = !Common::FS::Exists(filename);
77 if (!generate_new_id) { 79 if (!generate_new_id) {
78 FileUtil::IOFile file(filename, "rb"); 80 Common::FS::IOFile file(filename, "rb");
79 if (!file.IsOpen()) { 81 if (!file.IsOpen()) {
80 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 82 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
81 return {}; 83 return {};
@@ -88,7 +90,7 @@ u64 GetTelemetryId() {
88 } 90 }
89 91
90 if (generate_new_id) { 92 if (generate_new_id) {
91 FileUtil::IOFile file(filename, "wb"); 93 Common::FS::IOFile file(filename, "wb");
92 if (!file.IsOpen()) { 94 if (!file.IsOpen()) {
93 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 95 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
94 return {}; 96 return {};
@@ -102,10 +104,10 @@ u64 GetTelemetryId() {
102 104
103u64 RegenerateTelemetryId() { 105u64 RegenerateTelemetryId() {
104 const u64 new_telemetry_id{GenerateTelemetryId()}; 106 const u64 new_telemetry_id{GenerateTelemetryId()};
105 const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + 107 const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) +
106 "telemetry_id"}; 108 "telemetry_id"};
107 109
108 FileUtil::IOFile file(filename, "wb"); 110 Common::FS::IOFile file(filename, "wb");
109 if (!file.IsOpen()) { 111 if (!file.IsOpen()) {
110 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 112 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
111 return {}; 113 return {};
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index 17ac22377..66789d4bd 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -52,7 +52,7 @@ public:
52 * @param value Value for the field to add. 52 * @param value Value for the field to add.
53 */ 53 */
54 template <typename T> 54 template <typename T>
55 void AddField(Telemetry::FieldType type, const char* name, T value) { 55 void AddField(Common::Telemetry::FieldType type, const char* name, T value) {
56 field_collection.AddField(type, name, std::move(value)); 56 field_collection.AddField(type, name, std::move(value));
57 } 57 }
58 58
@@ -63,7 +63,8 @@ public:
63 bool SubmitTestcase(); 63 bool SubmitTestcase();
64 64
65private: 65private:
66 Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session 66 /// Tracks all added fields for the session
67 Common::Telemetry::FieldCollection field_collection;
67}; 68};
68 69
69/** 70/**
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 2003e096f..5c674a099 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -107,28 +107,21 @@ void Freezer::Unfreeze(VAddr address) {
107 107
108 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address); 108 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
109 109
110 entries.erase( 110 std::erase_if(entries, [address](const Entry& entry) { return entry.address == address; });
111 std::remove_if(entries.begin(), entries.end(),
112 [&address](const Entry& entry) { return entry.address == address; }),
113 entries.end());
114} 111}
115 112
116bool Freezer::IsFrozen(VAddr address) const { 113bool Freezer::IsFrozen(VAddr address) const {
117 std::lock_guard lock{entries_mutex}; 114 std::lock_guard lock{entries_mutex};
118 115
119 return std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { 116 return FindEntry(address) != entries.cend();
120 return entry.address == address;
121 }) != entries.end();
122} 117}
123 118
124void Freezer::SetFrozenValue(VAddr address, u64 value) { 119void Freezer::SetFrozenValue(VAddr address, u64 value) {
125 std::lock_guard lock{entries_mutex}; 120 std::lock_guard lock{entries_mutex};
126 121
127 const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { 122 const auto iter = FindEntry(address);
128 return entry.address == address;
129 });
130 123
131 if (iter == entries.end()) { 124 if (iter == entries.cend()) {
132 LOG_ERROR(Common_Memory, 125 LOG_ERROR(Common_Memory,
133 "Tried to set freeze value for address={:016X} that is not frozen!", address); 126 "Tried to set freeze value for address={:016X} that is not frozen!", address);
134 return; 127 return;
@@ -143,11 +136,9 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) {
143std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { 136std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
144 std::lock_guard lock{entries_mutex}; 137 std::lock_guard lock{entries_mutex};
145 138
146 const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { 139 const auto iter = FindEntry(address);
147 return entry.address == address;
148 });
149 140
150 if (iter == entries.end()) { 141 if (iter == entries.cend()) {
151 return std::nullopt; 142 return std::nullopt;
152 } 143 }
153 144
@@ -160,6 +151,16 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const {
160 return entries; 151 return entries;
161} 152}
162 153
154Freezer::Entries::iterator Freezer::FindEntry(VAddr address) {
155 return std::find_if(entries.begin(), entries.end(),
156 [address](const Entry& entry) { return entry.address == address; });
157}
158
159Freezer::Entries::const_iterator Freezer::FindEntry(VAddr address) const {
160 return std::find_if(entries.begin(), entries.end(),
161 [address](const Entry& entry) { return entry.address == address; });
162}
163
163void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { 164void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
164 if (!IsActive()) { 165 if (!IsActive()) {
165 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); 166 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h
index 2b2326bc4..0fdb701a7 100644
--- a/src/core/tools/freezer.h
+++ b/src/core/tools/freezer.h
@@ -73,13 +73,18 @@ public:
73 std::vector<Entry> GetEntries() const; 73 std::vector<Entry> GetEntries() const;
74 74
75private: 75private:
76 using Entries = std::vector<Entry>;
77
78 Entries::iterator FindEntry(VAddr address);
79 Entries::const_iterator FindEntry(VAddr address) const;
80
76 void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 81 void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
77 void FillEntryReads(); 82 void FillEntryReads();
78 83
79 std::atomic_bool active{false}; 84 std::atomic_bool active{false};
80 85
81 mutable std::mutex entries_mutex; 86 mutable std::mutex entries_mutex;
82 std::vector<Entry> entries; 87 Entries entries;
83 88
84 std::shared_ptr<Core::Timing::EventType> event; 89 std::shared_ptr<Core::Timing::EventType> event;
85 Core::Timing::CoreTiming& core_timing; 90 Core::Timing::CoreTiming& core_timing;
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
7 main.h 7 main.h
8 motion_emu.cpp 8 motion_emu.cpp
9 motion_emu.h 9 motion_emu.h
10 settings.cpp
11 settings.h
10 gcadapter/gc_adapter.cpp 12 gcadapter/gc_adapter.cpp
11 gcadapter/gc_adapter.h 13 gcadapter/gc_adapter.h
12 gcadapter/gc_poller.cpp 14 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() {
148 148
149class GCAnalog final : public Input::AnalogDevice { 149class GCAnalog final : public Input::AnalogDevice {
150public: 150public:
151 GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter) 151 GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter,
152 float range_)
152 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), 153 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
153 origin_value_x(adapter->GetOriginValue(port_, axis_x_)), 154 origin_value_x(adapter->GetOriginValue(port_, axis_x_)),
154 origin_value_y(adapter->GetOriginValue(port_, axis_y_)) {} 155 origin_value_y(adapter->GetOriginValue(port_, axis_y_)), range(range_) {}
155 156
156 float GetAxis(int axis) const { 157 float GetAxis(int axis) const {
157 if (gcadapter->DeviceConnected(port)) { 158 if (gcadapter->DeviceConnected(port)) {
158 std::lock_guard lock{mutex}; 159 std::lock_guard lock{mutex};
159 const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y; 160 const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
160 // division is not by a perfect 128 to account for some variance in center location 161 return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / (100.0f * range);
161 // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
162 // [20-230]
163 return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / 95.0f;
164 } 162 }
165 return 0.0f; 163 return 0.0f;
166 } 164 }
@@ -193,7 +191,7 @@ public:
193 191
194 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 192 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
195 const auto [x, y] = GetStatus(); 193 const auto [x, y] = GetStatus();
196 const float directional_deadzone = 0.4f; 194 const float directional_deadzone = 0.5f;
197 switch (direction) { 195 switch (direction) {
198 case Input::AnalogDirection::RIGHT: 196 case Input::AnalogDirection::RIGHT:
199 return x > directional_deadzone; 197 return x > directional_deadzone;
@@ -215,6 +213,7 @@ private:
215 GCAdapter::Adapter* gcadapter; 213 GCAdapter::Adapter* gcadapter;
216 const float origin_value_x; 214 const float origin_value_x;
217 const float origin_value_y; 215 const float origin_value_y;
216 const float range;
218 mutable std::mutex mutex; 217 mutable std::mutex mutex;
219}; 218};
220 219
@@ -233,9 +232,10 @@ std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::Param
233 const int port = params.Get("port", 0); 232 const int port = params.Get("port", 0);
234 const int axis_x = params.Get("axis_x", 0); 233 const int axis_x = params.Get("axis_x", 0);
235 const int axis_y = params.Get("axis_y", 1); 234 const int axis_y = params.Get("axis_y", 1);
236 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); 235 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
236 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
237 237
238 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get()); 238 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range);
239} 239}
240 240
241void GCAnalogFactory::BeginConfiguration() { 241void GCAnalogFactory::BeginConfiguration() {
diff --git a/src/input_common/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 @@
18 18
19namespace InputCommon { 19namespace InputCommon {
20 20
21static std::shared_ptr<Keyboard> keyboard; 21struct InputSubsystem::Impl {
22static std::shared_ptr<MotionEmu> motion_emu; 22 void Initialize() {
23 auto gcadapter = std::make_shared<GCAdapter::Adapter>();
24 gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
25 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
26 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
27 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
28
29 keyboard = std::make_shared<Keyboard>();
30 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
31 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
32 std::make_shared<AnalogFromButton>());
33 motion_emu = std::make_shared<MotionEmu>();
34 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
35
23#ifdef HAVE_SDL2 36#ifdef HAVE_SDL2
24static std::unique_ptr<SDL::State> sdl; 37 sdl = SDL::Init();
25#endif 38#endif
26static std::unique_ptr<CemuhookUDP::State> udp;
27static std::shared_ptr<GCButtonFactory> gcbuttons;
28static std::shared_ptr<GCAnalogFactory> gcanalog;
29
30void Init() {
31 auto gcadapter = std::make_shared<GCAdapter::Adapter>();
32 gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
33 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
34 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
35 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
36
37 keyboard = std::make_shared<Keyboard>();
38 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
39 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
40 std::make_shared<AnalogFromButton>());
41 motion_emu = std::make_shared<MotionEmu>();
42 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
43 39
40 udp = CemuhookUDP::Init();
41 }
42
43 void Shutdown() {
44 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
45 keyboard.reset();
46 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
47 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
48 motion_emu.reset();
44#ifdef HAVE_SDL2 49#ifdef HAVE_SDL2
45 sdl = SDL::Init(); 50 sdl.reset();
46#endif 51#endif
52 udp.reset();
53 Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
54 Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
55
56 gcbuttons.reset();
57 gcanalog.reset();
58 }
59
60 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
61 std::vector<Common::ParamPackage> devices = {
62 Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
63 Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "key"}},
64 };
65#ifdef HAVE_SDL2
66 auto sdl_devices = sdl->GetInputDevices();
67 devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
68#endif
69 auto udp_devices = udp->GetInputDevices();
70 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
71 return devices;
72 }
73
74 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
75 const Common::ParamPackage& params) const {
76 if (!params.Has("class") || params.Get("class", "") == "any") {
77 return {};
78 }
79 if (params.Get("class", "") == "key") {
80 // TODO consider returning the SDL key codes for the default keybindings
81 return {};
82 }
83#ifdef HAVE_SDL2
84 if (params.Get("class", "") == "sdl") {
85 return sdl->GetAnalogMappingForDevice(params);
86 }
87#endif
88 return {};
89 }
90
91 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(
92 const Common::ParamPackage& params) const {
93 if (!params.Has("class") || params.Get("class", "") == "any") {
94 return {};
95 }
96 if (params.Get("class", "") == "key") {
97 // TODO consider returning the SDL key codes for the default keybindings
98 return {};
99 }
100#ifdef HAVE_SDL2
101 if (params.Get("class", "") == "sdl") {
102 return sdl->GetButtonMappingForDevice(params);
103 }
104#endif
105 return {};
106 }
47 107
48 udp = CemuhookUDP::Init(); 108 std::shared_ptr<Keyboard> keyboard;
49} 109 std::shared_ptr<MotionEmu> motion_emu;
50
51void Shutdown() {
52 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
53 keyboard.reset();
54 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
55 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
56 motion_emu.reset();
57#ifdef HAVE_SDL2 110#ifdef HAVE_SDL2
58 sdl.reset(); 111 std::unique_ptr<SDL::State> sdl;
59#endif 112#endif
60 udp.reset(); 113 std::unique_ptr<CemuhookUDP::State> udp;
61 Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); 114 std::shared_ptr<GCButtonFactory> gcbuttons;
62 Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); 115 std::shared_ptr<GCAnalogFactory> gcanalog;
116};
117
118InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
119
120InputSubsystem::~InputSubsystem() = default;
121
122void InputSubsystem::Initialize() {
123 impl->Initialize();
124}
125
126void InputSubsystem::Shutdown() {
127 impl->Shutdown();
128}
129
130Keyboard* InputSubsystem::GetKeyboard() {
131 return impl->keyboard.get();
132}
133
134const Keyboard* InputSubsystem::GetKeyboard() const {
135 return impl->keyboard.get();
136}
63 137
64 gcbuttons.reset(); 138MotionEmu* InputSubsystem::GetMotionEmu() {
65 gcanalog.reset(); 139 return impl->motion_emu.get();
66} 140}
67 141
68Keyboard* GetKeyboard() { 142const MotionEmu* InputSubsystem::GetMotionEmu() const {
69 return keyboard.get(); 143 return impl->motion_emu.get();
70} 144}
71 145
72MotionEmu* GetMotionEmu() { 146std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
73 return motion_emu.get(); 147 return impl->GetInputDevices();
74} 148}
75 149
76GCButtonFactory* GetGCButtons() { 150AnalogMapping InputSubsystem::GetAnalogMappingForDevice(const Common::ParamPackage& device) const {
77 return gcbuttons.get(); 151 return impl->GetAnalogMappingForDevice(device);
78} 152}
79 153
80GCAnalogFactory* GetGCAnalogs() { 154ButtonMapping InputSubsystem::GetButtonMappingForDevice(const Common::ParamPackage& device) const {
81 return gcanalog.get(); 155 return impl->GetButtonMappingForDevice(device);
156}
157
158GCAnalogFactory* InputSubsystem::GetGCAnalogs() {
159 return impl->gcanalog.get();
160}
161
162const GCAnalogFactory* InputSubsystem::GetGCAnalogs() const {
163 return impl->gcanalog.get();
164}
165
166GCButtonFactory* InputSubsystem::GetGCButtons() {
167 return impl->gcbuttons.get();
168}
169
170const GCButtonFactory* InputSubsystem::GetGCButtons() const {
171 return impl->gcbuttons.get();
172}
173
174std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
175 Polling::DeviceType type) const {
176#ifdef HAVE_SDL2
177 return impl->sdl->GetPollers(type);
178#else
179 return {};
180#endif
82} 181}
83 182
84std::string GenerateKeyboardParam(int key_code) { 183std::string GenerateKeyboardParam(int key_code) {
@@ -102,18 +201,4 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left,
102 }; 201 };
103 return circle_pad_param.Serialize(); 202 return circle_pad_param.Serialize();
104} 203}
105
106namespace Polling {
107
108std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
109 std::vector<std::unique_ptr<DevicePoller>> pollers;
110
111#ifdef HAVE_SDL2
112 pollers = sdl->GetPollers(type);
113#endif
114
115 return pollers;
116}
117
118} // namespace Polling
119} // namespace InputCommon 204} // 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 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <unordered_map>
9#include <vector> 10#include <vector>
10#include "input_common/gcadapter/gc_poller.h"
11 11
12namespace Common { 12namespace Common {
13class ParamPackage; 13class ParamPackage;
14} 14}
15 15
16namespace InputCommon { 16namespace Settings::NativeAnalog {
17 17enum Values : int;
18/// Initializes and registers all built-in input device factories. 18}
19void Init();
20
21/// Deregisters all built-in input device factories and shuts them down.
22void Shutdown();
23
24class Keyboard;
25
26/// Gets the keyboard button device factory.
27Keyboard* GetKeyboard();
28
29class MotionEmu;
30
31/// Gets the motion emulation factory.
32MotionEmu* GetMotionEmu();
33
34GCButtonFactory* GetGCButtons();
35
36GCAnalogFactory* GetGCAnalogs();
37
38/// Generates a serialized param package for creating a keyboard button device
39std::string GenerateKeyboardParam(int key_code);
40 19
41/// Generates a serialized param package for creating an analog device taking input from keyboard 20namespace Settings::NativeButton {
42std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, 21enum Values : int;
43 int key_modifier, float modifier_scale); 22}
44 23
24namespace InputCommon {
45namespace Polling { 25namespace Polling {
46 26
47enum class DeviceType { Button, Analog }; 27enum class DeviceType { Button, AnalogPreferred };
48 28
49/** 29/**
50 * A class that can be used to get inputs from an input device like controllers without having to 30 * A class that can be used to get inputs from an input device like controllers without having to
@@ -54,7 +34,9 @@ class DevicePoller {
54public: 34public:
55 virtual ~DevicePoller() = default; 35 virtual ~DevicePoller() = default;
56 /// Setup and start polling for inputs, should be called before GetNextInput 36 /// Setup and start polling for inputs, should be called before GetNextInput
57 virtual void Start() = 0; 37 /// If a device_id is provided, events should be filtered to only include events from this
38 /// device id
39 virtual void Start(const std::string& device_id = "") = 0;
58 /// Stop polling 40 /// Stop polling
59 virtual void Stop() = 0; 41 virtual void Stop() = 0;
60 /** 42 /**
@@ -64,8 +46,89 @@ public:
64 */ 46 */
65 virtual Common::ParamPackage GetNextInput() = 0; 47 virtual Common::ParamPackage GetNextInput() = 0;
66}; 48};
67
68// Get all DevicePoller from all backends for a specific device type
69std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type);
70} // namespace Polling 49} // namespace Polling
50
51class GCAnalogFactory;
52class GCButtonFactory;
53class Keyboard;
54class MotionEmu;
55
56/**
57 * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
58 * mapping for the device. This is currently only implemented for the SDL backend devices.
59 */
60using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
61using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
62
63class InputSubsystem {
64public:
65 explicit InputSubsystem();
66 ~InputSubsystem();
67
68 InputSubsystem(const InputSubsystem&) = delete;
69 InputSubsystem& operator=(const InputSubsystem&) = delete;
70
71 InputSubsystem(InputSubsystem&&) = delete;
72 InputSubsystem& operator=(InputSubsystem&&) = delete;
73
74 /// Initializes and registers all built-in input device factories.
75 void Initialize();
76
77 /// Unregisters all built-in input device factories and shuts them down.
78 void Shutdown();
79
80 /// Retrieves the underlying keyboard device.
81 [[nodiscard]] Keyboard* GetKeyboard();
82
83 /// Retrieves the underlying keyboard device.
84 [[nodiscard]] const Keyboard* GetKeyboard() const;
85
86 /// Retrieves the underlying motion emulation factory.
87 [[nodiscard]] MotionEmu* GetMotionEmu();
88
89 /// Retrieves the underlying motion emulation factory.
90 [[nodiscard]] const MotionEmu* GetMotionEmu() const;
91
92 /**
93 * Returns all available input devices that this Factory can create a new device with.
94 * Each returned ParamPackage should have a `display` field used for display, a class field for
95 * backends to determine if this backend is meant to service the request and any other
96 * information needed to identify this in the backend later.
97 */
98 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const;
99
100 /// Retrieves the analog mappings for the given device.
101 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& device) const;
102
103 /// Retrieves the button mappings for the given device.
104 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const;
105
106 /// Retrieves the underlying GameCube analog handler.
107 [[nodiscard]] GCAnalogFactory* GetGCAnalogs();
108
109 /// Retrieves the underlying GameCube analog handler.
110 [[nodiscard]] const GCAnalogFactory* GetGCAnalogs() const;
111
112 /// Retrieves the underlying GameCube button handler.
113 [[nodiscard]] GCButtonFactory* GetGCButtons();
114
115 /// Retrieves the underlying GameCube button handler.
116 [[nodiscard]] const GCButtonFactory* GetGCButtons() const;
117
118 /// Get all DevicePoller from all backends for a specific device type
119 [[nodiscard]] std::vector<std::unique_ptr<Polling::DevicePoller>> GetPollers(
120 Polling::DeviceType type) const;
121
122private:
123 struct Impl;
124 std::unique_ptr<Impl> impl;
125};
126
127/// Generates a serialized param package for creating a keyboard button device
128std::string GenerateKeyboardParam(int key_code);
129
130/// Generates a serialized param package for creating an analog device taking input from keyboard
131std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
132 int key_modifier, float modifier_scale);
133
71} // namespace InputCommon 134} // 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 @@
6 6
7#include <memory> 7#include <memory>
8#include <vector> 8#include <vector>
9#include "common/param_package.h"
9#include "input_common/main.h" 10#include "input_common/main.h"
10 11
11namespace InputCommon::Polling { 12namespace InputCommon::Polling {
@@ -22,14 +23,24 @@ public:
22 /// Unregisters SDL device factories and shut them down. 23 /// Unregisters SDL device factories and shut them down.
23 virtual ~State() = default; 24 virtual ~State() = default;
24 25
25 virtual Pollers GetPollers(Polling::DeviceType type) = 0; 26 virtual Pollers GetPollers(Polling::DeviceType type) {
27 return {};
28 }
29
30 virtual std::vector<Common::ParamPackage> GetInputDevices() {
31 return {};
32 }
33
34 virtual ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage&) {
35 return {};
36 }
37 virtual AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage&) {
38 return {};
39 }
26}; 40};
27 41
28class NullState : public State { 42class NullState : public State {
29public: 43public:
30 Pollers GetPollers(Polling::DeviceType type) override {
31 return {};
32 }
33}; 44};
34 45
35std::unique_ptr<State> Init(); 46std::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 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array>
6#include <atomic> 7#include <atomic>
7#include <cmath> 8#include <cmath>
8#include <functional> 9#include <functional>
9#include <mutex> 10#include <mutex>
11#include <optional>
12#include <sstream>
10#include <string> 13#include <string>
11#include <thread> 14#include <thread>
12#include <tuple> 15#include <tuple>
@@ -15,15 +18,16 @@
15#include <vector> 18#include <vector>
16#include <SDL.h> 19#include <SDL.h>
17#include "common/logging/log.h" 20#include "common/logging/log.h"
18#include "common/math_util.h"
19#include "common/param_package.h" 21#include "common/param_package.h"
20#include "common/threadsafe_queue.h" 22#include "common/threadsafe_queue.h"
21#include "core/frontend/input.h" 23#include "core/frontend/input.h"
22#include "input_common/sdl/sdl_impl.h" 24#include "input_common/sdl/sdl_impl.h"
25#include "input_common/settings.h"
23 26
24namespace InputCommon::SDL { 27namespace InputCommon::SDL {
25 28
26static std::string GetGUID(SDL_Joystick* joystick) { 29namespace {
30std::string GetGUID(SDL_Joystick* joystick) {
27 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); 31 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
28 char guid_str[33]; 32 char guid_str[33];
29 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); 33 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
@@ -31,7 +35,8 @@ static std::string GetGUID(SDL_Joystick* joystick) {
31} 35}
32 36
33/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice 37/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
34static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event); 38Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
39} // Anonymous namespace
35 40
36static int SDLEventWatcher(void* user_data, SDL_Event* event) { 41static int SDLEventWatcher(void* user_data, SDL_Event* event) {
37 auto* const sdl_state = static_cast<SDLState*>(user_data); 42 auto* const sdl_state = static_cast<SDLState*>(user_data);
@@ -48,8 +53,10 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
48 53
49class SDLJoystick { 54class SDLJoystick {
50public: 55public:
51 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick) 56 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
52 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {} 57 SDL_GameController* gamecontroller)
58 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
59 sdl_controller{gamecontroller, &SDL_GameControllerClose} {}
53 60
54 void SetButton(int button, bool value) { 61 void SetButton(int button, bool value) {
55 std::lock_guard lock{mutex}; 62 std::lock_guard lock{mutex};
@@ -66,14 +73,14 @@ public:
66 state.axes.insert_or_assign(axis, value); 73 state.axes.insert_or_assign(axis, value);
67 } 74 }
68 75
69 float GetAxis(int axis) const { 76 float GetAxis(int axis, float range) const {
70 std::lock_guard lock{mutex}; 77 std::lock_guard lock{mutex};
71 return state.axes.at(axis) / 32767.0f; 78 return state.axes.at(axis) / (32767.0f * range);
72 } 79 }
73 80
74 std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const { 81 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const {
75 float x = GetAxis(axis_x); 82 float x = GetAxis(axis_x, range);
76 float y = GetAxis(axis_y); 83 float y = GetAxis(axis_y, range);
77 y = -y; // 3DS uses an y-axis inverse from SDL 84 y = -y; // 3DS uses an y-axis inverse from SDL
78 85
79 // Make sure the coordinates are in the unit circle, 86 // Make sure the coordinates are in the unit circle,
@@ -115,10 +122,15 @@ public:
115 return sdl_joystick.get(); 122 return sdl_joystick.get();
116 } 123 }
117 124
118 void SetSDLJoystick(SDL_Joystick* joystick) { 125 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
126 sdl_controller.reset(controller);
119 sdl_joystick.reset(joystick); 127 sdl_joystick.reset(joystick);
120 } 128 }
121 129
130 SDL_GameController* GetSDLGameController() const {
131 return sdl_controller.get();
132 }
133
122private: 134private:
123 struct State { 135 struct State {
124 std::unordered_map<int, bool> buttons; 136 std::unordered_map<int, bool> buttons;
@@ -128,6 +140,7 @@ private:
128 std::string guid; 140 std::string guid;
129 int port; 141 int port;
130 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; 142 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
143 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
131 mutable std::mutex mutex; 144 mutable std::mutex mutex;
132}; 145};
133 146
@@ -136,18 +149,19 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& g
136 const auto it = joystick_map.find(guid); 149 const auto it = joystick_map.find(guid);
137 if (it != joystick_map.end()) { 150 if (it != joystick_map.end()) {
138 while (it->second.size() <= static_cast<std::size_t>(port)) { 151 while (it->second.size() <= static_cast<std::size_t>(port)) {
139 auto joystick = 152 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
140 std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), nullptr); 153 nullptr, nullptr);
141 it->second.emplace_back(std::move(joystick)); 154 it->second.emplace_back(std::move(joystick));
142 } 155 }
143 return it->second[port]; 156 return it->second[port];
144 } 157 }
145 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr); 158 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
146 return joystick_map[guid].emplace_back(std::move(joystick)); 159 return joystick_map[guid].emplace_back(std::move(joystick));
147} 160}
148 161
149std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { 162std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
150 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); 163 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
164 auto sdl_controller = SDL_GameControllerFromInstanceID(sdl_id);
151 const std::string guid = GetGUID(sdl_joystick); 165 const std::string guid = GetGUID(sdl_joystick);
152 166
153 std::lock_guard lock{joystick_map_mutex}; 167 std::lock_guard lock{joystick_map_mutex};
@@ -171,23 +185,27 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_
171 }); 185 });
172 if (nullptr_it != map_it->second.end()) { 186 if (nullptr_it != map_it->second.end()) {
173 // ... and map it 187 // ... and map it
174 (*nullptr_it)->SetSDLJoystick(sdl_joystick); 188 (*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller);
175 return *nullptr_it; 189 return *nullptr_it;
176 } 190 }
177 191
178 // There is no SDLJoystick without a mapped SDL_Joystick 192 // There is no SDLJoystick without a mapped SDL_Joystick
179 // Create a new SDLJoystick 193 // Create a new SDLJoystick
180 const int port = static_cast<int>(map_it->second.size()); 194 const int port = static_cast<int>(map_it->second.size());
181 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick); 195 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_controller);
182 return map_it->second.emplace_back(std::move(joystick)); 196 return map_it->second.emplace_back(std::move(joystick));
183 } 197 }
184 198
185 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); 199 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_controller);
186 return joystick_map[guid].emplace_back(std::move(joystick)); 200 return joystick_map[guid].emplace_back(std::move(joystick));
187} 201}
188 202
189void SDLState::InitJoystick(int joystick_index) { 203void SDLState::InitJoystick(int joystick_index) {
190 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); 204 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
205 SDL_GameController* sdl_gamecontroller = nullptr;
206 if (SDL_IsGameController(joystick_index)) {
207 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
208 }
191 if (!sdl_joystick) { 209 if (!sdl_joystick) {
192 LOG_ERROR(Input, "failed to open joystick {}", joystick_index); 210 LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
193 return; 211 return;
@@ -196,7 +214,7 @@ void SDLState::InitJoystick(int joystick_index) {
196 214
197 std::lock_guard lock{joystick_map_mutex}; 215 std::lock_guard lock{joystick_map_mutex};
198 if (joystick_map.find(guid) == joystick_map.end()) { 216 if (joystick_map.find(guid) == joystick_map.end()) {
199 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); 217 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
200 joystick_map[guid].emplace_back(std::move(joystick)); 218 joystick_map[guid].emplace_back(std::move(joystick));
201 return; 219 return;
202 } 220 }
@@ -205,11 +223,11 @@ void SDLState::InitJoystick(int joystick_index) {
205 joystick_guid_list.begin(), joystick_guid_list.end(), 223 joystick_guid_list.begin(), joystick_guid_list.end(),
206 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); }); 224 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
207 if (it != joystick_guid_list.end()) { 225 if (it != joystick_guid_list.end()) {
208 (*it)->SetSDLJoystick(sdl_joystick); 226 (*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
209 return; 227 return;
210 } 228 }
211 const int port = static_cast<int>(joystick_guid_list.size()); 229 const int port = static_cast<int>(joystick_guid_list.size());
212 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick); 230 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
213 joystick_guid_list.emplace_back(std::move(joystick)); 231 joystick_guid_list.emplace_back(std::move(joystick));
214} 232}
215 233
@@ -231,7 +249,7 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
231 249
232 // Destruct SDL_Joystick outside the lock guard because SDL can internally call the 250 // Destruct SDL_Joystick outside the lock guard because SDL can internally call the
233 // event callback which locks the mutex again. 251 // event callback which locks the mutex again.
234 joystick->SetSDLJoystick(nullptr); 252 joystick->SetSDLJoystick(nullptr, nullptr);
235} 253}
236 254
237void SDLState::HandleGameControllerEvent(const SDL_Event& event) { 255void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
@@ -313,7 +331,7 @@ public:
313 trigger_if_greater(trigger_if_greater_) {} 331 trigger_if_greater(trigger_if_greater_) {}
314 332
315 bool GetStatus() const override { 333 bool GetStatus() const override {
316 const float axis_value = joystick->GetAxis(axis); 334 const float axis_value = joystick->GetAxis(axis, 1.0f);
317 if (trigger_if_greater) { 335 if (trigger_if_greater) {
318 return axis_value > threshold; 336 return axis_value > threshold;
319 } 337 }
@@ -329,22 +347,24 @@ private:
329 347
330class SDLAnalog final : public Input::AnalogDevice { 348class SDLAnalog final : public Input::AnalogDevice {
331public: 349public:
332 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_) 350 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_,
333 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_) {} 351 float range_)
352 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_),
353 range(range_) {}
334 354
335 std::tuple<float, float> GetStatus() const override { 355 std::tuple<float, float> GetStatus() const override {
336 const auto [x, y] = joystick->GetAnalog(axis_x, axis_y); 356 const auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range);
337 const float r = std::sqrt((x * x) + (y * y)); 357 const float r = std::sqrt((x * x) + (y * y));
338 if (r > deadzone) { 358 if (r > deadzone) {
339 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone), 359 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
340 y / r * (r - deadzone) / (1 - deadzone)); 360 y / r * (r - deadzone) / (1 - deadzone));
341 } 361 }
342 return std::make_tuple<float, float>(0.0f, 0.0f); 362 return {};
343 } 363 }
344 364
345 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 365 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
346 const auto [x, y] = GetStatus(); 366 const auto [x, y] = GetStatus();
347 const float directional_deadzone = 0.4f; 367 const float directional_deadzone = 0.5f;
348 switch (direction) { 368 switch (direction) {
349 case Input::AnalogDirection::RIGHT: 369 case Input::AnalogDirection::RIGHT:
350 return x > directional_deadzone; 370 return x > directional_deadzone;
@@ -363,6 +383,7 @@ private:
363 const int axis_x; 383 const int axis_x;
364 const int axis_y; 384 const int axis_y;
365 const float deadzone; 385 const float deadzone;
386 const float range;
366}; 387};
367 388
368/// A button device factory that creates button devices from SDL joystick 389/// A button device factory that creates button devices from SDL joystick
@@ -457,14 +478,14 @@ public:
457 const int port = params.Get("port", 0); 478 const int port = params.Get("port", 0);
458 const int axis_x = params.Get("axis_x", 0); 479 const int axis_x = params.Get("axis_x", 0);
459 const int axis_y = params.Get("axis_y", 1); 480 const int axis_y = params.Get("axis_y", 1);
460 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); 481 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
461 482 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
462 auto joystick = state.GetSDLJoystickByGUID(guid, port); 483 auto joystick = state.GetSDLJoystickByGUID(guid, port);
463 484
464 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash 485 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
465 joystick->SetAxis(axis_x, 0); 486 joystick->SetAxis(axis_x, 0);
466 joystick->SetAxis(axis_y, 0); 487 joystick->SetAxis(axis_y, 0);
467 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone); 488 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone, range);
468 } 489 }
469 490
470private: 491private:
@@ -473,8 +494,10 @@ private:
473 494
474SDLState::SDLState() { 495SDLState::SDLState() {
475 using namespace Input; 496 using namespace Input;
476 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this)); 497 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
477 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this)); 498 button_factory = std::make_shared<SDLButtonFactory>(*this);
499 RegisterFactory<AnalogDevice>("sdl", analog_factory);
500 RegisterFactory<ButtonDevice>("sdl", button_factory);
478 501
479 // If the frontend is going to manage the event loop, then we dont start one here 502 // If the frontend is going to manage the event loop, then we dont start one here
480 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK); 503 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK);
@@ -482,6 +505,7 @@ SDLState::SDLState() {
482 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); 505 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
483 return; 506 return;
484 } 507 }
508 has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
485 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) { 509 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
486 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError()); 510 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
487 } 511 }
@@ -494,7 +518,7 @@ SDLState::SDLState() {
494 using namespace std::chrono_literals; 518 using namespace std::chrono_literals;
495 while (initialized) { 519 while (initialized) {
496 SDL_PumpEvents(); 520 SDL_PumpEvents();
497 std::this_thread::sleep_for(10ms); 521 std::this_thread::sleep_for(5ms);
498 } 522 }
499 }); 523 });
500 } 524 }
@@ -520,65 +544,230 @@ SDLState::~SDLState() {
520 } 544 }
521} 545}
522 546
523static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { 547std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
548 std::scoped_lock lock(joystick_map_mutex);
549 std::vector<Common::ParamPackage> devices;
550 for (const auto& [key, value] : joystick_map) {
551 for (const auto& joystick : value) {
552 auto joy = joystick->GetSDLJoystick();
553 if (auto controller = joystick->GetSDLGameController()) {
554 std::string name =
555 fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
556 devices.emplace_back(Common::ParamPackage{
557 {"class", "sdl"},
558 {"display", std::move(name)},
559 {"guid", joystick->GetGUID()},
560 {"port", std::to_string(joystick->GetPort())},
561 });
562 } else if (joy) {
563 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
564 devices.emplace_back(Common::ParamPackage{
565 {"class", "sdl"},
566 {"display", std::move(name)},
567 {"guid", joystick->GetGUID()},
568 {"port", std::to_string(joystick->GetPort())},
569 });
570 }
571 }
572 }
573 return devices;
574}
575
576namespace {
577Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis,
578 float value = 0.1f) {
579 Common::ParamPackage params({{"engine", "sdl"}});
580 params.Set("port", port);
581 params.Set("guid", std::move(guid));
582 params.Set("axis", axis);
583 if (value > 0) {
584 params.Set("direction", "+");
585 params.Set("threshold", "0.5");
586 } else {
587 params.Set("direction", "-");
588 params.Set("threshold", "-0.5");
589 }
590 return params;
591}
592
593Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) {
524 Common::ParamPackage params({{"engine", "sdl"}}); 594 Common::ParamPackage params({{"engine", "sdl"}});
595 params.Set("port", port);
596 params.Set("guid", std::move(guid));
597 params.Set("button", button);
598 return params;
599}
600
601Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) {
602 Common::ParamPackage params({{"engine", "sdl"}});
603
604 params.Set("port", port);
605 params.Set("guid", std::move(guid));
606 params.Set("hat", hat);
607 switch (value) {
608 case SDL_HAT_UP:
609 params.Set("direction", "up");
610 break;
611 case SDL_HAT_DOWN:
612 params.Set("direction", "down");
613 break;
614 case SDL_HAT_LEFT:
615 params.Set("direction", "left");
616 break;
617 case SDL_HAT_RIGHT:
618 params.Set("direction", "right");
619 break;
620 default:
621 return {};
622 }
623 return params;
624}
525 625
626Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
526 switch (event.type) { 627 switch (event.type) {
527 case SDL_JOYAXISMOTION: { 628 case SDL_JOYAXISMOTION: {
528 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 629 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
529 params.Set("port", joystick->GetPort()); 630 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
530 params.Set("guid", joystick->GetGUID()); 631 event.jaxis.axis, event.jaxis.value);
531 params.Set("axis", event.jaxis.axis);
532 if (event.jaxis.value > 0) {
533 params.Set("direction", "+");
534 params.Set("threshold", "0.5");
535 } else {
536 params.Set("direction", "-");
537 params.Set("threshold", "-0.5");
538 }
539 break;
540 } 632 }
541 case SDL_JOYBUTTONUP: { 633 case SDL_JOYBUTTONUP: {
542 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); 634 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
543 params.Set("port", joystick->GetPort()); 635 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
544 params.Set("guid", joystick->GetGUID()); 636 event.jbutton.button);
545 params.Set("button", event.jbutton.button);
546 break;
547 } 637 }
548 case SDL_JOYHATMOTION: { 638 case SDL_JOYHATMOTION: {
549 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); 639 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
550 params.Set("port", joystick->GetPort()); 640 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
551 params.Set("guid", joystick->GetGUID()); 641 event.jhat.hat, event.jhat.value);
552 params.Set("hat", event.jhat.hat);
553 switch (event.jhat.value) {
554 case SDL_HAT_UP:
555 params.Set("direction", "up");
556 break;
557 case SDL_HAT_DOWN:
558 params.Set("direction", "down");
559 break;
560 case SDL_HAT_LEFT:
561 params.Set("direction", "left");
562 break;
563 case SDL_HAT_RIGHT:
564 params.Set("direction", "right");
565 break;
566 default:
567 return {};
568 }
569 break;
570 } 642 }
571 } 643 }
644 return {};
645}
646
647Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
648 const SDL_GameControllerButtonBind& binding) {
649 switch (binding.bindType) {
650 case SDL_CONTROLLER_BINDTYPE_AXIS:
651 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
652 case SDL_CONTROLLER_BINDTYPE_BUTTON:
653 return BuildButtonParamPackageForButton(port, guid, binding.value.button);
654 case SDL_CONTROLLER_BINDTYPE_HAT:
655 return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
656 binding.value.hat.hat_mask);
657 }
658 return {};
659}
660
661Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
662 int axis_y) {
663 Common::ParamPackage params;
664 params.Set("engine", "sdl");
665 params.Set("port", port);
666 params.Set("guid", guid);
667 params.Set("axis_x", axis_x);
668 params.Set("axis_y", axis_y);
572 return params; 669 return params;
573} 670}
671} // Anonymous namespace
574 672
575namespace Polling { 673ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) {
674 if (!params.Has("guid") || !params.Has("port")) {
675 return {};
676 }
677 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
678 auto* controller = joystick->GetSDLGameController();
679 if (controller == nullptr) {
680 return {};
681 }
682
683 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
684 // We will add those afterwards
685 // This list also excludes Screenshot since theres not really a mapping for that
686 using ButtonBindings =
687 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
688 static constexpr ButtonBindings switch_to_sdl_button{{
689 {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
690 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
691 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
692 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
693 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
694 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
695 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
696 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
697 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
698 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
699 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
700 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
701 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
702 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
703 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
704 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
705 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
706 }};
707
708 // Add the missing bindings for ZL/ZR
709 using ZBindings =
710 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
711 static constexpr ZBindings switch_to_sdl_axis{{
712 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
713 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
714 }};
715
716 ButtonMapping mapping;
717 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
718
719 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
720 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
721 mapping.insert_or_assign(
722 switch_button,
723 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
724 }
725 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
726 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
727 mapping.insert_or_assign(
728 switch_button,
729 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
730 }
731
732 return mapping;
733}
576 734
735AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
736 if (!params.Has("guid") || !params.Has("port")) {
737 return {};
738 }
739 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
740 auto* controller = joystick->GetSDLGameController();
741 if (controller == nullptr) {
742 return {};
743 }
744
745 AnalogMapping mapping = {};
746 const auto& binding_left_x =
747 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
748 const auto& binding_left_y =
749 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
750 mapping.insert_or_assign(Settings::NativeAnalog::LStick,
751 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
752 binding_left_x.value.axis,
753 binding_left_y.value.axis));
754 const auto& binding_right_x =
755 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
756 const auto& binding_right_y =
757 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
758 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
759 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
760 binding_right_x.value.axis,
761 binding_right_y.value.axis));
762 return mapping;
763}
764
765namespace Polling {
577class SDLPoller : public InputCommon::Polling::DevicePoller { 766class SDLPoller : public InputCommon::Polling::DevicePoller {
578public: 767public:
579 explicit SDLPoller(SDLState& state_) : state(state_) {} 768 explicit SDLPoller(SDLState& state_) : state(state_) {}
580 769
581 void Start() override { 770 void Start(const std::string& device_id) override {
582 state.event_queue.Clear(); 771 state.event_queue.Clear();
583 state.polling = true; 772 state.polling = true;
584 } 773 }
@@ -598,71 +787,106 @@ public:
598 Common::ParamPackage GetNextInput() override { 787 Common::ParamPackage GetNextInput() override {
599 SDL_Event event; 788 SDL_Event event;
600 while (state.event_queue.Pop(event)) { 789 while (state.event_queue.Pop(event)) {
601 switch (event.type) { 790 const auto package = FromEvent(event);
602 case SDL_JOYAXISMOTION: 791 if (package) {
603 if (std::abs(event.jaxis.value / 32767.0) < 0.5) { 792 return *package;
604 break;
605 }
606 [[fallthrough]];
607 case SDL_JOYBUTTONUP:
608 case SDL_JOYHATMOTION:
609 return SDLEventToButtonParamPackage(state, event);
610 } 793 }
611 } 794 }
612 return {}; 795 return {};
613 } 796 }
797 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const {
798 switch (event.type) {
799 case SDL_JOYAXISMOTION:
800 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
801 break;
802 }
803 [[fallthrough]];
804 case SDL_JOYBUTTONUP:
805 case SDL_JOYHATMOTION:
806 return {SDLEventToButtonParamPackage(state, event)};
807 }
808 return std::nullopt;
809 }
614}; 810};
615 811
616class SDLAnalogPoller final : public SDLPoller { 812/**
813 * Attempts to match the press to a controller joy axis (left/right stick) and if a match
814 * isn't found, checks if the event matches anything from SDLButtonPoller and uses that
815 * instead
816 */
817class SDLAnalogPreferredPoller final : public SDLPoller {
617public: 818public:
618 explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {} 819 explicit SDLAnalogPreferredPoller(SDLState& state_)
619 820 : SDLPoller(state_), button_poller(state_) {}
620 void Start() override {
621 SDLPoller::Start();
622 821
822 void Start(const std::string& device_id) override {
823 SDLPoller::Start(device_id);
824 // Load the game controller
623 // Reset stored axes 825 // Reset stored axes
624 analog_x_axis = -1; 826 analog_x_axis = -1;
625 analog_y_axis = -1; 827 analog_y_axis = -1;
626 analog_axes_joystick = -1;
627 } 828 }
628 829
629 Common::ParamPackage GetNextInput() override { 830 Common::ParamPackage GetNextInput() override {
630 SDL_Event event; 831 SDL_Event event;
631 while (state.event_queue.Pop(event)) { 832 while (state.event_queue.Pop(event)) {
632 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { 833 // Filter out axis events that are below a threshold
834 if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
633 continue; 835 continue;
634 } 836 }
635 // An analog device needs two axes, so we need to store the axis for later and wait for 837 // Simplify controller config by testing if game controller support is enabled.
636 // a second SDL event. The axes also must be from the same joystick. 838 if (event.type == SDL_JOYAXISMOTION) {
637 const int axis = event.jaxis.axis; 839 const auto axis = event.jaxis.axis;
638 if (analog_x_axis == -1) { 840 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
639 analog_x_axis = axis; 841 const auto controller = joystick->GetSDLGameController();
640 analog_axes_joystick = event.jaxis.which; 842 if (controller) {
641 } else if (analog_y_axis == -1 && analog_x_axis != axis && 843 const auto axis_left_x =
642 analog_axes_joystick == event.jaxis.which) { 844 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)
643 analog_y_axis = axis; 845 .value.axis;
846 const auto axis_left_y =
847 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY)
848 .value.axis;
849 const auto axis_right_x =
850 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX)
851 .value.axis;
852 const auto axis_right_y =
853 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY)
854 .value.axis;
855
856 if (axis == axis_left_x || axis == axis_left_y) {
857 analog_x_axis = axis_left_x;
858 analog_y_axis = axis_left_y;
859 break;
860 } else if (axis == axis_right_x || axis == axis_right_y) {
861 analog_x_axis = axis_right_x;
862 analog_y_axis = axis_right_y;
863 break;
864 }
865 }
866 }
867
868 // If the press wasn't accepted as a joy axis, check for a button press
869 auto button_press = button_poller.FromEvent(event);
870 if (button_press) {
871 return *button_press;
644 } 872 }
645 } 873 }
646 Common::ParamPackage params; 874
647 if (analog_x_axis != -1 && analog_y_axis != -1) { 875 if (analog_x_axis != -1 && analog_y_axis != -1) {
648 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 876 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
649 params.Set("engine", "sdl"); 877 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
650 params.Set("port", joystick->GetPort()); 878 analog_x_axis, analog_y_axis);
651 params.Set("guid", joystick->GetGUID());
652 params.Set("axis_x", analog_x_axis);
653 params.Set("axis_y", analog_y_axis);
654 analog_x_axis = -1; 879 analog_x_axis = -1;
655 analog_y_axis = -1; 880 analog_y_axis = -1;
656 analog_axes_joystick = -1;
657 return params; 881 return params;
658 } 882 }
659 return params; 883 return {};
660 } 884 }
661 885
662private: 886private:
663 int analog_x_axis = -1; 887 int analog_x_axis = -1;
664 int analog_y_axis = -1; 888 int analog_y_axis = -1;
665 SDL_JoystickID analog_axes_joystick = -1; 889 SDLButtonPoller button_poller;
666}; 890};
667} // namespace Polling 891} // namespace Polling
668 892
@@ -670,8 +894,8 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
670 Pollers pollers; 894 Pollers pollers;
671 895
672 switch (type) { 896 switch (type) {
673 case InputCommon::Polling::DeviceType::Analog: 897 case InputCommon::Polling::DeviceType::AnalogPreferred:
674 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this)); 898 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this));
675 break; 899 break;
676 case InputCommon::Polling::DeviceType::Button: 900 case InputCommon::Polling::DeviceType::Button:
677 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); 901 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:
50 std::atomic<bool> polling = false; 50 std::atomic<bool> polling = false;
51 Common::SPSCQueue<SDL_Event> event_queue; 51 Common::SPSCQueue<SDL_Event> event_queue;
52 52
53 std::vector<Common::ParamPackage> GetInputDevices() override;
54
55 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
56 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
57
53private: 58private:
54 void InitJoystick(int joystick_index); 59 void InitJoystick(int joystick_index);
55 void CloseJoystick(SDL_Joystick* sdl_joystick); 60 void CloseJoystick(SDL_Joystick* sdl_joystick);
@@ -57,6 +62,9 @@ private:
57 /// Needs to be called before SDL_QuitSubSystem. 62 /// Needs to be called before SDL_QuitSubSystem.
58 void CloseJoysticks(); 63 void CloseJoysticks();
59 64
65 // Set to true if SDL supports game controller subsystem
66 bool has_gamecontroller = false;
67
60 /// Map of GUID of a list of corresponding virtual Joysticks 68 /// Map of GUID of a list of corresponding virtual Joysticks
61 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; 69 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
62 std::mutex joystick_map_mutex; 70 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 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "input_common/settings.h"
6
7namespace Settings {
8namespace NativeButton {
9const std::array<const char*, NumButtons> mapping = {{
10 "button_a", "button_b", "button_x", "button_y", "button_lstick",
11 "button_rstick", "button_l", "button_r", "button_zl", "button_zr",
12 "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
13 "button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot",
14}};
15}
16
17namespace NativeAnalog {
18const std::array<const char*, NumAnalogs> mapping = {{
19 "lstick",
20 "rstick",
21}};
22}
23
24namespace NativeMouseButton {
25const std::array<const char*, NumMouseButtons> mapping = {{
26 "left",
27 "right",
28 "middle",
29 "forward",
30 "back",
31}};
32}
33} // 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 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <string>
9#include "common/common_types.h"
10
11namespace Settings {
12namespace NativeButton {
13enum Values : int {
14 A,
15 B,
16 X,
17 Y,
18 LStick,
19 RStick,
20 L,
21 R,
22 ZL,
23 ZR,
24 Plus,
25 Minus,
26
27 DLeft,
28 DUp,
29 DRight,
30 DDown,
31
32 SL,
33 SR,
34
35 Home,
36 Screenshot,
37
38 NumButtons,
39};
40
41constexpr int BUTTON_HID_BEGIN = A;
42constexpr int BUTTON_NS_BEGIN = Home;
43
44constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
45constexpr int BUTTON_NS_END = NumButtons;
46
47constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
48constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
49
50extern const std::array<const char*, NumButtons> mapping;
51
52} // namespace NativeButton
53
54namespace NativeAnalog {
55enum Values : int {
56 LStick,
57 RStick,
58
59 NumAnalogs,
60};
61
62constexpr int STICK_HID_BEGIN = LStick;
63constexpr int STICK_HID_END = NumAnalogs;
64constexpr int NUM_STICKS_HID = NumAnalogs;
65
66extern const std::array<const char*, NumAnalogs> mapping;
67} // namespace NativeAnalog
68
69namespace NativeMouseButton {
70enum Values {
71 Left,
72 Right,
73 Middle,
74 Forward,
75 Back,
76
77 NumMouseButtons,
78};
79
80constexpr int MOUSE_HID_BEGIN = Left;
81constexpr int MOUSE_HID_END = NumMouseButtons;
82constexpr int NUM_MOUSE_HID = NumMouseButtons;
83
84extern const std::array<const char*, NumMouseButtons> mapping;
85} // namespace NativeMouseButton
86
87namespace NativeKeyboard {
88enum Keys {
89 None,
90 Error,
91
92 A = 4,
93 B,
94 C,
95 D,
96 E,
97 F,
98 G,
99 H,
100 I,
101 J,
102 K,
103 L,
104 M,
105 N,
106 O,
107 P,
108 Q,
109 R,
110 S,
111 T,
112 U,
113 V,
114 W,
115 X,
116 Y,
117 Z,
118 N1,
119 N2,
120 N3,
121 N4,
122 N5,
123 N6,
124 N7,
125 N8,
126 N9,
127 N0,
128 Enter,
129 Escape,
130 Backspace,
131 Tab,
132 Space,
133 Minus,
134 Equal,
135 LeftBrace,
136 RightBrace,
137 Backslash,
138 Tilde,
139 Semicolon,
140 Apostrophe,
141 Grave,
142 Comma,
143 Dot,
144 Slash,
145 CapsLockKey,
146
147 F1,
148 F2,
149 F3,
150 F4,
151 F5,
152 F6,
153 F7,
154 F8,
155 F9,
156 F10,
157 F11,
158 F12,
159
160 SystemRequest,
161 ScrollLockKey,
162 Pause,
163 Insert,
164 Home,
165 PageUp,
166 Delete,
167 End,
168 PageDown,
169 Right,
170 Left,
171 Down,
172 Up,
173
174 NumLockKey,
175 KPSlash,
176 KPAsterisk,
177 KPMinus,
178 KPPlus,
179 KPEnter,
180 KP1,
181 KP2,
182 KP3,
183 KP4,
184 KP5,
185 KP6,
186 KP7,
187 KP8,
188 KP9,
189 KP0,
190 KPDot,
191
192 Key102,
193 Compose,
194 Power,
195 KPEqual,
196
197 F13,
198 F14,
199 F15,
200 F16,
201 F17,
202 F18,
203 F19,
204 F20,
205 F21,
206 F22,
207 F23,
208 F24,
209
210 Open,
211 Help,
212 Properties,
213 Front,
214 Stop,
215 Repeat,
216 Undo,
217 Cut,
218 Copy,
219 Paste,
220 Find,
221 Mute,
222 VolumeUp,
223 VolumeDown,
224 CapsLockActive,
225 NumLockActive,
226 ScrollLockActive,
227 KPComma,
228
229 KPLeftParenthesis,
230 KPRightParenthesis,
231
232 LeftControlKey = 0xE0,
233 LeftShiftKey,
234 LeftAltKey,
235 LeftMetaKey,
236 RightControlKey,
237 RightShiftKey,
238 RightAltKey,
239 RightMetaKey,
240
241 MediaPlayPause,
242 MediaStopCD,
243 MediaPrevious,
244 MediaNext,
245 MediaEject,
246 MediaVolumeUp,
247 MediaVolumeDown,
248 MediaMute,
249 MediaWebsite,
250 MediaBack,
251 MediaForward,
252 MediaStop,
253 MediaFind,
254 MediaScrollUp,
255 MediaScrollDown,
256 MediaEdit,
257 MediaSleep,
258 MediaCoffee,
259 MediaRefresh,
260 MediaCalculator,
261
262 NumKeyboardKeys,
263};
264
265static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
266
267enum Modifiers {
268 LeftControl,
269 LeftShift,
270 LeftAlt,
271 LeftMeta,
272 RightControl,
273 RightShift,
274 RightAlt,
275 RightMeta,
276 CapsLock,
277 ScrollLock,
278 NumLock,
279
280 NumKeyboardMods,
281};
282
283constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
284constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
285constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
286
287constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
288constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
289constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
290
291} // namespace NativeKeyboard
292
293using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
294using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
295using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
296using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
297using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
298
299constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
300constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
301constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
302constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
303
304enum class ControllerType {
305 ProController,
306 DualJoyconDetached,
307 LeftJoycon,
308 RightJoycon,
309 Handheld,
310};
311
312struct PlayerInput {
313 bool connected;
314 ControllerType controller_type;
315 ButtonsRaw buttons;
316 AnalogsRaw analogs;
317 std::string lstick_mod;
318 std::string rstick_mod;
319
320 u32 body_color_left;
321 u32 body_color_right;
322 u32 button_color_left;
323 u32 button_color_right;
324};
325
326struct TouchscreenInput {
327 bool enabled;
328 std::string device;
329
330 u32 finger;
331 u32 diameter_x;
332 u32 diameter_y;
333 u32 rotation_angle;
334};
335} // 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
224 } else { 224 } else {
225 failure_callback(); 225 failure_callback();
226 } 226 }
227 }) 227 }).detach();
228 .detach();
229} 228}
230 229
231CalibrationConfigurationJob::CalibrationConfigurationJob( 230CalibrationConfigurationJob::CalibrationConfigurationJob(
@@ -279,8 +278,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
279 complete_event.Wait(); 278 complete_event.Wait();
280 socket.Stop(); 279 socket.Stop();
281 worker_thread.join(); 280 worker_thread.join();
282 }) 281 }).detach();
283 .detach();
284} 282}
285 283
286CalibrationConfigurationJob::~CalibrationConfigurationJob() { 284CalibrationConfigurationJob::~CalibrationConfigurationJob() {
diff --git a/src/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() {
77 std::make_unique<Client>(status, Settings::values.udp_input_address, 77 std::make_unique<Client>(status, Settings::values.udp_input_address,
78 Settings::values.udp_input_port, Settings::values.udp_pad_index); 78 Settings::values.udp_input_port, Settings::values.udp_pad_index);
79 79
80 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", 80 motion_factory = std::make_shared<UDPMotionFactory>(status);
81 std::make_shared<UDPTouchFactory>(status)); 81 touch_factory = std::make_shared<UDPTouchFactory>(status);
82 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", 82
83 std::make_shared<UDPMotionFactory>(status)); 83 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", motion_factory);
84 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", touch_factory);
84} 85}
85 86
86State::~State() { 87State::~State() {
@@ -88,6 +89,11 @@ State::~State() {
88 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); 89 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
89} 90}
90 91
92std::vector<Common::ParamPackage> State::GetInputDevices() const {
93 // TODO support binding udp devices
94 return {};
95}
96
91void State::ReloadUDPClient() { 97void State::ReloadUDPClient() {
92 client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, 98 client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port,
93 Settings::values.udp_pad_index); 99 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 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <vector>
9#include "common/param_package.h"
8 10
9namespace InputCommon::CemuhookUDP { 11namespace InputCommon::CemuhookUDP {
10 12
11class Client; 13class Client;
14class UDPMotionFactory;
15class UDPTouchFactory;
12 16
13class State { 17class State {
14public: 18public:
15 State(); 19 State();
16 ~State(); 20 ~State();
17 void ReloadUDPClient(); 21 void ReloadUDPClient();
22 std::vector<Common::ParamPackage> GetInputDevices() const;
18 23
19private: 24private:
20 std::unique_ptr<Client> client; 25 std::unique_ptr<Client> client;
26 std::shared_ptr<UDPMotionFactory> motion_factory;
27 std::shared_ptr<UDPTouchFactory> touch_factory;
21}; 28};
22 29
23std::unique_ptr<State> Init(); 30std::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 @@
1add_subdirectory(host_shaders)
2
1add_library(video_core STATIC 3add_library(video_core STATIC
2 buffer_cache/buffer_block.h 4 buffer_cache/buffer_block.h
3 buffer_cache/buffer_cache.h 5 buffer_cache/buffer_cache.h
@@ -244,6 +246,9 @@ create_target_directory_groups(video_core)
244target_link_libraries(video_core PUBLIC common core) 246target_link_libraries(video_core PUBLIC common core)
245target_link_libraries(video_core PRIVATE glad xbyak) 247target_link_libraries(video_core PRIVATE glad xbyak)
246 248
249add_dependencies(video_core host_shaders)
250target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
251
247if (ENABLE_VULKAN) 252if (ENABLE_VULKAN)
248 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) 253 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
249 target_compile_definitions(video_core PRIVATE HAS_VULKAN) 254 target_compile_definitions(video_core PRIVATE HAS_VULKAN)
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index ff10ff40d..6e50661a3 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -10,7 +10,13 @@
10 10
11namespace Tegra::Engines { 11namespace Tegra::Engines {
12 12
13Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {} 13Fermi2D::Fermi2D() = default;
14
15Fermi2D::~Fermi2D() = default;
16
17void Fermi2D::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
18 rasterizer = &rasterizer_;
19}
14 20
15void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 21void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
16 ASSERT_MSG(method < Regs::NUM_REGS, 22 ASSERT_MSG(method < Regs::NUM_REGS,
@@ -87,7 +93,7 @@ void Fermi2D::HandleSurfaceCopy() {
87 copy_config.src_rect = src_rect; 93 copy_config.src_rect = src_rect;
88 copy_config.dst_rect = dst_rect; 94 copy_config.dst_rect = dst_rect;
89 95
90 if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst, copy_config)) { 96 if (!rasterizer->AccelerateSurfaceCopy(regs.src, regs.dst, copy_config)) {
91 UNIMPLEMENTED(); 97 UNIMPLEMENTED();
92 } 98 }
93} 99}
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 8f37d053f..213abfaae 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -34,8 +34,11 @@ namespace Tegra::Engines {
34 34
35class Fermi2D final : public EngineInterface { 35class Fermi2D final : public EngineInterface {
36public: 36public:
37 explicit Fermi2D(VideoCore::RasterizerInterface& rasterizer); 37 explicit Fermi2D();
38 ~Fermi2D() = default; 38 ~Fermi2D();
39
40 /// Binds a rasterizer to this engine.
41 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
39 42
40 /// Write the value to the register identified by method. 43 /// Write the value to the register identified by method.
41 void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; 44 void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
@@ -149,7 +152,7 @@ public:
149 }; 152 };
150 153
151private: 154private:
152 VideoCore::RasterizerInterface& rasterizer; 155 VideoCore::RasterizerInterface* rasterizer;
153 156
154 /// Performs the copy from the source surface to the destination surface as configured in the 157 /// Performs the copy from the source surface to the destination surface as configured in the
155 /// registers. 158 /// registers.
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index a82b06a38..898370739 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -16,14 +16,15 @@
16 16
17namespace Tegra::Engines { 17namespace Tegra::Engines {
18 18
19KeplerCompute::KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 19KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_)
20 MemoryManager& memory_manager) 20 : system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {}
21 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, upload_state{
22 memory_manager,
23 regs.upload} {}
24 21
25KeplerCompute::~KeplerCompute() = default; 22KeplerCompute::~KeplerCompute() = default;
26 23
24void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
25 rasterizer = &rasterizer_;
26}
27
27void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 28void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
28 ASSERT_MSG(method < Regs::NUM_REGS, 29 ASSERT_MSG(method < Regs::NUM_REGS,
29 "Invalid KeplerCompute register, increase the size of the Regs structure"); 30 "Invalid KeplerCompute register, increase the size of the Regs structure");
@@ -104,11 +105,11 @@ SamplerDescriptor KeplerCompute::AccessSampler(u32 handle) const {
104} 105}
105 106
106VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() { 107VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() {
107 return rasterizer.AccessGuestDriverProfile(); 108 return rasterizer->AccessGuestDriverProfile();
108} 109}
109 110
110const VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() const { 111const VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() const {
111 return rasterizer.AccessGuestDriverProfile(); 112 return rasterizer->AccessGuestDriverProfile();
112} 113}
113 114
114void KeplerCompute::ProcessLaunch() { 115void KeplerCompute::ProcessLaunch() {
@@ -119,7 +120,7 @@ void KeplerCompute::ProcessLaunch() {
119 const GPUVAddr code_addr = regs.code_loc.Address() + launch_description.program_start; 120 const GPUVAddr code_addr = regs.code_loc.Address() + launch_description.program_start;
120 LOG_TRACE(HW_GPU, "Compute invocation launched at address 0x{:016x}", code_addr); 121 LOG_TRACE(HW_GPU, "Compute invocation launched at address 0x{:016x}", code_addr);
121 122
122 rasterizer.DispatchCompute(code_addr); 123 rasterizer->DispatchCompute(code_addr);
123} 124}
124 125
125Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const { 126Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const {
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index b7f668d88..7f2500aab 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -42,10 +42,12 @@ namespace Tegra::Engines {
42 42
43class KeplerCompute final : public ConstBufferEngineInterface, public EngineInterface { 43class KeplerCompute final : public ConstBufferEngineInterface, public EngineInterface {
44public: 44public:
45 explicit KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 45 explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager);
46 MemoryManager& memory_manager);
47 ~KeplerCompute(); 46 ~KeplerCompute();
48 47
48 /// Binds a rasterizer to this engine.
49 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
50
49 static constexpr std::size_t NumConstBuffers = 8; 51 static constexpr std::size_t NumConstBuffers = 8;
50 52
51 struct Regs { 53 struct Regs {
@@ -230,11 +232,6 @@ public:
230 const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override; 232 const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override;
231 233
232private: 234private:
233 Core::System& system;
234 VideoCore::RasterizerInterface& rasterizer;
235 MemoryManager& memory_manager;
236 Upload::State upload_state;
237
238 void ProcessLaunch(); 235 void ProcessLaunch();
239 236
240 /// Retrieves information about a specific TIC entry from the TIC buffer. 237 /// Retrieves information about a specific TIC entry from the TIC buffer.
@@ -242,6 +239,11 @@ private:
242 239
243 /// Retrieves information about a specific TSC entry from the TSC buffer. 240 /// Retrieves information about a specific TSC entry from the TSC buffer.
244 Texture::TSCEntry GetTSCEntry(u32 tsc_index) const; 241 Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
242
243 Core::System& system;
244 MemoryManager& memory_manager;
245 VideoCore::RasterizerInterface* rasterizer = nullptr;
246 Upload::State upload_state;
245}; 247};
246 248
247#define ASSERT_REG_POSITION(field_name, position) \ 249#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index c01436295..33854445f 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -22,14 +22,19 @@ using VideoCore::QueryType;
22/// First register id that is actually a Macro call. 22/// First register id that is actually a Macro call.
23constexpr u32 MacroRegistersStart = 0xE00; 23constexpr u32 MacroRegistersStart = 0xE00;
24 24
25Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 25Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
26 MemoryManager& memory_manager) 26 : system{system_}, memory_manager{memory_manager_}, macro_engine{GetMacroEngine(*this)},
27 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, 27 upload_state{memory_manager, regs.upload} {
28 macro_engine{GetMacroEngine(*this)}, upload_state{memory_manager, regs.upload} {
29 dirty.flags.flip(); 28 dirty.flags.flip();
30 InitializeRegisterDefaults(); 29 InitializeRegisterDefaults();
31} 30}
32 31
32Maxwell3D::~Maxwell3D() = default;
33
34void Maxwell3D::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
35 rasterizer = &rasterizer_;
36}
37
33void Maxwell3D::InitializeRegisterDefaults() { 38void Maxwell3D::InitializeRegisterDefaults() {
34 // Initializes registers to their default values - what games expect them to be at boot. This is 39 // Initializes registers to their default values - what games expect them to be at boot. This is
35 // for certain registers that may not be explicitly set by games. 40 // for certain registers that may not be explicitly set by games.
@@ -192,7 +197,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
192 197
193 switch (method) { 198 switch (method) {
194 case MAXWELL3D_REG_INDEX(wait_for_idle): { 199 case MAXWELL3D_REG_INDEX(wait_for_idle): {
195 rasterizer.WaitForIdle(); 200 rasterizer->WaitForIdle();
196 break; 201 break;
197 } 202 }
198 case MAXWELL3D_REG_INDEX(shadow_ram_control): { 203 case MAXWELL3D_REG_INDEX(shadow_ram_control): {
@@ -402,7 +407,7 @@ void Maxwell3D::FlushMMEInlineDraw() {
402 407
403 const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed; 408 const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
404 if (ShouldExecute()) { 409 if (ShouldExecute()) {
405 rasterizer.Draw(is_indexed, true); 410 rasterizer->Draw(is_indexed, true);
406 } 411 }
407 412
408 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if 413 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
@@ -465,7 +470,7 @@ void Maxwell3D::ProcessQueryGet() {
465 switch (regs.query.query_get.operation) { 470 switch (regs.query.query_get.operation) {
466 case Regs::QueryOperation::Release: 471 case Regs::QueryOperation::Release:
467 if (regs.query.query_get.fence == 1) { 472 if (regs.query.query_get.fence == 1) {
468 rasterizer.SignalSemaphore(regs.query.QueryAddress(), regs.query.query_sequence); 473 rasterizer->SignalSemaphore(regs.query.QueryAddress(), regs.query.query_sequence);
469 } else { 474 } else {
470 StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0); 475 StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0);
471 } 476 }
@@ -533,7 +538,7 @@ void Maxwell3D::ProcessQueryCondition() {
533void Maxwell3D::ProcessCounterReset() { 538void Maxwell3D::ProcessCounterReset() {
534 switch (regs.counter_reset) { 539 switch (regs.counter_reset) {
535 case Regs::CounterReset::SampleCnt: 540 case Regs::CounterReset::SampleCnt:
536 rasterizer.ResetCounter(QueryType::SamplesPassed); 541 rasterizer->ResetCounter(QueryType::SamplesPassed);
537 break; 542 break;
538 default: 543 default:
539 LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", 544 LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}",
@@ -547,7 +552,7 @@ void Maxwell3D::ProcessSyncPoint() {
547 const u32 increment = regs.sync_info.increment.Value(); 552 const u32 increment = regs.sync_info.increment.Value();
548 [[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value(); 553 [[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value();
549 if (increment) { 554 if (increment) {
550 rasterizer.SignalSyncPoint(sync_point); 555 rasterizer->SignalSyncPoint(sync_point);
551 } 556 }
552} 557}
553 558
@@ -570,7 +575,7 @@ void Maxwell3D::DrawArrays() {
570 575
571 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; 576 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
572 if (ShouldExecute()) { 577 if (ShouldExecute()) {
573 rasterizer.Draw(is_indexed, false); 578 rasterizer->Draw(is_indexed, false);
574 } 579 }
575 580
576 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if 581 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
@@ -590,8 +595,8 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
590 return 0; 595 return 0;
591 case Regs::QuerySelect::SamplesPassed: 596 case Regs::QuerySelect::SamplesPassed:
592 // Deferred. 597 // Deferred.
593 rasterizer.Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed, 598 rasterizer->Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed,
594 system.GPU().GetTicks()); 599 system.GPU().GetTicks());
595 return {}; 600 return {};
596 default: 601 default:
597 LOG_DEBUG(HW_GPU, "Unimplemented query select type {}", 602 LOG_DEBUG(HW_GPU, "Unimplemented query select type {}",
@@ -718,7 +723,7 @@ void Maxwell3D::ProcessClearBuffers() {
718 regs.clear_buffers.R == regs.clear_buffers.B && 723 regs.clear_buffers.R == regs.clear_buffers.B &&
719 regs.clear_buffers.R == regs.clear_buffers.A); 724 regs.clear_buffers.R == regs.clear_buffers.A);
720 725
721 rasterizer.Clear(); 726 rasterizer->Clear();
722} 727}
723 728
724u32 Maxwell3D::AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const { 729u32 Maxwell3D::AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const {
@@ -752,11 +757,11 @@ SamplerDescriptor Maxwell3D::AccessSampler(u32 handle) const {
752} 757}
753 758
754VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() { 759VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() {
755 return rasterizer.AccessGuestDriverProfile(); 760 return rasterizer->AccessGuestDriverProfile();
756} 761}
757 762
758const VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() const { 763const VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() const {
759 return rasterizer.AccessGuestDriverProfile(); 764 return rasterizer->AccessGuestDriverProfile();
760} 765}
761 766
762} // namespace Tegra::Engines 767} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index ef1618990..bc289c55d 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -51,9 +51,11 @@ namespace Tegra::Engines {
51 51
52class Maxwell3D final : public ConstBufferEngineInterface, public EngineInterface { 52class Maxwell3D final : public ConstBufferEngineInterface, public EngineInterface {
53public: 53public:
54 explicit Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 54 explicit Maxwell3D(Core::System& system, MemoryManager& memory_manager);
55 MemoryManager& memory_manager); 55 ~Maxwell3D();
56 ~Maxwell3D() = default; 56
57 /// Binds a rasterizer to this engine.
58 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
57 59
58 /// Register structure of the Maxwell3D engine. 60 /// Register structure of the Maxwell3D engine.
59 /// TODO(Subv): This structure will need to be made bigger as more registers are discovered. 61 /// TODO(Subv): This structure will need to be made bigger as more registers are discovered.
@@ -647,7 +649,7 @@ public:
647 GetX() + GetWidth(), // right 649 GetX() + GetWidth(), // right
648 GetY() // bottom 650 GetY() // bottom
649 }; 651 };
650 }; 652 }
651 653
652 f32 GetX() const { 654 f32 GetX() const {
653 return std::max(0.0f, translate_x - std::fabs(scale_x)); 655 return std::max(0.0f, translate_x - std::fabs(scale_x));
@@ -1418,12 +1420,12 @@ public:
1418 return execute_on; 1420 return execute_on;
1419 } 1421 }
1420 1422
1421 VideoCore::RasterizerInterface& GetRasterizer() { 1423 VideoCore::RasterizerInterface& Rasterizer() {
1422 return rasterizer; 1424 return *rasterizer;
1423 } 1425 }
1424 1426
1425 const VideoCore::RasterizerInterface& GetRasterizer() const { 1427 const VideoCore::RasterizerInterface& Rasterizer() const {
1426 return rasterizer; 1428 return *rasterizer;
1427 } 1429 }
1428 1430
1429 /// Notify a memory write has happened. 1431 /// Notify a memory write has happened.
@@ -1460,11 +1462,10 @@ private:
1460 void InitializeRegisterDefaults(); 1462 void InitializeRegisterDefaults();
1461 1463
1462 Core::System& system; 1464 Core::System& system;
1463
1464 VideoCore::RasterizerInterface& rasterizer;
1465
1466 MemoryManager& memory_manager; 1465 MemoryManager& memory_manager;
1467 1466
1467 VideoCore::RasterizerInterface* rasterizer = nullptr;
1468
1468 /// Start offsets of each macro in macro_memory 1469 /// Start offsets of each macro in macro_memory
1469 std::array<u32, 0x80> macro_positions = {}; 1470 std::array<u32, 0x80> macro_positions = {};
1470 1471
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index a2d3d7823..e88290754 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -94,7 +94,8 @@ void MaxwellDMA::CopyPitchToPitch() {
94} 94}
95 95
96void MaxwellDMA::CopyBlockLinearToPitch() { 96void MaxwellDMA::CopyBlockLinearToPitch() {
97 ASSERT(regs.src_params.block_size.depth == 0); 97 UNIMPLEMENTED_IF(regs.src_params.block_size.depth != 0);
98 UNIMPLEMENTED_IF(regs.src_params.layer != 0);
98 99
99 // Optimized path for micro copies. 100 // Optimized path for micro copies.
100 const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count; 101 const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
@@ -123,17 +124,12 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
123 write_buffer.resize(dst_size); 124 write_buffer.resize(dst_size);
124 } 125 }
125 126
126 if (Settings::IsGPULevelExtreme()) { 127 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
127 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); 128 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
128 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
129 } else {
130 memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
131 memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
132 }
133 129
134 UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel, 130 UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel,
135 read_buffer.data() + src_layer_size * src_params.layer, write_buffer.data(), 131 block_height, src_params.origin.x, src_params.origin.y, write_buffer.data(),
136 block_height, src_params.origin.x, src_params.origin.y); 132 read_buffer.data());
137 133
138 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 134 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
139} 135}
@@ -198,7 +194,6 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
198 if (read_buffer.size() < src_size) { 194 if (read_buffer.size() < src_size) {
199 read_buffer.resize(src_size); 195 read_buffer.resize(src_size);
200 } 196 }
201
202 if (write_buffer.size() < dst_size) { 197 if (write_buffer.size() < dst_size) {
203 write_buffer.resize(dst_size); 198 write_buffer.resize(dst_size);
204 } 199 }
@@ -212,8 +207,8 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
212 } 207 }
213 208
214 UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width, 209 UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width,
215 bytes_per_pixel, read_buffer.data(), write_buffer.data(), 210 bytes_per_pixel, regs.src_params.block_size.height, pos_x, pos_y,
216 regs.src_params.block_size.height, pos_x, pos_y); 211 write_buffer.data(), read_buffer.data());
217 212
218 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 213 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
219} 214}
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index 8b2a6a42c..06cc12d5a 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -5,15 +5,10 @@
5#pragma once 5#pragma once
6 6
7#include <algorithm> 7#include <algorithm>
8#include <array>
9#include <memory>
10#include <queue> 8#include <queue>
11 9
12#include "common/assert.h"
13#include "common/common_types.h" 10#include "common/common_types.h"
14#include "core/core.h" 11#include "core/core.h"
15#include "core/memory.h"
16#include "core/settings.h"
17#include "video_core/gpu.h" 12#include "video_core/gpu.h"
18#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
19#include "video_core/rasterizer_interface.h" 14#include "video_core/rasterizer_interface.h"
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 512578c8b..acb6e6d46 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -27,21 +27,28 @@ namespace Tegra {
27 27
28MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); 28MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
29 29
30GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, bool is_async) 30GPU::GPU(Core::System& system_, bool is_async_)
31 : system{system}, renderer{std::move(renderer_)}, is_async{is_async} { 31 : system{system_}, dma_pusher{std::make_unique<Tegra::DmaPusher>(system, *this)},
32 auto& rasterizer{renderer->Rasterizer()}; 32 memory_manager{std::make_unique<Tegra::MemoryManager>(system)},
33 memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer); 33 maxwell_3d{std::make_unique<Engines::Maxwell3D>(system, *memory_manager)},
34 dma_pusher = std::make_unique<Tegra::DmaPusher>(system, *this); 34 fermi_2d{std::make_unique<Engines::Fermi2D>()},
35 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); 35 kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)},
36 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer); 36 maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)},
37 kepler_compute = std::make_unique<Engines::KeplerCompute>(system, rasterizer, *memory_manager); 37 kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)},
38 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, *memory_manager); 38 shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_} {}
39 kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager);
40 shader_notify = std::make_unique<VideoCore::ShaderNotify>();
41}
42 39
43GPU::~GPU() = default; 40GPU::~GPU() = default;
44 41
42void GPU::BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) {
43 renderer = std::move(renderer_);
44
45 VideoCore::RasterizerInterface& rasterizer = renderer->Rasterizer();
46 memory_manager->BindRasterizer(rasterizer);
47 maxwell_3d->BindRasterizer(rasterizer);
48 fermi_2d->BindRasterizer(rasterizer);
49 kepler_compute->BindRasterizer(rasterizer);
50}
51
45Engines::Maxwell3D& GPU::Maxwell3D() { 52Engines::Maxwell3D& GPU::Maxwell3D() {
46 return *maxwell_3d; 53 return *maxwell_3d;
47} 54}
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index ebfc7b0c7..c7d11deb2 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -142,11 +142,6 @@ class MemoryManager;
142 142
143class GPU { 143class GPU {
144public: 144public:
145 explicit GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
146 bool is_async);
147
148 virtual ~GPU();
149
150 struct MethodCall { 145 struct MethodCall {
151 u32 method{}; 146 u32 method{};
152 u32 argument{}; 147 u32 argument{};
@@ -162,6 +157,12 @@ public:
162 method_count(method_count) {} 157 method_count(method_count) {}
163 }; 158 };
164 159
160 explicit GPU(Core::System& system, bool is_async);
161 virtual ~GPU();
162
163 /// Binds a renderer to the GPU.
164 void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer);
165
165 /// Calls a GPU method. 166 /// Calls a GPU method.
166 void CallMethod(const MethodCall& method_call); 167 void CallMethod(const MethodCall& method_call);
167 168
@@ -345,8 +346,8 @@ private:
345 bool ExecuteMethodOnEngine(u32 method); 346 bool ExecuteMethodOnEngine(u32 method);
346 347
347protected: 348protected:
348 std::unique_ptr<Tegra::DmaPusher> dma_pusher;
349 Core::System& system; 349 Core::System& system;
350 std::unique_ptr<Tegra::DmaPusher> dma_pusher;
350 std::unique_ptr<VideoCore::RendererBase> renderer; 351 std::unique_ptr<VideoCore::RendererBase> renderer;
351 352
352private: 353private:
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index 7b855f63e..70a3d5738 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -10,16 +10,14 @@
10 10
11namespace VideoCommon { 11namespace VideoCommon {
12 12
13GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, 13GPUAsynch::GPUAsynch(Core::System& system) : GPU{system, true}, gpu_thread{system} {}
14 std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
15 : GPU(system, std::move(renderer_), true), gpu_thread{system},
16 cpu_context(renderer->GetRenderWindow().CreateSharedContext()),
17 gpu_context(std::move(context)) {}
18 14
19GPUAsynch::~GPUAsynch() = default; 15GPUAsynch::~GPUAsynch() = default;
20 16
21void GPUAsynch::Start() { 17void GPUAsynch::Start() {
22 gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher); 18 gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher);
19 cpu_context = renderer->GetRenderWindow().CreateSharedContext();
20 cpu_context->MakeCurrent();
23} 21}
24 22
25void GPUAsynch::ObtainContext() { 23void GPUAsynch::ObtainContext() {
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index 15e9f1d38..f89c855a5 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -20,8 +20,7 @@ namespace VideoCommon {
20/// Implementation of GPU interface that runs the GPU asynchronously 20/// Implementation of GPU interface that runs the GPU asynchronously
21class GPUAsynch final : public Tegra::GPU { 21class GPUAsynch final : public Tegra::GPU {
22public: 22public:
23 explicit GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, 23 explicit GPUAsynch(Core::System& system);
24 std::unique_ptr<Core::Frontend::GraphicsContext>&& context);
25 ~GPUAsynch() override; 24 ~GPUAsynch() override;
26 25
27 void Start() override; 26 void Start() override;
@@ -42,7 +41,6 @@ protected:
42private: 41private:
43 GPUThread::ThreadManager gpu_thread; 42 GPUThread::ThreadManager gpu_thread;
44 std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context; 43 std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
45 std::unique_ptr<Core::Frontend::GraphicsContext> gpu_context;
46}; 44};
47 45
48} // namespace VideoCommon 46} // namespace VideoCommon
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
index aaeb9811d..1ca47ddef 100644
--- a/src/video_core/gpu_synch.cpp
+++ b/src/video_core/gpu_synch.cpp
@@ -7,20 +7,18 @@
7 7
8namespace VideoCommon { 8namespace VideoCommon {
9 9
10GPUSynch::GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, 10GPUSynch::GPUSynch(Core::System& system) : GPU{system, false} {}
11 std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
12 : GPU(system, std::move(renderer), false), context{std::move(context)} {}
13 11
14GPUSynch::~GPUSynch() = default; 12GPUSynch::~GPUSynch() = default;
15 13
16void GPUSynch::Start() {} 14void GPUSynch::Start() {}
17 15
18void GPUSynch::ObtainContext() { 16void GPUSynch::ObtainContext() {
19 context->MakeCurrent(); 17 renderer->Context().MakeCurrent();
20} 18}
21 19
22void GPUSynch::ReleaseContext() { 20void GPUSynch::ReleaseContext() {
23 context->DoneCurrent(); 21 renderer->Context().DoneCurrent();
24} 22}
25 23
26void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { 24void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 762c20aa5..297258cb1 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -19,8 +19,7 @@ namespace VideoCommon {
19/// Implementation of GPU interface that runs the GPU synchronously 19/// Implementation of GPU interface that runs the GPU synchronously
20class GPUSynch final : public Tegra::GPU { 20class GPUSynch final : public Tegra::GPU {
21public: 21public:
22 explicit GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, 22 explicit GPUSynch(Core::System& system);
23 std::unique_ptr<Core::Frontend::GraphicsContext>&& context);
24 ~GPUSynch() override; 23 ~GPUSynch() override;
25 24
26 void Start() override; 25 void Start() override;
@@ -36,9 +35,6 @@ public:
36protected: 35protected:
37 void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id, 36 void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,
38 [[maybe_unused]] u32 value) const override {} 37 [[maybe_unused]] u32 value) const override {}
39
40private:
41 std::unique_ptr<Core::Frontend::GraphicsContext> context;
42}; 38};
43 39
44} // namespace VideoCommon 40} // namespace VideoCommon
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
new file mode 100644
index 000000000..aa62363a7
--- /dev/null
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -0,0 +1,43 @@
1set(SHADER_FILES
2 opengl_present.frag
3 opengl_present.vert
4)
5
6set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include)
7set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
8
9set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders)
10add_custom_command(
11 OUTPUT
12 ${SHADER_DIR}
13 COMMAND
14 ${CMAKE_COMMAND} -E make_directory ${SHADER_DIR}
15)
16
17set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in)
18set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake)
19
20foreach(FILENAME IN ITEMS ${SHADER_FILES})
21 string(REPLACE "." "_" SHADER_NAME ${FILENAME})
22 set(SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME})
23 set(HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}.h)
24 add_custom_command(
25 OUTPUT
26 ${HEADER_FILE}
27 COMMAND
28 ${CMAKE_COMMAND} -P ${HEADER_GENERATOR} ${SOURCE_FILE} ${HEADER_FILE} ${INPUT_FILE}
29 MAIN_DEPENDENCY
30 ${SOURCE_FILE}
31 DEPENDS
32 ${HEADER_GENERATOR}
33 ${INPUT_FILE}
34 )
35 set(SHADER_HEADERS ${SHADER_HEADERS} ${HEADER_FILE})
36endforeach()
37
38add_custom_target(host_shaders
39 DEPENDS
40 ${SHADER_HEADERS}
41 SOURCES
42 ${SHADER_FILES}
43)
diff --git a/src/video_core/host_shaders/StringShaderHeader.cmake b/src/video_core/host_shaders/StringShaderHeader.cmake
new file mode 100644
index 000000000..368bce0ed
--- /dev/null
+++ b/src/video_core/host_shaders/StringShaderHeader.cmake
@@ -0,0 +1,11 @@
1set(SOURCE_FILE ${CMAKE_ARGV3})
2set(HEADER_FILE ${CMAKE_ARGV4})
3set(INPUT_FILE ${CMAKE_ARGV5})
4
5get_filename_component(CONTENTS_NAME ${SOURCE_FILE} NAME)
6string(REPLACE "." "_" CONTENTS_NAME ${CONTENTS_NAME})
7string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME)
8
9file(READ ${SOURCE_FILE} CONTENTS)
10
11configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY)
diff --git a/src/video_core/host_shaders/opengl_present.frag b/src/video_core/host_shaders/opengl_present.frag
new file mode 100644
index 000000000..8a4cb024b
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_present.frag
@@ -0,0 +1,10 @@
1#version 430 core
2
3layout (location = 0) in vec2 frag_tex_coord;
4layout (location = 0) out vec4 color;
5
6layout (binding = 0) uniform sampler2D color_texture;
7
8void main() {
9 color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
10}
diff --git a/src/video_core/host_shaders/opengl_present.vert b/src/video_core/host_shaders/opengl_present.vert
new file mode 100644
index 000000000..2235d31a4
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_present.vert
@@ -0,0 +1,24 @@
1#version 430 core
2
3out gl_PerVertex {
4 vec4 gl_Position;
5};
6
7layout (location = 0) in vec2 vert_position;
8layout (location = 1) in vec2 vert_tex_coord;
9layout (location = 0) out vec2 frag_tex_coord;
10
11// This is a truncated 3x3 matrix for 2D transformations:
12// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
13// The third column performs translation.
14// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
15// implicitly be [0, 0, 1]
16layout (location = 0) uniform mat3x2 modelview_matrix;
17
18void main() {
19 // Multiply input position by the rotscale part of the matrix and then manually translate by
20 // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
21 // to `vec3(vert_position.xy, 1.0)`
22 gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
23 frag_tex_coord = vert_tex_coord;
24}
diff --git a/src/video_core/host_shaders/source_shader.h.in b/src/video_core/host_shaders/source_shader.h.in
new file mode 100644
index 000000000..ccdb0d2a9
--- /dev/null
+++ b/src/video_core/host_shaders/source_shader.h.in
@@ -0,0 +1,9 @@
1#pragma once
2
3#include <string_view>
4
5namespace HostShaders {
6
7constexpr std::string_view @CONTENTS_NAME@ = R"(@CONTENTS@)";
8
9} // namespace HostShaders
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 0c9ff59a4..df00b57df 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -24,7 +24,7 @@ void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
24 maxwell3d.regs.index_array.first = parameters[4]; 24 maxwell3d.regs.index_array.first = parameters[4];
25 25
26 if (maxwell3d.ShouldExecute()) { 26 if (maxwell3d.ShouldExecute()) {
27 maxwell3d.GetRasterizer().Draw(true, true); 27 maxwell3d.Rasterizer().Draw(true, true);
28 } 28 }
29 maxwell3d.regs.index_array.count = 0; 29 maxwell3d.regs.index_array.count = 0;
30 maxwell3d.mme_draw.instance_count = 0; 30 maxwell3d.mme_draw.instance_count = 0;
@@ -42,7 +42,7 @@ void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
42 maxwell3d.mme_draw.instance_count = count; 42 maxwell3d.mme_draw.instance_count = count;
43 43
44 if (maxwell3d.ShouldExecute()) { 44 if (maxwell3d.ShouldExecute()) {
45 maxwell3d.GetRasterizer().Draw(false, true); 45 maxwell3d.Rasterizer().Draw(false, true);
46 } 46 }
47 maxwell3d.regs.vertex_buffer.count = 0; 47 maxwell3d.regs.vertex_buffer.count = 0;
48 maxwell3d.mme_draw.instance_count = 0; 48 maxwell3d.mme_draw.instance_count = 0;
@@ -65,7 +65,7 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
65 maxwell3d.regs.draw.topology.Assign( 65 maxwell3d.regs.draw.topology.Assign(
66 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); 66 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
67 if (maxwell3d.ShouldExecute()) { 67 if (maxwell3d.ShouldExecute()) {
68 maxwell3d.GetRasterizer().Draw(true, true); 68 maxwell3d.Rasterizer().Draw(true, true);
69 } 69 }
70 maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base? 70 maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base?
71 maxwell3d.regs.index_array.count = 0; 71 maxwell3d.regs.index_array.count = 0;
diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp
index aa5256419..bd01fd1f2 100644
--- a/src/video_core/macro/macro_interpreter.cpp
+++ b/src/video_core/macro/macro_interpreter.cpp
@@ -34,7 +34,6 @@ void MacroInterpreterImpl::Execute(const std::vector<u32>& parameters, u32 metho
34 this->parameters = std::make_unique<u32[]>(num_parameters); 34 this->parameters = std::make_unique<u32[]>(num_parameters);
35 } 35 }
36 std::memcpy(this->parameters.get(), parameters.data(), num_parameters * sizeof(u32)); 36 std::memcpy(this->parameters.get(), parameters.data(), num_parameters * sizeof(u32));
37 this->num_parameters = num_parameters;
38 37
39 // Execute the code until we hit an exit condition. 38 // Execute the code until we hit an exit condition.
40 bool keep_executing = true; 39 bool keep_executing = true;
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 844164645..16b2aaa27 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -14,11 +14,15 @@
14 14
15namespace Tegra { 15namespace Tegra {
16 16
17MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer) 17MemoryManager::MemoryManager(Core::System& system_)
18 : system{system}, rasterizer{rasterizer}, page_table(page_table_size) {} 18 : system{system_}, page_table(page_table_size) {}
19 19
20MemoryManager::~MemoryManager() = default; 20MemoryManager::~MemoryManager() = default;
21 21
22void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
23 rasterizer = &rasterizer_;
24}
25
22GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) { 26GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
23 u64 remaining_size{size}; 27 u64 remaining_size{size};
24 for (u64 offset{}; offset < size; offset += page_size) { 28 for (u64 offset{}; offset < size; offset += page_size) {
@@ -217,7 +221,7 @@ void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::siz
217 221
218 // Flush must happen on the rasterizer interface, such that memory is always synchronous 222 // Flush must happen on the rasterizer interface, such that memory is always synchronous
219 // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu. 223 // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
220 rasterizer.FlushRegion(src_addr, copy_amount); 224 rasterizer->FlushRegion(src_addr, copy_amount);
221 system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount); 225 system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
222 } 226 }
223 227
@@ -266,7 +270,7 @@ void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, s
266 270
267 // Invalidate must happen on the rasterizer interface, such that memory is always 271 // Invalidate must happen on the rasterizer interface, such that memory is always
268 // synchronous when it is written (even when in asynchronous GPU mode). 272 // synchronous when it is written (even when in asynchronous GPU mode).
269 rasterizer.InvalidateRegion(dest_addr, copy_amount); 273 rasterizer->InvalidateRegion(dest_addr, copy_amount);
270 system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount); 274 system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
271 } 275 }
272 276
@@ -312,10 +316,10 @@ void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_add
312 WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size); 316 WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size);
313} 317}
314 318
315bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) { 319bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
316 const auto cpu_addr{GpuToCpuAddress(gpu_addr)}; 320 const auto cpu_addr{GpuToCpuAddress(gpu_addr)};
317 if (!cpu_addr) { 321 if (!cpu_addr) {
318 return {}; 322 return false;
319 } 323 }
320 const std::size_t page{(*cpu_addr & Core::Memory::PAGE_MASK) + size}; 324 const std::size_t page{(*cpu_addr & Core::Memory::PAGE_MASK) + size};
321 return page <= Core::Memory::PAGE_SIZE; 325 return page <= Core::Memory::PAGE_SIZE;
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 681bd9588..53c8d122a 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -31,19 +31,19 @@ public:
31 constexpr PageEntry(State state) : state{state} {} 31 constexpr PageEntry(State state) : state{state} {}
32 constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {} 32 constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {}
33 33
34 constexpr bool IsUnmapped() const { 34 [[nodiscard]] constexpr bool IsUnmapped() const {
35 return state == State::Unmapped; 35 return state == State::Unmapped;
36 } 36 }
37 37
38 constexpr bool IsAllocated() const { 38 [[nodiscard]] constexpr bool IsAllocated() const {
39 return state == State::Allocated; 39 return state == State::Allocated;
40 } 40 }
41 41
42 constexpr bool IsValid() const { 42 [[nodiscard]] constexpr bool IsValid() const {
43 return !IsUnmapped() && !IsAllocated(); 43 return !IsUnmapped() && !IsAllocated();
44 } 44 }
45 45
46 constexpr VAddr ToAddress() const { 46 [[nodiscard]] constexpr VAddr ToAddress() const {
47 if (!IsValid()) { 47 if (!IsValid()) {
48 return {}; 48 return {};
49 } 49 }
@@ -51,7 +51,7 @@ public:
51 return static_cast<VAddr>(state) << ShiftBits; 51 return static_cast<VAddr>(state) << ShiftBits;
52 } 52 }
53 53
54 constexpr PageEntry operator+(u64 offset) { 54 [[nodiscard]] constexpr PageEntry operator+(u64 offset) const {
55 // If this is a reserved value, offsets do not apply 55 // If this is a reserved value, offsets do not apply
56 if (!IsValid()) { 56 if (!IsValid()) {
57 return *this; 57 return *this;
@@ -68,19 +68,22 @@ static_assert(sizeof(PageEntry) == 4, "PageEntry is too large");
68 68
69class MemoryManager final { 69class MemoryManager final {
70public: 70public:
71 explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer); 71 explicit MemoryManager(Core::System& system);
72 ~MemoryManager(); 72 ~MemoryManager();
73 73
74 std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const; 74 /// Binds a renderer to the memory manager.
75 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
76
77 [[nodiscard]] std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
75 78
76 template <typename T> 79 template <typename T>
77 T Read(GPUVAddr addr) const; 80 [[nodiscard]] T Read(GPUVAddr addr) const;
78 81
79 template <typename T> 82 template <typename T>
80 void Write(GPUVAddr addr, T data); 83 void Write(GPUVAddr addr, T data);
81 84
82 u8* GetPointer(GPUVAddr addr); 85 [[nodiscard]] u8* GetPointer(GPUVAddr addr);
83 const u8* GetPointer(GPUVAddr addr) const; 86 [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const;
84 87
85 /** 88 /**
86 * ReadBlock and WriteBlock are full read and write operations over virtual 89 * ReadBlock and WriteBlock are full read and write operations over virtual
@@ -109,24 +112,24 @@ public:
109 /** 112 /**
110 * IsGranularRange checks if a gpu region can be simply read with a pointer. 113 * IsGranularRange checks if a gpu region can be simply read with a pointer.
111 */ 114 */
112 bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size); 115 [[nodiscard]] bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const;
113 116
114 GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size); 117 [[nodiscard]] GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size);
115 GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align); 118 [[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align);
116 std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size); 119 [[nodiscard]] std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size);
117 GPUVAddr Allocate(std::size_t size, std::size_t align); 120 [[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align);
118 void Unmap(GPUVAddr gpu_addr, std::size_t size); 121 void Unmap(GPUVAddr gpu_addr, std::size_t size);
119 122
120private: 123private:
121 PageEntry GetPageEntry(GPUVAddr gpu_addr) const; 124 [[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
122 void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size); 125 void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size);
123 GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size); 126 GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size);
124 std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const; 127 [[nodiscard]] std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const;
125 128
126 void TryLockPage(PageEntry page_entry, std::size_t size); 129 void TryLockPage(PageEntry page_entry, std::size_t size);
127 void TryUnlockPage(PageEntry page_entry, std::size_t size); 130 void TryUnlockPage(PageEntry page_entry, std::size_t size);
128 131
129 static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) { 132 [[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
130 return (gpu_addr >> page_bits) & page_table_mask; 133 return (gpu_addr >> page_bits) & page_table_mask;
131 } 134 }
132 135
@@ -141,7 +144,7 @@ private:
141 144
142 Core::System& system; 145 Core::System& system;
143 146
144 VideoCore::RasterizerInterface& rasterizer; 147 VideoCore::RasterizerInterface* rasterizer = nullptr;
145 148
146 std::vector<PageEntry> page_table; 149 std::vector<PageEntry> page_table;
147}; 150};
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index dfb06e87e..a93a1732c 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -9,7 +9,9 @@
9 9
10namespace VideoCore { 10namespace VideoCore {
11 11
12RendererBase::RendererBase(Core::Frontend::EmuWindow& window) : render_window{window} { 12RendererBase::RendererBase(Core::Frontend::EmuWindow& window_,
13 std::unique_ptr<Core::Frontend::GraphicsContext> context_)
14 : render_window{window_}, context{std::move(context_)} {
13 RefreshBaseSettings(); 15 RefreshBaseSettings();
14} 16}
15 17
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 1d85219b6..649074acd 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -15,7 +15,8 @@
15 15
16namespace Core::Frontend { 16namespace Core::Frontend {
17class EmuWindow; 17class EmuWindow;
18} 18class GraphicsContext;
19} // namespace Core::Frontend
19 20
20namespace VideoCore { 21namespace VideoCore {
21 22
@@ -25,14 +26,15 @@ struct RendererSettings {
25 26
26 // Screenshot 27 // Screenshot
27 std::atomic<bool> screenshot_requested{false}; 28 std::atomic<bool> screenshot_requested{false};
28 void* screenshot_bits; 29 void* screenshot_bits{};
29 std::function<void()> screenshot_complete_callback; 30 std::function<void()> screenshot_complete_callback;
30 Layout::FramebufferLayout screenshot_framebuffer_layout; 31 Layout::FramebufferLayout screenshot_framebuffer_layout;
31}; 32};
32 33
33class RendererBase : NonCopyable { 34class RendererBase : NonCopyable {
34public: 35public:
35 explicit RendererBase(Core::Frontend::EmuWindow& window); 36 explicit RendererBase(Core::Frontend::EmuWindow& window,
37 std::unique_ptr<Core::Frontend::GraphicsContext> context);
36 virtual ~RendererBase(); 38 virtual ~RendererBase();
37 39
38 /// Initialize the renderer 40 /// Initialize the renderer
@@ -68,6 +70,14 @@ public:
68 return *rasterizer; 70 return *rasterizer;
69 } 71 }
70 72
73 Core::Frontend::GraphicsContext& Context() {
74 return *context;
75 }
76
77 const Core::Frontend::GraphicsContext& Context() const {
78 return *context;
79 }
80
71 Core::Frontend::EmuWindow& GetRenderWindow() { 81 Core::Frontend::EmuWindow& GetRenderWindow() {
72 return render_window; 82 return render_window;
73 } 83 }
@@ -94,6 +104,7 @@ public:
94protected: 104protected:
95 Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle. 105 Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
96 std::unique_ptr<RasterizerInterface> rasterizer; 106 std::unique_ptr<RasterizerInterface> rasterizer;
107 std::unique_ptr<Core::Frontend::GraphicsContext> context;
97 f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer 108 f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer
98 int m_current_frame = 0; ///< Current frame, should be set by the renderer 109 int m_current_frame = 0; ///< Current frame, should be set by the renderer
99 110
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp
index ec5421afa..3d2588dd2 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp
@@ -4,16 +4,17 @@
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6 6
7#include <glad/glad.h>
8
7#include "video_core/renderer_opengl/gl_buffer_cache.h" 9#include "video_core/renderer_opengl/gl_buffer_cache.h"
8#include "video_core/renderer_opengl/gl_fence_manager.h" 10#include "video_core/renderer_opengl/gl_fence_manager.h"
9 11
10namespace OpenGL { 12namespace OpenGL {
11 13
12GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed) 14GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed) : FenceBase(payload, is_stubbed) {}
13 : VideoCommon::FenceBase(payload, is_stubbed), sync_object{} {}
14 15
15GLInnerFence::GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed) 16GLInnerFence::GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed)
16 : VideoCommon::FenceBase(address, payload, is_stubbed), sync_object{} {} 17 : FenceBase(address, payload, is_stubbed) {}
17 18
18GLInnerFence::~GLInnerFence() = default; 19GLInnerFence::~GLInnerFence() = default;
19 20
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h
index c917b3343..1686cf5c8 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.h
+++ b/src/video_core/renderer_opengl/gl_fence_manager.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <glad/glad.h>
9 8
10#include "common/common_types.h" 9#include "common/common_types.h"
11#include "video_core/fence_manager.h" 10#include "video_core/fence_manager.h"
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index cb284db77..4af5824cd 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -177,15 +177,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
177 } 177 }
178 178
179 if (device.UseAsynchronousShaders()) { 179 if (device.UseAsynchronousShaders()) {
180 // Max worker threads we should allow 180 async_shaders.AllocateWorkers();
181 constexpr u32 MAX_THREADS = 4;
182 // Deduce how many threads we can use
183 const u32 threads_used = std::thread::hardware_concurrency() / 4;
184 // Always allow at least 1 thread regardless of our settings
185 const auto max_worker_count = std::max(1U, threads_used);
186 // Don't use more than MAX_THREADS
187 const auto worker_count = std::min(max_worker_count, MAX_THREADS);
188 async_shaders.AllocateWorkers(worker_count);
189 } 181 }
190} 182}
191 183
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index a787e27d2..0ebcec427 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
5#include <utility> 6#include <utility>
6#include <glad/glad.h> 7#include <glad/glad.h>
7#include "common/common_types.h" 8#include "common/common_types.h"
@@ -82,11 +83,13 @@ void OGLSampler::Release() {
82 handle = 0; 83 handle = 0;
83} 84}
84 85
85void OGLShader::Create(const char* source, GLenum type) { 86void OGLShader::Create(std::string_view source, GLenum type) {
86 if (handle != 0) 87 if (handle != 0) {
87 return; 88 return;
88 if (source == nullptr) 89 }
90 if (source.empty()) {
89 return; 91 return;
92 }
90 93
91 MICROPROFILE_SCOPE(OpenGL_ResourceCreation); 94 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
92 handle = GLShader::LoadShader(source, type); 95 handle = GLShader::LoadShader(source, type);
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index b05cb641c..f48398669 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <string_view>
7#include <utility> 8#include <utility>
8#include <glad/glad.h> 9#include <glad/glad.h>
9#include "common/common_types.h" 10#include "common/common_types.h"
@@ -127,7 +128,7 @@ public:
127 return *this; 128 return *this;
128 } 129 }
129 130
130 void Create(const char* source, GLenum type); 131 void Create(std::string_view source, GLenum type);
131 132
132 void Release(); 133 void Release();
133 134
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 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 @@
22#include "video_core/memory_manager.h" 22#include "video_core/memory_manager.h"
23#include "video_core/renderer_opengl/gl_arb_decompiler.h" 23#include "video_core/renderer_opengl/gl_arb_decompiler.h"
24#include "video_core/renderer_opengl/gl_rasterizer.h" 24#include "video_core/renderer_opengl/gl_rasterizer.h"
25#include "video_core/renderer_opengl/gl_resource_manager.h"
25#include "video_core/renderer_opengl/gl_shader_cache.h" 26#include "video_core/renderer_opengl/gl_shader_cache.h"
26#include "video_core/renderer_opengl/gl_shader_decompiler.h" 27#include "video_core/renderer_opengl/gl_shader_decompiler.h"
27#include "video_core/renderer_opengl/gl_shader_disk_cache.h" 28#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
@@ -403,7 +404,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
403 } 404 }
404 }; 405 };
405 406
406 const auto num_workers{static_cast<std::size_t>(std::thread::hardware_concurrency() + 1ULL)}; 407 const std::size_t num_workers{std::max(1U, std::thread::hardware_concurrency())};
407 const std::size_t bucket_size{transferable->size() / num_workers}; 408 const std::size_t bucket_size{transferable->size() / num_workers};
408 std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers); 409 std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers);
409 std::vector<std::thread> threads(num_workers); 410 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;
73 73
74ShaderDiskCacheEntry::~ShaderDiskCacheEntry() = default; 74ShaderDiskCacheEntry::~ShaderDiskCacheEntry() = default;
75 75
76bool ShaderDiskCacheEntry::Load(FileUtil::IOFile& file) { 76bool ShaderDiskCacheEntry::Load(Common::FS::IOFile& file) {
77 if (file.ReadBytes(&type, sizeof(u32)) != sizeof(u32)) { 77 if (file.ReadBytes(&type, sizeof(u32)) != sizeof(u32)) {
78 return false; 78 return false;
79 } 79 }
@@ -144,7 +144,7 @@ bool ShaderDiskCacheEntry::Load(FileUtil::IOFile& file) {
144 return true; 144 return true;
145} 145}
146 146
147bool ShaderDiskCacheEntry::Save(FileUtil::IOFile& file) const { 147bool ShaderDiskCacheEntry::Save(Common::FS::IOFile& file) const {
148 if (file.WriteObject(static_cast<u32>(type)) != 1 || 148 if (file.WriteObject(static_cast<u32>(type)) != 1 ||
149 file.WriteObject(static_cast<u32>(code.size())) != 1 || 149 file.WriteObject(static_cast<u32>(code.size())) != 1 ||
150 file.WriteObject(static_cast<u32>(code_b.size())) != 1) { 150 file.WriteObject(static_cast<u32>(code_b.size())) != 1) {
@@ -214,20 +214,20 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
214 // Skip games without title id 214 // Skip games without title id
215 const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0; 215 const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
216 if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) { 216 if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) {
217 return {}; 217 return std::nullopt;
218 } 218 }
219 219
220 FileUtil::IOFile file(GetTransferablePath(), "rb"); 220 Common::FS::IOFile file(GetTransferablePath(), "rb");
221 if (!file.IsOpen()) { 221 if (!file.IsOpen()) {
222 LOG_INFO(Render_OpenGL, "No transferable shader cache found"); 222 LOG_INFO(Render_OpenGL, "No transferable shader cache found");
223 is_usable = true; 223 is_usable = true;
224 return {}; 224 return std::nullopt;
225 } 225 }
226 226
227 u32 version{}; 227 u32 version{};
228 if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) { 228 if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) {
229 LOG_ERROR(Render_OpenGL, "Failed to get transferable cache version, skipping it"); 229 LOG_ERROR(Render_OpenGL, "Failed to get transferable cache version, skipping it");
230 return {}; 230 return std::nullopt;
231 } 231 }
232 232
233 if (version < NativeVersion) { 233 if (version < NativeVersion) {
@@ -235,12 +235,12 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
235 file.Close(); 235 file.Close();
236 InvalidateTransferable(); 236 InvalidateTransferable();
237 is_usable = true; 237 is_usable = true;
238 return {}; 238 return std::nullopt;
239 } 239 }
240 if (version > NativeVersion) { 240 if (version > NativeVersion) {
241 LOG_WARNING(Render_OpenGL, "Transferable shader cache was generated with a newer version " 241 LOG_WARNING(Render_OpenGL, "Transferable shader cache was generated with a newer version "
242 "of the emulator, skipping"); 242 "of the emulator, skipping");
243 return {}; 243 return std::nullopt;
244 } 244 }
245 245
246 // Version is valid, load the shaders 246 // Version is valid, load the shaders
@@ -249,7 +249,7 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
249 ShaderDiskCacheEntry& entry = entries.emplace_back(); 249 ShaderDiskCacheEntry& entry = entries.emplace_back();
250 if (!entry.Load(file)) { 250 if (!entry.Load(file)) {
251 LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry, skipping"); 251 LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry, skipping");
252 return {}; 252 return std::nullopt;
253 } 253 }
254 } 254 }
255 255
@@ -262,7 +262,7 @@ std::vector<ShaderDiskCachePrecompiled> ShaderDiskCacheOpenGL::LoadPrecompiled()
262 return {}; 262 return {};
263 } 263 }
264 264
265 FileUtil::IOFile file(GetPrecompiledPath(), "rb"); 265 Common::FS::IOFile file(GetPrecompiledPath(), "rb");
266 if (!file.IsOpen()) { 266 if (!file.IsOpen()) {
267 LOG_INFO(Render_OpenGL, "No precompiled shader cache found"); 267 LOG_INFO(Render_OpenGL, "No precompiled shader cache found");
268 return {}; 268 return {};
@@ -279,7 +279,7 @@ std::vector<ShaderDiskCachePrecompiled> ShaderDiskCacheOpenGL::LoadPrecompiled()
279} 279}
280 280
281std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::LoadPrecompiledFile( 281std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::LoadPrecompiledFile(
282 FileUtil::IOFile& file) { 282 Common::FS::IOFile& file) {
283 // Read compressed file from disk and decompress to virtual precompiled cache file 283 // Read compressed file from disk and decompress to virtual precompiled cache file
284 std::vector<u8> compressed(file.GetSize()); 284 std::vector<u8> compressed(file.GetSize());
285 file.ReadBytes(compressed.data(), compressed.size()); 285 file.ReadBytes(compressed.data(), compressed.size());
@@ -290,12 +290,12 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo
290 ShaderCacheVersionHash file_hash{}; 290 ShaderCacheVersionHash file_hash{};
291 if (!LoadArrayFromPrecompiled(file_hash.data(), file_hash.size())) { 291 if (!LoadArrayFromPrecompiled(file_hash.data(), file_hash.size())) {
292 precompiled_cache_virtual_file_offset = 0; 292 precompiled_cache_virtual_file_offset = 0;
293 return {}; 293 return std::nullopt;
294 } 294 }
295 if (GetShaderCacheVersionHash() != file_hash) { 295 if (GetShaderCacheVersionHash() != file_hash) {
296 LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator"); 296 LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator");
297 precompiled_cache_virtual_file_offset = 0; 297 precompiled_cache_virtual_file_offset = 0;
298 return {}; 298 return std::nullopt;
299 } 299 }
300 300
301 std::vector<ShaderDiskCachePrecompiled> entries; 301 std::vector<ShaderDiskCachePrecompiled> entries;
@@ -305,19 +305,20 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo
305 if (!LoadObjectFromPrecompiled(entry.unique_identifier) || 305 if (!LoadObjectFromPrecompiled(entry.unique_identifier) ||
306 !LoadObjectFromPrecompiled(entry.binary_format) || 306 !LoadObjectFromPrecompiled(entry.binary_format) ||
307 !LoadObjectFromPrecompiled(binary_size)) { 307 !LoadObjectFromPrecompiled(binary_size)) {
308 return {}; 308 return std::nullopt;
309 } 309 }
310 310
311 entry.binary.resize(binary_size); 311 entry.binary.resize(binary_size);
312 if (!LoadArrayFromPrecompiled(entry.binary.data(), entry.binary.size())) { 312 if (!LoadArrayFromPrecompiled(entry.binary.data(), entry.binary.size())) {
313 return {}; 313 return std::nullopt;
314 } 314 }
315 } 315 }
316 return entries; 316
317 return std::move(entries);
317} 318}
318 319
319void ShaderDiskCacheOpenGL::InvalidateTransferable() { 320void ShaderDiskCacheOpenGL::InvalidateTransferable() {
320 if (!FileUtil::Delete(GetTransferablePath())) { 321 if (!Common::FS::Delete(GetTransferablePath())) {
321 LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}", 322 LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}",
322 GetTransferablePath()); 323 GetTransferablePath());
323 } 324 }
@@ -328,7 +329,7 @@ void ShaderDiskCacheOpenGL::InvalidatePrecompiled() {
328 // Clear virtaul precompiled cache file 329 // Clear virtaul precompiled cache file
329 precompiled_cache_virtual_file.Resize(0); 330 precompiled_cache_virtual_file.Resize(0);
330 331
331 if (!FileUtil::Delete(GetPrecompiledPath())) { 332 if (!Common::FS::Delete(GetPrecompiledPath())) {
332 LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath()); 333 LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath());
333 } 334 }
334} 335}
@@ -344,7 +345,7 @@ void ShaderDiskCacheOpenGL::SaveEntry(const ShaderDiskCacheEntry& entry) {
344 return; 345 return;
345 } 346 }
346 347
347 FileUtil::IOFile file = AppendTransferableFile(); 348 Common::FS::IOFile file = AppendTransferableFile();
348 if (!file.IsOpen()) { 349 if (!file.IsOpen()) {
349 return; 350 return;
350 } 351 }
@@ -386,15 +387,15 @@ void ShaderDiskCacheOpenGL::SavePrecompiled(u64 unique_identifier, GLuint progra
386 } 387 }
387} 388}
388 389
389FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const { 390Common::FS::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const {
390 if (!EnsureDirectories()) { 391 if (!EnsureDirectories()) {
391 return {}; 392 return {};
392 } 393 }
393 394
394 const auto transferable_path{GetTransferablePath()}; 395 const auto transferable_path{GetTransferablePath()};
395 const bool existed = FileUtil::Exists(transferable_path); 396 const bool existed = Common::FS::Exists(transferable_path);
396 397
397 FileUtil::IOFile file(transferable_path, "ab"); 398 Common::FS::IOFile file(transferable_path, "ab");
398 if (!file.IsOpen()) { 399 if (!file.IsOpen()) {
399 LOG_ERROR(Render_OpenGL, "Failed to open transferable cache in path={}", transferable_path); 400 LOG_ERROR(Render_OpenGL, "Failed to open transferable cache in path={}", transferable_path);
400 return {}; 401 return {};
@@ -426,7 +427,7 @@ void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() {
426 Common::Compression::CompressDataZSTDDefault(uncompressed.data(), uncompressed.size()); 427 Common::Compression::CompressDataZSTDDefault(uncompressed.data(), uncompressed.size());
427 428
428 const auto precompiled_path{GetPrecompiledPath()}; 429 const auto precompiled_path{GetPrecompiledPath()};
429 FileUtil::IOFile file(precompiled_path, "wb"); 430 Common::FS::IOFile file(precompiled_path, "wb");
430 431
431 if (!file.IsOpen()) { 432 if (!file.IsOpen()) {
432 LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path); 433 LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path);
@@ -440,24 +441,24 @@ void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() {
440 441
441bool ShaderDiskCacheOpenGL::EnsureDirectories() const { 442bool ShaderDiskCacheOpenGL::EnsureDirectories() const {
442 const auto CreateDir = [](const std::string& dir) { 443 const auto CreateDir = [](const std::string& dir) {
443 if (!FileUtil::CreateDir(dir)) { 444 if (!Common::FS::CreateDir(dir)) {
444 LOG_ERROR(Render_OpenGL, "Failed to create directory={}", dir); 445 LOG_ERROR(Render_OpenGL, "Failed to create directory={}", dir);
445 return false; 446 return false;
446 } 447 }
447 return true; 448 return true;
448 }; 449 };
449 450
450 return CreateDir(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) && 451 return CreateDir(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)) &&
451 CreateDir(GetBaseDir()) && CreateDir(GetTransferableDir()) && 452 CreateDir(GetBaseDir()) && CreateDir(GetTransferableDir()) &&
452 CreateDir(GetPrecompiledDir()); 453 CreateDir(GetPrecompiledDir());
453} 454}
454 455
455std::string ShaderDiskCacheOpenGL::GetTransferablePath() const { 456std::string ShaderDiskCacheOpenGL::GetTransferablePath() const {
456 return FileUtil::SanitizePath(GetTransferableDir() + DIR_SEP_CHR + GetTitleID() + ".bin"); 457 return Common::FS::SanitizePath(GetTransferableDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
457} 458}
458 459
459std::string ShaderDiskCacheOpenGL::GetPrecompiledPath() const { 460std::string ShaderDiskCacheOpenGL::GetPrecompiledPath() const {
460 return FileUtil::SanitizePath(GetPrecompiledDir() + DIR_SEP_CHR + GetTitleID() + ".bin"); 461 return Common::FS::SanitizePath(GetPrecompiledDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
461} 462}
462 463
463std::string ShaderDiskCacheOpenGL::GetTransferableDir() const { 464std::string ShaderDiskCacheOpenGL::GetTransferableDir() const {
@@ -469,7 +470,7 @@ std::string ShaderDiskCacheOpenGL::GetPrecompiledDir() const {
469} 470}
470 471
471std::string ShaderDiskCacheOpenGL::GetBaseDir() const { 472std::string ShaderDiskCacheOpenGL::GetBaseDir() const {
472 return FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + DIR_SEP "opengl"; 473 return Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir) + DIR_SEP "opengl";
473} 474}
474 475
475std::string ShaderDiskCacheOpenGL::GetTitleID() const { 476std::string ShaderDiskCacheOpenGL::GetTitleID() const {
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
index a79cef0e9..db2bb73bc 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
@@ -25,7 +25,7 @@ namespace Core {
25class System; 25class System;
26} 26}
27 27
28namespace FileUtil { 28namespace Common::FS {
29class IOFile; 29class IOFile;
30} 30}
31 31
@@ -38,9 +38,9 @@ struct ShaderDiskCacheEntry {
38 ShaderDiskCacheEntry(); 38 ShaderDiskCacheEntry();
39 ~ShaderDiskCacheEntry(); 39 ~ShaderDiskCacheEntry();
40 40
41 bool Load(FileUtil::IOFile& file); 41 bool Load(Common::FS::IOFile& file);
42 42
43 bool Save(FileUtil::IOFile& file) const; 43 bool Save(Common::FS::IOFile& file) const;
44 44
45 bool HasProgramA() const { 45 bool HasProgramA() const {
46 return !code.empty() && !code_b.empty(); 46 return !code.empty() && !code_b.empty();
@@ -97,10 +97,10 @@ public:
97private: 97private:
98 /// Loads the transferable cache. Returns empty on failure. 98 /// Loads the transferable cache. Returns empty on failure.
99 std::optional<std::vector<ShaderDiskCachePrecompiled>> LoadPrecompiledFile( 99 std::optional<std::vector<ShaderDiskCachePrecompiled>> LoadPrecompiledFile(
100 FileUtil::IOFile& file); 100 Common::FS::IOFile& file);
101 101
102 /// Opens current game's transferable file and write it's header if it doesn't exist 102 /// Opens current game's transferable file and write it's header if it doesn't exist
103 FileUtil::IOFile AppendTransferableFile() const; 103 Common::FS::IOFile AppendTransferableFile() const;
104 104
105 /// Save precompiled header to precompiled_cache_in_memory 105 /// Save precompiled header to precompiled_cache_in_memory
106 void SavePrecompiledHeaderToVirtualPrecompiledCache(); 106 void SavePrecompiledHeaderToVirtualPrecompiledCache();
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index 9e74eda0d..4bf0d6090 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
5#include <vector> 6#include <vector>
6#include <glad/glad.h> 7#include <glad/glad.h>
7#include "common/assert.h" 8#include "common/assert.h"
@@ -11,7 +12,8 @@
11namespace OpenGL::GLShader { 12namespace OpenGL::GLShader {
12 13
13namespace { 14namespace {
14const char* GetStageDebugName(GLenum type) { 15
16std::string_view StageDebugName(GLenum type) {
15 switch (type) { 17 switch (type) {
16 case GL_VERTEX_SHADER: 18 case GL_VERTEX_SHADER:
17 return "vertex"; 19 return "vertex";
@@ -25,12 +27,17 @@ const char* GetStageDebugName(GLenum type) {
25 UNIMPLEMENTED(); 27 UNIMPLEMENTED();
26 return "unknown"; 28 return "unknown";
27} 29}
30
28} // Anonymous namespace 31} // Anonymous namespace
29 32
30GLuint LoadShader(const char* source, GLenum type) { 33GLuint LoadShader(std::string_view source, GLenum type) {
31 const char* debug_type = GetStageDebugName(type); 34 const std::string_view debug_type = StageDebugName(type);
32 const GLuint shader_id = glCreateShader(type); 35 const GLuint shader_id = glCreateShader(type);
33 glShaderSource(shader_id, 1, &source, nullptr); 36
37 const GLchar* source_string = source.data();
38 const GLint source_length = static_cast<GLint>(source.size());
39
40 glShaderSource(shader_id, 1, &source_string, &source_length);
34 LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); 41 LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type);
35 glCompileShader(shader_id); 42 glCompileShader(shader_id);
36 43
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h
index 03b7548c2..1b770532e 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.h
+++ b/src/video_core/renderer_opengl/gl_shader_util.h
@@ -38,7 +38,7 @@ void LogShaderSource(T... shaders) {
38 * @param source String of the GLSL shader program 38 * @param source String of the GLSL shader program
39 * @param type Type of the shader (GL_VERTEX_SHADER, GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER) 39 * @param type Type of the shader (GL_VERTEX_SHADER, GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER)
40 */ 40 */
41GLuint LoadShader(const char* source, GLenum type); 41GLuint LoadShader(std::string_view source, GLenum type);
42 42
43/** 43/**
44 * Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader) 44 * Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader)
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 0a7bc9e2b..f403f388a 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -403,7 +403,7 @@ void CachedSurface::DecorateSurfaceName() {
403 LabelGLObject(GL_TEXTURE, texture.handle, GetGpuAddr(), params.TargetName()); 403 LabelGLObject(GL_TEXTURE, texture.handle, GetGpuAddr(), params.TargetName());
404} 404}
405 405
406void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, std::string prefix) { 406void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, const std::string& prefix) {
407 LabelGLObject(GL_TEXTURE, main_view.handle, gpu_addr, prefix); 407 LabelGLObject(GL_TEXTURE, main_view.handle, gpu_addr, prefix);
408} 408}
409 409
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index bfc4ddf5d..de8f18489 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -90,7 +90,7 @@ public:
90 Tegra::Texture::SwizzleSource z_source, 90 Tegra::Texture::SwizzleSource z_source,
91 Tegra::Texture::SwizzleSource w_source); 91 Tegra::Texture::SwizzleSource w_source);
92 92
93 void DecorateViewName(GPUVAddr gpu_addr, std::string prefix); 93 void DecorateViewName(GPUVAddr gpu_addr, const std::string& prefix);
94 94
95 void MarkAsModified(u64 tick) { 95 void MarkAsModified(u64 tick) {
96 surface.MarkAsModified(true, tick); 96 surface.MarkAsModified(true, tick);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 52e9e8250..b759c2dba 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -21,6 +21,8 @@
21#include "core/perf_stats.h" 21#include "core/perf_stats.h"
22#include "core/settings.h" 22#include "core/settings.h"
23#include "core/telemetry_session.h" 23#include "core/telemetry_session.h"
24#include "video_core/host_shaders/opengl_present_frag.h"
25#include "video_core/host_shaders/opengl_present_vert.h"
24#include "video_core/morton.h" 26#include "video_core/morton.h"
25#include "video_core/renderer_opengl/gl_rasterizer.h" 27#include "video_core/renderer_opengl/gl_rasterizer.h"
26#include "video_core/renderer_opengl/gl_shader_manager.h" 28#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -44,46 +46,6 @@ struct Frame {
44 bool is_srgb{}; /// Framebuffer is sRGB or RGB 46 bool is_srgb{}; /// Framebuffer is sRGB or RGB
45}; 47};
46 48
47constexpr char VERTEX_SHADER[] = R"(
48#version 430 core
49
50out gl_PerVertex {
51 vec4 gl_Position;
52};
53
54layout (location = 0) in vec2 vert_position;
55layout (location = 1) in vec2 vert_tex_coord;
56layout (location = 0) out vec2 frag_tex_coord;
57
58// This is a truncated 3x3 matrix for 2D transformations:
59// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
60// The third column performs translation.
61// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
62// implicitly be [0, 0, 1]
63layout (location = 0) uniform mat3x2 modelview_matrix;
64
65void main() {
66 // Multiply input position by the rotscale part of the matrix and then manually translate by
67 // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
68 // to `vec3(vert_position.xy, 1.0)`
69 gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
70 frag_tex_coord = vert_tex_coord;
71}
72)";
73
74constexpr char FRAGMENT_SHADER[] = R"(
75#version 430 core
76
77layout (location = 0) in vec2 frag_tex_coord;
78layout (location = 0) out vec4 color;
79
80layout (binding = 0) uniform sampler2D color_texture;
81
82void main() {
83 color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
84}
85)";
86
87constexpr GLint PositionLocation = 0; 49constexpr GLint PositionLocation = 0;
88constexpr GLint TexCoordLocation = 1; 50constexpr GLint TexCoordLocation = 1;
89constexpr GLint ModelViewMatrixLocation = 0; 51constexpr GLint ModelViewMatrixLocation = 0;
@@ -313,10 +275,11 @@ public:
313 } 275 }
314}; 276};
315 277
316RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system, 278RendererOpenGL::RendererOpenGL(Core::System& system_, Core::Frontend::EmuWindow& emu_window_,
317 Core::Frontend::GraphicsContext& context) 279 Tegra::GPU& gpu_,
318 : RendererBase{emu_window}, emu_window{emu_window}, system{system}, context{context}, 280 std::unique_ptr<Core::Frontend::GraphicsContext> context_)
319 program_manager{device}, has_debug_tool{HasDebugTool()} {} 281 : RendererBase{emu_window_, std::move(context_)}, system{system_},
282 emu_window{emu_window_}, gpu{gpu_}, program_manager{device}, has_debug_tool{HasDebugTool()} {}
320 283
321RendererOpenGL::~RendererOpenGL() = default; 284RendererOpenGL::~RendererOpenGL() = default;
322 285
@@ -384,7 +347,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
384 if (has_debug_tool) { 347 if (has_debug_tool) {
385 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 348 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
386 Present(0); 349 Present(0);
387 context.SwapBuffers(); 350 context->SwapBuffers();
388 } 351 }
389} 352}
390 353
@@ -460,10 +423,10 @@ void RendererOpenGL::InitOpenGLObjects() {
460 423
461 // Create shader programs 424 // Create shader programs
462 OGLShader vertex_shader; 425 OGLShader vertex_shader;
463 vertex_shader.Create(VERTEX_SHADER, GL_VERTEX_SHADER); 426 vertex_shader.Create(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
464 427
465 OGLShader fragment_shader; 428 OGLShader fragment_shader;
466 fragment_shader.Create(FRAGMENT_SHADER, GL_FRAGMENT_SHADER); 429 fragment_shader.Create(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER);
467 430
468 vertex_program.Create(true, false, vertex_shader.handle); 431 vertex_program.Create(true, false, vertex_shader.handle);
469 fragment_program.Create(true, false, fragment_shader.handle); 432 fragment_program.Create(true, false, fragment_shader.handle);
@@ -509,9 +472,10 @@ void RendererOpenGL::AddTelemetryFields() {
509 LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model); 472 LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);
510 473
511 auto& telemetry_session = system.TelemetrySession(); 474 auto& telemetry_session = system.TelemetrySession();
512 telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_Vendor", gpu_vendor); 475 constexpr auto user_system = Common::Telemetry::FieldType::UserSystem;
513 telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model); 476 telemetry_session.AddField(user_system, "GPU_Vendor", gpu_vendor);
514 telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_OpenGL_Version", gl_version); 477 telemetry_session.AddField(user_system, "GPU_Model", gpu_model);
478 telemetry_session.AddField(user_system, "GPU_OpenGL_Version", gl_version);
515} 479}
516 480
517void RendererOpenGL::CreateRasterizer() { 481void RendererOpenGL::CreateRasterizer() {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 8b18d32e6..52ea76b7d 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -56,8 +56,9 @@ class FrameMailbox;
56 56
57class RendererOpenGL final : public VideoCore::RendererBase { 57class RendererOpenGL final : public VideoCore::RendererBase {
58public: 58public:
59 explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system, 59 explicit RendererOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
60 Core::Frontend::GraphicsContext& context); 60 Tegra::GPU& gpu,
61 std::unique_ptr<Core::Frontend::GraphicsContext> context);
61 ~RendererOpenGL() override; 62 ~RendererOpenGL() override;
62 63
63 bool Init() override; 64 bool Init() override;
@@ -93,9 +94,9 @@ private:
93 94
94 bool Present(int timeout_ms); 95 bool Present(int timeout_ms);
95 96
96 Core::Frontend::EmuWindow& emu_window;
97 Core::System& system; 97 Core::System& system;
98 Core::Frontend::GraphicsContext& context; 98 Core::Frontend::EmuWindow& emu_window;
99 Tegra::GPU& gpu;
99 const Device device; 100 const Device device;
100 101
101 StateTracker state_tracker{system}; 102 StateTracker state_tracker{system};
@@ -120,7 +121,7 @@ private:
120 std::vector<u8> gl_framebuffer_data; 121 std::vector<u8> gl_framebuffer_data;
121 122
122 /// Used for transforming the framebuffer orientation 123 /// Used for transforming the framebuffer orientation
123 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags; 124 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{};
124 Common::Rectangle<int> framebuffer_crop_rect; 125 Common::Rectangle<int> framebuffer_crop_rect;
125 126
126 /// Frame presentation mailbox 127 /// Frame presentation mailbox
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
index 435c8c1b8..5b01020ec 100644
--- a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
+++ b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
@@ -65,10 +65,10 @@ bool NsightAftermathTracker::Initialize() {
65 return false; 65 return false;
66 } 66 }
67 67
68 dump_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "gpucrash"; 68 dump_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir) + "gpucrash";
69 69
70 (void)FileUtil::DeleteDirRecursively(dump_dir); 70 (void)Common::FS::DeleteDirRecursively(dump_dir);
71 if (!FileUtil::CreateDir(dump_dir)) { 71 if (!Common::FS::CreateDir(dump_dir)) {
72 LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory"); 72 LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory");
73 return false; 73 return false;
74 } 74 }
@@ -106,7 +106,7 @@ void NsightAftermathTracker::SaveShader(const std::vector<u32>& spirv) const {
106 return; 106 return;
107 } 107 }
108 108
109 FileUtil::IOFile file(fmt::format("{}/source_{:016x}.spv", dump_dir, hash.hash), "wb"); 109 Common::FS::IOFile file(fmt::format("{}/source_{:016x}.spv", dump_dir, hash.hash), "wb");
110 if (!file.IsOpen()) { 110 if (!file.IsOpen()) {
111 LOG_ERROR(Render_Vulkan, "Failed to dump SPIR-V module with hash={:016x}", hash.hash); 111 LOG_ERROR(Render_Vulkan, "Failed to dump SPIR-V module with hash={:016x}", hash.hash);
112 return; 112 return;
@@ -156,12 +156,12 @@ void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump,
156 }(); 156 }();
157 157
158 std::string_view dump_view(static_cast<const char*>(gpu_crash_dump), gpu_crash_dump_size); 158 std::string_view dump_view(static_cast<const char*>(gpu_crash_dump), gpu_crash_dump_size);
159 if (FileUtil::WriteStringToFile(false, base_name, dump_view) != gpu_crash_dump_size) { 159 if (Common::FS::WriteStringToFile(false, base_name, dump_view) != gpu_crash_dump_size) {
160 LOG_ERROR(Render_Vulkan, "Failed to write dump file"); 160 LOG_ERROR(Render_Vulkan, "Failed to write dump file");
161 return; 161 return;
162 } 162 }
163 const std::string_view json_view(json.data(), json.size()); 163 const std::string_view json_view(json.data(), json.size());
164 if (FileUtil::WriteStringToFile(true, base_name + ".json", json_view) != json.size()) { 164 if (Common::FS::WriteStringToFile(true, base_name + ".json", json_view) != json.size()) {
165 LOG_ERROR(Render_Vulkan, "Failed to write JSON"); 165 LOG_ERROR(Render_Vulkan, "Failed to write JSON");
166 return; 166 return;
167 } 167 }
@@ -180,7 +180,7 @@ void NsightAftermathTracker::OnShaderDebugInfoCallback(const void* shader_debug_
180 180
181 const std::string path = 181 const std::string path =
182 fmt::format("{}/shader_{:016x}{:016x}.nvdbg", dump_dir, identifier.id[0], identifier.id[1]); 182 fmt::format("{}/shader_{:016x}{:016x}.nvdbg", dump_dir, identifier.id[0], identifier.id[1]);
183 FileUtil::IOFile file(path, "wb"); 183 Common::FS::IOFile file(path, "wb");
184 if (!file.IsOpen()) { 184 if (!file.IsOpen()) {
185 LOG_ERROR(Render_Vulkan, "Failed to create file {}", path); 185 LOG_ERROR(Render_Vulkan, "Failed to create file {}", path);
186 return; 186 return;
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 2258479f5..ae46e0444 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -78,7 +78,7 @@ Common::DynamicLibrary OpenVulkanLibrary() {
78 if (!libvulkan_env || !library.Open(libvulkan_env)) { 78 if (!libvulkan_env || !library.Open(libvulkan_env)) {
79 // Use the libvulkan.dylib from the application bundle. 79 // Use the libvulkan.dylib from the application bundle.
80 const std::string filename = 80 const std::string filename =
81 FileUtil::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib"; 81 Common::FS::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib";
82 library.Open(filename.c_str()); 82 library.Open(filename.c_str());
83 } 83 }
84#else 84#else
@@ -237,8 +237,10 @@ std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_ext
237 237
238} // Anonymous namespace 238} // Anonymous namespace
239 239
240RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system) 240RendererVulkan::RendererVulkan(Core::System& system_, Core::Frontend::EmuWindow& emu_window,
241 : RendererBase(window), system{system} {} 241 Tegra::GPU& gpu_,
242 std::unique_ptr<Core::Frontend::GraphicsContext> context)
243 : RendererBase{emu_window, std::move(context)}, system{system_}, gpu{gpu_} {}
242 244
243RendererVulkan::~RendererVulkan() { 245RendererVulkan::~RendererVulkan() {
244 ShutDown(); 246 ShutDown();
@@ -439,7 +441,7 @@ void RendererVulkan::Report() const {
439 LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version); 441 LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
440 442
441 auto& telemetry_session = system.TelemetrySession(); 443 auto& telemetry_session = system.TelemetrySession();
442 constexpr auto field = Telemetry::FieldType::UserSystem; 444 constexpr auto field = Common::Telemetry::FieldType::UserSystem;
443 telemetry_session.AddField(field, "GPU_Vendor", vendor_name); 445 telemetry_session.AddField(field, "GPU_Vendor", vendor_name);
444 telemetry_session.AddField(field, "GPU_Model", model_name); 446 telemetry_session.AddField(field, "GPU_Model", model_name);
445 telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name); 447 telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name);
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 522b5bff8..13debbbc0 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -38,7 +38,9 @@ struct VKScreenInfo {
38 38
39class RendererVulkan final : public VideoCore::RendererBase { 39class RendererVulkan final : public VideoCore::RendererBase {
40public: 40public:
41 explicit RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system); 41 explicit RendererVulkan(Core::System& system, Core::Frontend::EmuWindow& emu_window,
42 Tegra::GPU& gpu,
43 std::unique_ptr<Core::Frontend::GraphicsContext> context);
42 ~RendererVulkan() override; 44 ~RendererVulkan() override;
43 45
44 bool Init() override; 46 bool Init() override;
@@ -58,6 +60,7 @@ private:
58 void Report() const; 60 void Report() const;
59 61
60 Core::System& system; 62 Core::System& system;
63 Tegra::GPU& gpu;
61 64
62 Common::DynamicLibrary library; 65 Common::DynamicLibrary library;
63 vk::InstanceDispatch dld; 66 vk::InstanceDispatch dld;
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index 0c03e4d83..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() {
380 380
381 CollectTelemetryParameters(); 381 CollectTelemetryParameters();
382 382
383 if (ext_extended_dynamic_state && driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR) {
384 // AMD's proprietary driver supports VK_EXT_extended_dynamic_state but the <stride> field
385 // seems to be bugged. Blacklisting it for now.
386 LOG_WARNING(Render_Vulkan,
387 "Blacklisting AMD proprietary from VK_EXT_extended_dynamic_state");
388 ext_extended_dynamic_state = false;
389 }
390
383 graphics_queue = logical.GetQueue(graphics_family); 391 graphics_queue = logical.GetQueue(graphics_family);
384 present_queue = logical.GetQueue(present_family); 392 present_queue = logical.GetQueue(present_family);
393
394 use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue();
385 return true; 395 return true;
386} 396}
387 397
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h
index 529744f2d..26a233db1 100644
--- a/src/video_core/renderer_vulkan/vk_device.h
+++ b/src/video_core/renderer_vulkan/vk_device.h
@@ -202,6 +202,11 @@ public:
202 return reported_extensions; 202 return reported_extensions;
203 } 203 }
204 204
205 /// Returns true if the setting for async shader compilation is enabled.
206 bool UseAsynchronousShaders() const {
207 return use_asynchronous_shaders;
208 }
209
205 /// Checks if the physical device is suitable. 210 /// Checks if the physical device is suitable.
206 static bool IsSuitable(vk::PhysicalDevice physical, VkSurfaceKHR surface); 211 static bool IsSuitable(vk::PhysicalDevice physical, VkSurfaceKHR surface);
207 212
@@ -252,6 +257,9 @@ private:
252 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state. 257 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
253 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. 258 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
254 259
260 // Asynchronous Graphics Pipeline setting
261 bool use_asynchronous_shaders{}; ///< Setting to use asynchronous shaders/graphics pipeline
262
255 // Telemetry parameters 263 // Telemetry parameters
256 std::string vendor_name; ///< Device's driver name. 264 std::string vendor_name; ///< Device's driver name.
257 std::vector<std::string> reported_extensions; ///< Reported Vulkan extensions. 265 std::vector<std::string> reported_extensions; ///< Reported Vulkan extensions.
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
index a02be5487..d7f65d435 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
@@ -29,7 +29,7 @@ void InnerFence::Queue() {
29 } 29 }
30 ASSERT(!event); 30 ASSERT(!event);
31 31
32 event = device.GetLogical().CreateEvent(); 32 event = device.GetLogical().CreateNewEvent();
33 ticks = scheduler.Ticks(); 33 ticks = scheduler.Ticks();
34 34
35 scheduler.RequestOutsideRenderPassOperationContext(); 35 scheduler.RequestOutsideRenderPassOperationContext();
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index aaf930b90..2e46c6278 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -78,15 +78,14 @@ VKGraphicsPipeline::VKGraphicsPipeline(const VKDevice& device, VKScheduler& sche
78 const GraphicsPipelineCacheKey& key, 78 const GraphicsPipelineCacheKey& key,
79 vk::Span<VkDescriptorSetLayoutBinding> bindings, 79 vk::Span<VkDescriptorSetLayoutBinding> bindings,
80 const SPIRVProgram& program) 80 const SPIRVProgram& program)
81 : device{device}, scheduler{scheduler}, fixed_state{key.fixed_state}, hash{key.Hash()}, 81 : device{device}, scheduler{scheduler}, cache_key{key}, hash{cache_key.Hash()},
82 descriptor_set_layout{CreateDescriptorSetLayout(bindings)}, 82 descriptor_set_layout{CreateDescriptorSetLayout(bindings)},
83 descriptor_allocator{descriptor_pool, *descriptor_set_layout}, 83 descriptor_allocator{descriptor_pool, *descriptor_set_layout},
84 update_descriptor_queue{update_descriptor_queue}, layout{CreatePipelineLayout()}, 84 update_descriptor_queue{update_descriptor_queue}, layout{CreatePipelineLayout()},
85 descriptor_template{CreateDescriptorUpdateTemplate(program)}, modules{CreateShaderModules( 85 descriptor_template{CreateDescriptorUpdateTemplate(program)}, modules{CreateShaderModules(
86 program)}, 86 program)},
87 renderpass{renderpass_cache.GetRenderPass(key.renderpass_params)}, pipeline{CreatePipeline( 87 renderpass{renderpass_cache.GetRenderPass(cache_key.renderpass_params)},
88 key.renderpass_params, 88 pipeline{CreatePipeline(cache_key.renderpass_params, program)} {}
89 program)} {}
90 89
91VKGraphicsPipeline::~VKGraphicsPipeline() = default; 90VKGraphicsPipeline::~VKGraphicsPipeline() = default;
92 91
@@ -181,7 +180,7 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
181 180
182vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params, 181vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params,
183 const SPIRVProgram& program) const { 182 const SPIRVProgram& program) const {
184 const auto& state = fixed_state; 183 const auto& state = cache_key.fixed_state;
185 const auto& viewport_swizzles = state.viewport_swizzles; 184 const auto& viewport_swizzles = state.viewport_swizzles;
186 185
187 FixedPipelineState::DynamicState dynamic; 186 FixedPipelineState::DynamicState dynamic;
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index a1d699a6c..58aa35efd 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -19,7 +19,27 @@ namespace Vulkan {
19 19
20using Maxwell = Tegra::Engines::Maxwell3D::Regs; 20using Maxwell = Tegra::Engines::Maxwell3D::Regs;
21 21
22struct GraphicsPipelineCacheKey; 22struct GraphicsPipelineCacheKey {
23 RenderPassParams renderpass_params;
24 u32 padding;
25 std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
26 FixedPipelineState fixed_state;
27
28 std::size_t Hash() const noexcept;
29
30 bool operator==(const GraphicsPipelineCacheKey& rhs) const noexcept;
31
32 bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
33 return !operator==(rhs);
34 }
35
36 std::size_t Size() const noexcept {
37 return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size();
38 }
39};
40static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
41static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
42static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>);
23 43
24class VKDescriptorPool; 44class VKDescriptorPool;
25class VKDevice; 45class VKDevice;
@@ -54,6 +74,10 @@ public:
54 return renderpass; 74 return renderpass;
55 } 75 }
56 76
77 GraphicsPipelineCacheKey GetCacheKey() const {
78 return cache_key;
79 }
80
57private: 81private:
58 vk::DescriptorSetLayout CreateDescriptorSetLayout( 82 vk::DescriptorSetLayout CreateDescriptorSetLayout(
59 vk::Span<VkDescriptorSetLayoutBinding> bindings) const; 83 vk::Span<VkDescriptorSetLayoutBinding> bindings) const;
@@ -70,7 +94,7 @@ private:
70 94
71 const VKDevice& device; 95 const VKDevice& device;
72 VKScheduler& scheduler; 96 VKScheduler& scheduler;
73 const FixedPipelineState fixed_state; 97 const GraphicsPipelineCacheKey cache_key;
74 const u64 hash; 98 const u64 hash;
75 99
76 vk::DescriptorSetLayout descriptor_set_layout; 100 vk::DescriptorSetLayout descriptor_set_layout;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 418c62bc4..cfdcdd6ab 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -28,6 +28,7 @@
28#include "video_core/shader/compiler_settings.h" 28#include "video_core/shader/compiler_settings.h"
29#include "video_core/shader/memory_util.h" 29#include "video_core/shader/memory_util.h"
30#include "video_core/shader_cache.h" 30#include "video_core/shader_cache.h"
31#include "video_core/shader_notify.h"
31 32
32namespace Vulkan { 33namespace Vulkan {
33 34
@@ -205,24 +206,43 @@ std::array<Shader*, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
205 return last_shaders = shaders; 206 return last_shaders = shaders;
206} 207}
207 208
208VKGraphicsPipeline& VKPipelineCache::GetGraphicsPipeline(const GraphicsPipelineCacheKey& key) { 209VKGraphicsPipeline* VKPipelineCache::GetGraphicsPipeline(
210 const GraphicsPipelineCacheKey& key, VideoCommon::Shader::AsyncShaders& async_shaders) {
209 MICROPROFILE_SCOPE(Vulkan_PipelineCache); 211 MICROPROFILE_SCOPE(Vulkan_PipelineCache);
210 212
211 if (last_graphics_pipeline && last_graphics_key == key) { 213 if (last_graphics_pipeline && last_graphics_key == key) {
212 return *last_graphics_pipeline; 214 return last_graphics_pipeline;
213 } 215 }
214 last_graphics_key = key; 216 last_graphics_key = key;
215 217
218 if (device.UseAsynchronousShaders() && async_shaders.IsShaderAsync(system.GPU())) {
219 std::unique_lock lock{pipeline_cache};
220 const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key);
221 if (is_cache_miss) {
222 system.GPU().ShaderNotify().MarkSharderBuilding();
223 LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash());
224 const auto [program, bindings] = DecompileShaders(key.fixed_state);
225 async_shaders.QueueVulkanShader(this, device, scheduler, descriptor_pool,
226 update_descriptor_queue, renderpass_cache, bindings,
227 program, key);
228 }
229 last_graphics_pipeline = pair->second.get();
230 return last_graphics_pipeline;
231 }
232
216 const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key); 233 const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key);
217 auto& entry = pair->second; 234 auto& entry = pair->second;
218 if (is_cache_miss) { 235 if (is_cache_miss) {
236 system.GPU().ShaderNotify().MarkSharderBuilding();
219 LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash()); 237 LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash());
220 const auto [program, bindings] = DecompileShaders(key); 238 const auto [program, bindings] = DecompileShaders(key.fixed_state);
221 entry = std::make_unique<VKGraphicsPipeline>(device, scheduler, descriptor_pool, 239 entry = std::make_unique<VKGraphicsPipeline>(device, scheduler, descriptor_pool,
222 update_descriptor_queue, renderpass_cache, key, 240 update_descriptor_queue, renderpass_cache, key,
223 bindings, program); 241 bindings, program);
242 system.GPU().ShaderNotify().MarkShaderComplete();
224 } 243 }
225 return *(last_graphics_pipeline = entry.get()); 244 last_graphics_pipeline = entry.get();
245 return last_graphics_pipeline;
226} 246}
227 247
228VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCacheKey& key) { 248VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCacheKey& key) {
@@ -277,6 +297,12 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
277 return *entry; 297 return *entry;
278} 298}
279 299
300void VKPipelineCache::EmplacePipeline(std::unique_ptr<VKGraphicsPipeline> pipeline) {
301 system.GPU().ShaderNotify().MarkShaderComplete();
302 std::unique_lock lock{pipeline_cache};
303 graphics_cache.at(pipeline->GetCacheKey()) = std::move(pipeline);
304}
305
280void VKPipelineCache::OnShaderRemoval(Shader* shader) { 306void VKPipelineCache::OnShaderRemoval(Shader* shader) {
281 bool finished = false; 307 bool finished = false;
282 const auto Finish = [&] { 308 const auto Finish = [&] {
@@ -312,8 +338,7 @@ void VKPipelineCache::OnShaderRemoval(Shader* shader) {
312} 338}
313 339
314std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> 340std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>>
315VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) { 341VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) {
316 const auto& fixed_state = key.fixed_state;
317 auto& memory_manager = system.GPU().MemoryManager(); 342 auto& memory_manager = system.GPU().MemoryManager();
318 const auto& gpu = system.GPU().Maxwell3D(); 343 const auto& gpu = system.GPU().Maxwell3D();
319 344
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 0a3fe65fb..c04829e77 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -22,6 +22,7 @@
22#include "video_core/renderer_vulkan/vk_renderpass_cache.h" 22#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
23#include "video_core/renderer_vulkan/vk_shader_decompiler.h" 23#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
24#include "video_core/renderer_vulkan/wrapper.h" 24#include "video_core/renderer_vulkan/wrapper.h"
25#include "video_core/shader/async_shaders.h"
25#include "video_core/shader/memory_util.h" 26#include "video_core/shader/memory_util.h"
26#include "video_core/shader/registry.h" 27#include "video_core/shader/registry.h"
27#include "video_core/shader/shader_ir.h" 28#include "video_core/shader/shader_ir.h"
@@ -43,28 +44,6 @@ class VKUpdateDescriptorQueue;
43 44
44using Maxwell = Tegra::Engines::Maxwell3D::Regs; 45using Maxwell = Tegra::Engines::Maxwell3D::Regs;
45 46
46struct GraphicsPipelineCacheKey {
47 RenderPassParams renderpass_params;
48 u32 padding;
49 std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
50 FixedPipelineState fixed_state;
51
52 std::size_t Hash() const noexcept;
53
54 bool operator==(const GraphicsPipelineCacheKey& rhs) const noexcept;
55
56 bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
57 return !operator==(rhs);
58 }
59
60 std::size_t Size() const noexcept {
61 return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size();
62 }
63};
64static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
65static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
66static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>);
67
68struct ComputePipelineCacheKey { 47struct ComputePipelineCacheKey {
69 GPUVAddr shader; 48 GPUVAddr shader;
70 u32 shared_memory_size; 49 u32 shared_memory_size;
@@ -152,16 +131,19 @@ public:
152 131
153 std::array<Shader*, Maxwell::MaxShaderProgram> GetShaders(); 132 std::array<Shader*, Maxwell::MaxShaderProgram> GetShaders();
154 133
155 VKGraphicsPipeline& GetGraphicsPipeline(const GraphicsPipelineCacheKey& key); 134 VKGraphicsPipeline* GetGraphicsPipeline(const GraphicsPipelineCacheKey& key,
135 VideoCommon::Shader::AsyncShaders& async_shaders);
156 136
157 VKComputePipeline& GetComputePipeline(const ComputePipelineCacheKey& key); 137 VKComputePipeline& GetComputePipeline(const ComputePipelineCacheKey& key);
158 138
139 void EmplacePipeline(std::unique_ptr<VKGraphicsPipeline> pipeline);
140
159protected: 141protected:
160 void OnShaderRemoval(Shader* shader) final; 142 void OnShaderRemoval(Shader* shader) final;
161 143
162private: 144private:
163 std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> DecompileShaders( 145 std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> DecompileShaders(
164 const GraphicsPipelineCacheKey& key); 146 const FixedPipelineState& fixed_state);
165 147
166 Core::System& system; 148 Core::System& system;
167 const VKDevice& device; 149 const VKDevice& device;
@@ -178,6 +160,7 @@ private:
178 GraphicsPipelineCacheKey last_graphics_key; 160 GraphicsPipelineCacheKey last_graphics_key;
179 VKGraphicsPipeline* last_graphics_pipeline = nullptr; 161 VKGraphicsPipeline* last_graphics_pipeline = nullptr;
180 162
163 std::mutex pipeline_cache;
181 std::unordered_map<GraphicsPipelineCacheKey, std::unique_ptr<VKGraphicsPipeline>> 164 std::unordered_map<GraphicsPipelineCacheKey, std::unique_ptr<VKGraphicsPipeline>>
182 graphics_cache; 165 graphics_cache;
183 std::unordered_map<ComputePipelineCacheKey, std::unique_ptr<VKComputePipeline>> compute_cache; 166 std::unordered_map<ComputePipelineCacheKey, std::unique_ptr<VKComputePipeline>> compute_cache;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 7500e8244..ff1b52eab 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -14,6 +14,7 @@
14#include "common/assert.h" 14#include "common/assert.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/microprofile.h" 16#include "common/microprofile.h"
17#include "common/scope_exit.h"
17#include "core/core.h" 18#include "core/core.h"
18#include "core/settings.h" 19#include "core/settings.h"
19#include "video_core/engines/kepler_compute.h" 20#include "video_core/engines/kepler_compute.h"
@@ -400,8 +401,12 @@ RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWind
400 buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool), 401 buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool),
401 sampler_cache(device), 402 sampler_cache(device),
402 fence_manager(system, *this, device, scheduler, texture_cache, buffer_cache, query_cache), 403 fence_manager(system, *this, device, scheduler, texture_cache, buffer_cache, query_cache),
403 query_cache(system, *this, device, scheduler), wfi_event{device.GetLogical().CreateEvent()} { 404 query_cache(system, *this, device, scheduler),
405 wfi_event{device.GetLogical().CreateNewEvent()}, async_shaders{renderer} {
404 scheduler.SetQueryCache(query_cache); 406 scheduler.SetQueryCache(query_cache);
407 if (device.UseAsynchronousShaders()) {
408 async_shaders.AllocateWorkers();
409 }
405} 410}
406 411
407RasterizerVulkan::~RasterizerVulkan() = default; 412RasterizerVulkan::~RasterizerVulkan() = default;
@@ -413,6 +418,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
413 418
414 query_cache.UpdateCounters(); 419 query_cache.UpdateCounters();
415 420
421 SCOPE_EXIT({ system.GPU().TickWork(); });
422
416 const auto& gpu = system.GPU().Maxwell3D(); 423 const auto& gpu = system.GPU().Maxwell3D();
417 GraphicsPipelineCacheKey key; 424 GraphicsPipelineCacheKey key;
418 key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported()); 425 key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported());
@@ -439,10 +446,15 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
439 key.renderpass_params = GetRenderPassParams(texceptions); 446 key.renderpass_params = GetRenderPassParams(texceptions);
440 key.padding = 0; 447 key.padding = 0;
441 448
442 auto& pipeline = pipeline_cache.GetGraphicsPipeline(key); 449 auto* pipeline = pipeline_cache.GetGraphicsPipeline(key, async_shaders);
443 scheduler.BindGraphicsPipeline(pipeline.GetHandle()); 450 if (pipeline == nullptr || pipeline->GetHandle() == VK_NULL_HANDLE) {
451 // Async graphics pipeline was not ready.
452 return;
453 }
454
455 scheduler.BindGraphicsPipeline(pipeline->GetHandle());
444 456
445 const auto renderpass = pipeline.GetRenderPass(); 457 const auto renderpass = pipeline->GetRenderPass();
446 const auto [framebuffer, render_area] = ConfigureFramebuffers(renderpass); 458 const auto [framebuffer, render_area] = ConfigureFramebuffers(renderpass);
447 scheduler.RequestRenderpass(renderpass, framebuffer, render_area); 459 scheduler.RequestRenderpass(renderpass, framebuffer, render_area);
448 460
@@ -452,8 +464,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
452 464
453 BeginTransformFeedback(); 465 BeginTransformFeedback();
454 466
455 const auto pipeline_layout = pipeline.GetLayout(); 467 const auto pipeline_layout = pipeline->GetLayout();
456 const auto descriptor_set = pipeline.CommitDescriptorSet(); 468 const auto descriptor_set = pipeline->CommitDescriptorSet();
457 scheduler.Record([pipeline_layout, descriptor_set, draw_params](vk::CommandBuffer cmdbuf) { 469 scheduler.Record([pipeline_layout, descriptor_set, draw_params](vk::CommandBuffer cmdbuf) {
458 if (descriptor_set) { 470 if (descriptor_set) {
459 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 471 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
@@ -463,8 +475,6 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
463 }); 475 });
464 476
465 EndTransformFeedback(); 477 EndTransformFeedback();
466
467 system.GPU().TickWork();
468} 478}
469 479
470void RasterizerVulkan::Clear() { 480void RasterizerVulkan::Clear() {
@@ -1433,10 +1443,10 @@ void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
1433} 1443}
1434 1444
1435void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) { 1445void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) {
1436 if (!state_tracker.TouchPrimitiveTopology()) { 1446 const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
1447 if (!state_tracker.ChangePrimitiveTopology(primitive_topology)) {
1437 return; 1448 return;
1438 } 1449 }
1439 const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
1440 scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) { 1450 scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) {
1441 cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology)); 1451 cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology));
1442 }); 1452 });
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 923178b0b..f640ba649 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -32,6 +32,7 @@
32#include "video_core/renderer_vulkan/vk_texture_cache.h" 32#include "video_core/renderer_vulkan/vk_texture_cache.h"
33#include "video_core/renderer_vulkan/vk_update_descriptor.h" 33#include "video_core/renderer_vulkan/vk_update_descriptor.h"
34#include "video_core/renderer_vulkan/wrapper.h" 34#include "video_core/renderer_vulkan/wrapper.h"
35#include "video_core/shader/async_shaders.h"
35 36
36namespace Core { 37namespace Core {
37class System; 38class System;
@@ -136,6 +137,14 @@ public:
136 u32 pixel_stride) override; 137 u32 pixel_stride) override;
137 void SetupDirtyFlags() override; 138 void SetupDirtyFlags() override;
138 139
140 VideoCommon::Shader::AsyncShaders& GetAsyncShaders() {
141 return async_shaders;
142 }
143
144 const VideoCommon::Shader::AsyncShaders& GetAsyncShaders() const {
145 return async_shaders;
146 }
147
139 /// Maximum supported size that a constbuffer can have in bytes. 148 /// Maximum supported size that a constbuffer can have in bytes.
140 static constexpr std::size_t MaxConstbufferSize = 0x10000; 149 static constexpr std::size_t MaxConstbufferSize = 0x10000;
141 static_assert(MaxConstbufferSize % (4 * sizeof(float)) == 0, 150 static_assert(MaxConstbufferSize % (4 * sizeof(float)) == 0,
@@ -297,6 +306,7 @@ private:
297 vk::Buffer default_buffer; 306 vk::Buffer default_buffer;
298 VKMemoryCommit default_buffer_commit; 307 VKMemoryCommit default_buffer_commit;
299 vk::Event wfi_event; 308 vk::Event wfi_event;
309 VideoCommon::Shader::AsyncShaders async_shaders;
300 310
301 std::array<View, Maxwell::NumRenderTargets> color_attachments; 311 std::array<View, Maxwell::NumRenderTargets> color_attachments;
302 View zeta_attachment; 312 View zeta_attachment;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 9151d9fb1..4bd1009f9 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -42,7 +42,6 @@ Flags MakeInvalidationFlags() {
42 flags[DepthWriteEnable] = true; 42 flags[DepthWriteEnable] = true;
43 flags[DepthCompareOp] = true; 43 flags[DepthCompareOp] = true;
44 flags[FrontFace] = true; 44 flags[FrontFace] = true;
45 flags[PrimitiveTopology] = true;
46 flags[StencilOp] = true; 45 flags[StencilOp] = true;
47 flags[StencilTestEnable] = true; 46 flags[StencilTestEnable] = true;
48 return flags; 47 return flags;
@@ -112,10 +111,6 @@ void SetupDirtyFrontFace(Tables& tables) {
112 table[OFF(screen_y_control)] = FrontFace; 111 table[OFF(screen_y_control)] = FrontFace;
113} 112}
114 113
115void SetupDirtyPrimitiveTopology(Tables& tables) {
116 tables[0][OFF(draw.topology)] = PrimitiveTopology;
117}
118
119void SetupDirtyStencilOp(Tables& tables) { 114void SetupDirtyStencilOp(Tables& tables) {
120 auto& table = tables[0]; 115 auto& table = tables[0];
121 table[OFF(stencil_front_op_fail)] = StencilOp; 116 table[OFF(stencil_front_op_fail)] = StencilOp;
@@ -156,13 +151,13 @@ void StateTracker::Initialize() {
156 SetupDirtyDepthWriteEnable(tables); 151 SetupDirtyDepthWriteEnable(tables);
157 SetupDirtyDepthCompareOp(tables); 152 SetupDirtyDepthCompareOp(tables);
158 SetupDirtyFrontFace(tables); 153 SetupDirtyFrontFace(tables);
159 SetupDirtyPrimitiveTopology(tables);
160 SetupDirtyStencilOp(tables); 154 SetupDirtyStencilOp(tables);
161 SetupDirtyStencilTestEnable(tables); 155 SetupDirtyStencilTestEnable(tables);
162} 156}
163 157
164void StateTracker::InvalidateCommandBufferState() { 158void StateTracker::InvalidateCommandBufferState() {
165 system.GPU().Maxwell3D().dirty.flags |= invalidation_flags; 159 system.GPU().Maxwell3D().dirty.flags |= invalidation_flags;
160 current_topology = INVALID_TOPOLOGY;
166} 161}
167 162
168} // namespace Vulkan 163} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 54ca0d6c6..13a6ce786 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -32,7 +32,6 @@ enum : u8 {
32 DepthWriteEnable, 32 DepthWriteEnable,
33 DepthCompareOp, 33 DepthCompareOp,
34 FrontFace, 34 FrontFace,
35 PrimitiveTopology,
36 StencilOp, 35 StencilOp,
37 StencilTestEnable, 36 StencilTestEnable,
38 37
@@ -43,6 +42,8 @@ static_assert(Last <= std::numeric_limits<u8>::max());
43} // namespace Dirty 42} // namespace Dirty
44 43
45class StateTracker { 44class StateTracker {
45 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
46
46public: 47public:
47 explicit StateTracker(Core::System& system); 48 explicit StateTracker(Core::System& system);
48 49
@@ -102,10 +103,6 @@ public:
102 return Exchange(Dirty::FrontFace, false); 103 return Exchange(Dirty::FrontFace, false);
103 } 104 }
104 105
105 bool TouchPrimitiveTopology() {
106 return Exchange(Dirty::PrimitiveTopology, false);
107 }
108
109 bool TouchStencilOp() { 106 bool TouchStencilOp() {
110 return Exchange(Dirty::StencilOp, false); 107 return Exchange(Dirty::StencilOp, false);
111 } 108 }
@@ -114,7 +111,15 @@ public:
114 return Exchange(Dirty::StencilTestEnable, false); 111 return Exchange(Dirty::StencilTestEnable, false);
115 } 112 }
116 113
114 bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) {
115 const bool has_changed = current_topology != new_topology;
116 current_topology = new_topology;
117 return has_changed;
118 }
119
117private: 120private:
121 static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u);
122
118 bool Exchange(std::size_t id, bool new_value) const noexcept { 123 bool Exchange(std::size_t id, bool new_value) const noexcept {
119 auto& flags = system.GPU().Maxwell3D().dirty.flags; 124 auto& flags = system.GPU().Maxwell3D().dirty.flags;
120 const bool is_dirty = flags[id]; 125 const bool is_dirty = flags[id];
@@ -124,6 +129,7 @@ private:
124 129
125 Core::System& system; 130 Core::System& system;
126 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags; 131 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
132 Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY;
127}; 133};
128 134
129} // namespace Vulkan 135} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index 14cac38ea..013865aa4 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/renderer_vulkan/wrapper.cpp
@@ -644,7 +644,7 @@ ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) cons
644 return ShaderModule(object, handle, *dld); 644 return ShaderModule(object, handle, *dld);
645} 645}
646 646
647Event Device::CreateEvent() const { 647Event Device::CreateNewEvent() const {
648 static constexpr VkEventCreateInfo ci{ 648 static constexpr VkEventCreateInfo ci{
649 .sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO, 649 .sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO,
650 .pNext = nullptr, 650 .pNext = nullptr,
@@ -786,7 +786,7 @@ std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProp
786 VK_SUCCESS) { 786 VK_SUCCESS) {
787 return std::nullopt; 787 return std::nullopt;
788 } 788 }
789 return properties; 789 return std::move(properties);
790} 790}
791 791
792std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties( 792std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties(
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h
index 71daac9d7..b9d3fedc1 100644
--- a/src/video_core/renderer_vulkan/wrapper.h
+++ b/src/video_core/renderer_vulkan/wrapper.h
@@ -721,7 +721,7 @@ public:
721 721
722 ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const; 722 ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
723 723
724 Event CreateEvent() const; 724 Event CreateNewEvent() const;
725 725
726 SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const; 726 SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
727 727
@@ -756,8 +756,8 @@ public:
756 } 756 }
757 757
758 VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, 758 VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size,
759 void* data, VkDeviceSize stride, VkQueryResultFlags flags) const 759 void* data, VkDeviceSize stride,
760 noexcept { 760 VkQueryResultFlags flags) const noexcept {
761 return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride, 761 return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
762 flags); 762 flags);
763 } 763 }
@@ -849,8 +849,8 @@ public:
849 dld->vkCmdBindPipeline(handle, bind_point, pipeline); 849 dld->vkCmdBindPipeline(handle, bind_point, pipeline);
850 } 850 }
851 851
852 void BindIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType index_type) const 852 void BindIndexBuffer(VkBuffer buffer, VkDeviceSize offset,
853 noexcept { 853 VkIndexType index_type) const noexcept {
854 dld->vkCmdBindIndexBuffer(handle, buffer, offset, index_type); 854 dld->vkCmdBindIndexBuffer(handle, buffer, offset, index_type);
855 } 855 }
856 856
@@ -863,8 +863,8 @@ public:
863 BindVertexBuffers(binding, 1, &buffer, &offset); 863 BindVertexBuffers(binding, 1, &buffer, &offset);
864 } 864 }
865 865
866 void Draw(u32 vertex_count, u32 instance_count, u32 first_vertex, u32 first_instance) const 866 void Draw(u32 vertex_count, u32 instance_count, u32 first_vertex,
867 noexcept { 867 u32 first_instance) const noexcept {
868 dld->vkCmdDraw(handle, vertex_count, instance_count, first_vertex, first_instance); 868 dld->vkCmdDraw(handle, vertex_count, instance_count, first_vertex, first_instance);
869 } 869 }
870 870
@@ -874,15 +874,15 @@ public:
874 first_instance); 874 first_instance);
875 } 875 }
876 876
877 void ClearAttachments(Span<VkClearAttachment> attachments, Span<VkClearRect> rects) const 877 void ClearAttachments(Span<VkClearAttachment> attachments,
878 noexcept { 878 Span<VkClearRect> rects) const noexcept {
879 dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(), 879 dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(),
880 rects.data()); 880 rects.data());
881 } 881 }
882 882
883 void BlitImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image, 883 void BlitImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image,
884 VkImageLayout dst_layout, Span<VkImageBlit> regions, VkFilter filter) const 884 VkImageLayout dst_layout, Span<VkImageBlit> regions,
885 noexcept { 885 VkFilter filter) const noexcept {
886 dld->vkCmdBlitImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(), 886 dld->vkCmdBlitImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(),
887 regions.data(), filter); 887 regions.data(), filter);
888 } 888 }
@@ -907,8 +907,8 @@ public:
907 regions.data()); 907 regions.data());
908 } 908 }
909 909
910 void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, Span<VkBufferCopy> regions) const 910 void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer,
911 noexcept { 911 Span<VkBufferCopy> regions) const noexcept {
912 dld->vkCmdCopyBuffer(handle, src_buffer, dst_buffer, regions.size(), regions.data()); 912 dld->vkCmdCopyBuffer(handle, src_buffer, dst_buffer, regions.size(), regions.data());
913 } 913 }
914 914
@@ -924,8 +924,8 @@ public:
924 regions.data()); 924 regions.data());
925 } 925 }
926 926
927 void FillBuffer(VkBuffer dst_buffer, VkDeviceSize dst_offset, VkDeviceSize size, u32 data) const 927 void FillBuffer(VkBuffer dst_buffer, VkDeviceSize dst_offset, VkDeviceSize size,
928 noexcept { 928 u32 data) const noexcept {
929 dld->vkCmdFillBuffer(handle, dst_buffer, dst_offset, size, data); 929 dld->vkCmdFillBuffer(handle, dst_buffer, dst_offset, size, data);
930 } 930 }
931 931
diff --git a/src/video_core/shader/async_shaders.cpp b/src/video_core/shader/async_shaders.cpp
index b7f66d7ee..f815584f7 100644
--- a/src/video_core/shader/async_shaders.cpp
+++ b/src/video_core/shader/async_shaders.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono>
6#include <condition_variable> 5#include <condition_variable>
7#include <mutex> 6#include <mutex>
8#include <thread> 7#include <thread>
@@ -20,9 +19,18 @@ AsyncShaders::~AsyncShaders() {
20 KillWorkers(); 19 KillWorkers();
21} 20}
22 21
23void AsyncShaders::AllocateWorkers(std::size_t num_workers) { 22void AsyncShaders::AllocateWorkers() {
24 // If we're already have workers queued or don't want to queue workers, ignore 23 // Max worker threads we should allow
25 if (num_workers == worker_threads.size() || num_workers == 0) { 24 constexpr u32 MAX_THREADS = 4;
25 // Deduce how many threads we can use
26 const u32 threads_used = std::thread::hardware_concurrency() / 4;
27 // Always allow at least 1 thread regardless of our settings
28 const auto max_worker_count = std::max(1U, threads_used);
29 // Don't use more than MAX_THREADS
30 const auto num_workers = std::min(max_worker_count, MAX_THREADS);
31
32 // If we already have workers queued, ignore
33 if (num_workers == worker_threads.size()) {
26 return; 34 return;
27 } 35 }
28 36
@@ -34,8 +42,8 @@ void AsyncShaders::AllocateWorkers(std::size_t num_workers) {
34 // Create workers 42 // Create workers
35 for (std::size_t i = 0; i < num_workers; i++) { 43 for (std::size_t i = 0; i < num_workers; i++) {
36 context_list.push_back(emu_window.CreateSharedContext()); 44 context_list.push_back(emu_window.CreateSharedContext());
37 worker_threads.push_back(std::move( 45 worker_threads.push_back(
38 std::thread(&AsyncShaders::ShaderCompilerThread, this, context_list[i].get()))); 46 std::thread(&AsyncShaders::ShaderCompilerThread, this, context_list[i].get()));
39 } 47 }
40} 48}
41 49
@@ -111,24 +119,50 @@ void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
111 VideoCommon::Shader::CompilerSettings compiler_settings, 119 VideoCommon::Shader::CompilerSettings compiler_settings,
112 const VideoCommon::Shader::Registry& registry, 120 const VideoCommon::Shader::Registry& registry,
113 VAddr cpu_addr) { 121 VAddr cpu_addr) {
114 WorkerParams params{device.UseAssemblyShaders() ? AsyncShaders::Backend::GLASM 122 WorkerParams params{
115 : AsyncShaders::Backend::OpenGL, 123 .backend = device.UseAssemblyShaders() ? Backend::GLASM : Backend::OpenGL,
116 device, 124 .device = &device,
117 shader_type, 125 .shader_type = shader_type,
118 uid, 126 .uid = uid,
119 std::move(code), 127 .code = std::move(code),
120 std::move(code_b), 128 .code_b = std::move(code_b),
121 main_offset, 129 .main_offset = main_offset,
122 compiler_settings, 130 .compiler_settings = compiler_settings,
123 registry, 131 .registry = registry,
124 cpu_addr}; 132 .cpu_address = cpu_addr,
133 };
125 std::unique_lock lock(queue_mutex); 134 std::unique_lock lock(queue_mutex);
126 pending_queue.push_back(std::move(params)); 135 pending_queue.push(std::move(params));
136 cv.notify_one();
137}
138
139void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
140 const Vulkan::VKDevice& device, Vulkan::VKScheduler& scheduler,
141 Vulkan::VKDescriptorPool& descriptor_pool,
142 Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue,
143 Vulkan::VKRenderPassCache& renderpass_cache,
144 std::vector<VkDescriptorSetLayoutBinding> bindings,
145 Vulkan::SPIRVProgram program,
146 Vulkan::GraphicsPipelineCacheKey key) {
147 WorkerParams params{
148 .backend = Backend::Vulkan,
149 .pp_cache = pp_cache,
150 .vk_device = &device,
151 .scheduler = &scheduler,
152 .descriptor_pool = &descriptor_pool,
153 .update_descriptor_queue = &update_descriptor_queue,
154 .renderpass_cache = &renderpass_cache,
155 .bindings = bindings,
156 .program = program,
157 .key = key,
158 };
159
160 std::unique_lock lock(queue_mutex);
161 pending_queue.push(std::move(params));
127 cv.notify_one(); 162 cv.notify_one();
128} 163}
129 164
130void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context) { 165void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context) {
131 using namespace std::chrono_literals;
132 while (!is_thread_exiting.load(std::memory_order_relaxed)) { 166 while (!is_thread_exiting.load(std::memory_order_relaxed)) {
133 std::unique_lock lock{queue_mutex}; 167 std::unique_lock lock{queue_mutex};
134 cv.wait(lock, [this] { return HasWorkQueued() || is_thread_exiting; }); 168 cv.wait(lock, [this] { return HasWorkQueued() || is_thread_exiting; });
@@ -144,18 +178,17 @@ void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context
144 if (pending_queue.empty()) { 178 if (pending_queue.empty()) {
145 continue; 179 continue;
146 } 180 }
181
147 // Pull work from queue 182 // Pull work from queue
148 WorkerParams work = std::move(pending_queue.front()); 183 WorkerParams work = std::move(pending_queue.front());
149 pending_queue.pop_front(); 184 pending_queue.pop();
150
151 lock.unlock(); 185 lock.unlock();
152 186
153 if (work.backend == AsyncShaders::Backend::OpenGL || 187 if (work.backend == Backend::OpenGL || work.backend == Backend::GLASM) {
154 work.backend == AsyncShaders::Backend::GLASM) { 188 const ShaderIR ir(work.code, work.main_offset, work.compiler_settings, *work.registry);
155 const ShaderIR ir(work.code, work.main_offset, work.compiler_settings, work.registry);
156 const auto scope = context->Acquire(); 189 const auto scope = context->Acquire();
157 auto program = 190 auto program =
158 OpenGL::BuildShader(work.device, work.shader_type, work.uid, ir, work.registry); 191 OpenGL::BuildShader(*work.device, work.shader_type, work.uid, ir, *work.registry);
159 Result result{}; 192 Result result{};
160 result.backend = work.backend; 193 result.backend = work.backend;
161 result.cpu_address = work.cpu_address; 194 result.cpu_address = work.cpu_address;
@@ -164,9 +197,9 @@ void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context
164 result.code_b = std::move(work.code_b); 197 result.code_b = std::move(work.code_b);
165 result.shader_type = work.shader_type; 198 result.shader_type = work.shader_type;
166 199
167 if (work.backend == AsyncShaders::Backend::OpenGL) { 200 if (work.backend == Backend::OpenGL) {
168 result.program.opengl = std::move(program->source_program); 201 result.program.opengl = std::move(program->source_program);
169 } else if (work.backend == AsyncShaders::Backend::GLASM) { 202 } else if (work.backend == Backend::GLASM) {
170 result.program.glasm = std::move(program->assembly_program); 203 result.program.glasm = std::move(program->assembly_program);
171 } 204 }
172 205
@@ -174,6 +207,13 @@ void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context
174 std::unique_lock complete_lock(completed_mutex); 207 std::unique_lock complete_lock(completed_mutex);
175 finished_work.push_back(std::move(result)); 208 finished_work.push_back(std::move(result));
176 } 209 }
210 } else if (work.backend == Backend::Vulkan) {
211 auto pipeline = std::make_unique<Vulkan::VKGraphicsPipeline>(
212 *work.vk_device, *work.scheduler, *work.descriptor_pool,
213 *work.update_descriptor_queue, *work.renderpass_cache, work.key, work.bindings,
214 work.program);
215
216 work.pp_cache->EmplacePipeline(std::move(pipeline));
177 } 217 }
178 } 218 }
179} 219}
diff --git a/src/video_core/shader/async_shaders.h b/src/video_core/shader/async_shaders.h
index 2f5ee94ad..d5ae814d5 100644
--- a/src/video_core/shader/async_shaders.h
+++ b/src/video_core/shader/async_shaders.h
@@ -14,6 +14,10 @@
14#include "video_core/renderer_opengl/gl_device.h" 14#include "video_core/renderer_opengl/gl_device.h"
15#include "video_core/renderer_opengl/gl_resource_manager.h" 15#include "video_core/renderer_opengl/gl_resource_manager.h"
16#include "video_core/renderer_opengl/gl_shader_decompiler.h" 16#include "video_core/renderer_opengl/gl_shader_decompiler.h"
17#include "video_core/renderer_vulkan/vk_device.h"
18#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
19#include "video_core/renderer_vulkan/vk_scheduler.h"
20#include "video_core/renderer_vulkan/vk_update_descriptor.h"
17 21
18namespace Core::Frontend { 22namespace Core::Frontend {
19class EmuWindow; 23class EmuWindow;
@@ -24,6 +28,10 @@ namespace Tegra {
24class GPU; 28class GPU;
25} 29}
26 30
31namespace Vulkan {
32class VKPipelineCache;
33}
34
27namespace VideoCommon::Shader { 35namespace VideoCommon::Shader {
28 36
29class AsyncShaders { 37class AsyncShaders {
@@ -31,6 +39,7 @@ public:
31 enum class Backend { 39 enum class Backend {
32 OpenGL, 40 OpenGL,
33 GLASM, 41 GLASM,
42 Vulkan,
34 }; 43 };
35 44
36 struct ResultPrograms { 45 struct ResultPrograms {
@@ -52,7 +61,7 @@ public:
52 ~AsyncShaders(); 61 ~AsyncShaders();
53 62
54 /// Start up shader worker threads 63 /// Start up shader worker threads
55 void AllocateWorkers(std::size_t num_workers); 64 void AllocateWorkers();
56 65
57 /// Clear the shader queue and kill all worker threads 66 /// Clear the shader queue and kill all worker threads
58 void FreeWorkers(); 67 void FreeWorkers();
@@ -76,6 +85,14 @@ public:
76 VideoCommon::Shader::CompilerSettings compiler_settings, 85 VideoCommon::Shader::CompilerSettings compiler_settings,
77 const VideoCommon::Shader::Registry& registry, VAddr cpu_addr); 86 const VideoCommon::Shader::Registry& registry, VAddr cpu_addr);
78 87
88 void QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, const Vulkan::VKDevice& device,
89 Vulkan::VKScheduler& scheduler,
90 Vulkan::VKDescriptorPool& descriptor_pool,
91 Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue,
92 Vulkan::VKRenderPassCache& renderpass_cache,
93 std::vector<VkDescriptorSetLayoutBinding> bindings,
94 Vulkan::SPIRVProgram program, Vulkan::GraphicsPipelineCacheKey key);
95
79private: 96private:
80 void ShaderCompilerThread(Core::Frontend::GraphicsContext* context); 97 void ShaderCompilerThread(Core::Frontend::GraphicsContext* context);
81 98
@@ -83,16 +100,28 @@ private:
83 bool HasWorkQueued(); 100 bool HasWorkQueued();
84 101
85 struct WorkerParams { 102 struct WorkerParams {
86 AsyncShaders::Backend backend; 103 Backend backend;
87 OpenGL::Device device; 104 // For OGL
105 const OpenGL::Device* device;
88 Tegra::Engines::ShaderType shader_type; 106 Tegra::Engines::ShaderType shader_type;
89 u64 uid; 107 u64 uid;
90 std::vector<u64> code; 108 std::vector<u64> code;
91 std::vector<u64> code_b; 109 std::vector<u64> code_b;
92 u32 main_offset; 110 u32 main_offset;
93 VideoCommon::Shader::CompilerSettings compiler_settings; 111 VideoCommon::Shader::CompilerSettings compiler_settings;
94 VideoCommon::Shader::Registry registry; 112 std::optional<VideoCommon::Shader::Registry> registry;
95 VAddr cpu_address; 113 VAddr cpu_address;
114
115 // For Vulkan
116 Vulkan::VKPipelineCache* pp_cache;
117 const Vulkan::VKDevice* vk_device;
118 Vulkan::VKScheduler* scheduler;
119 Vulkan::VKDescriptorPool* descriptor_pool;
120 Vulkan::VKUpdateDescriptorQueue* update_descriptor_queue;
121 Vulkan::VKRenderPassCache* renderpass_cache;
122 std::vector<VkDescriptorSetLayoutBinding> bindings;
123 Vulkan::SPIRVProgram program;
124 Vulkan::GraphicsPipelineCacheKey key;
96 }; 125 };
97 126
98 std::condition_variable cv; 127 std::condition_variable cv;
@@ -101,7 +130,7 @@ private:
101 std::atomic<bool> is_thread_exiting{}; 130 std::atomic<bool> is_thread_exiting{};
102 std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> context_list; 131 std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> context_list;
103 std::vector<std::thread> worker_threads; 132 std::vector<std::thread> worker_threads;
104 std::deque<WorkerParams> pending_queue; 133 std::queue<WorkerParams> pending_queue;
105 std::vector<AsyncShaders::Result> finished_work; 134 std::vector<AsyncShaders::Result> finished_work;
106 Core::Frontend::EmuWindow& emu_window; 135 Core::Frontend::EmuWindow& emu_window;
107}; 136};
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp
index 8d86020f6..336397cdb 100644
--- a/src/video_core/shader/control_flow.cpp
+++ b/src/video_core/shader/control_flow.cpp
@@ -187,24 +187,26 @@ std::optional<std::pair<BufferInfo, u64>> TrackLDC(const CFGRebuildState& state,
187 187
188std::optional<u64> TrackSHLRegister(const CFGRebuildState& state, u32& pos, 188std::optional<u64> TrackSHLRegister(const CFGRebuildState& state, u32& pos,
189 u64 ldc_tracked_register) { 189 u64 ldc_tracked_register) {
190 return TrackInstruction<u64>(state, pos, 190 return TrackInstruction<u64>(
191 [ldc_tracked_register](auto instr, const auto& opcode) { 191 state, pos,
192 return opcode.GetId() == OpCode::Id::SHL_IMM && 192 [ldc_tracked_register](auto instr, const auto& opcode) {
193 instr.gpr0.Value() == ldc_tracked_register; 193 return opcode.GetId() == OpCode::Id::SHL_IMM &&
194 }, 194 instr.gpr0.Value() == ldc_tracked_register;
195 [](auto instr, const auto&) { return instr.gpr8.Value(); }); 195 },
196 [](auto instr, const auto&) { return instr.gpr8.Value(); });
196} 197}
197 198
198std::optional<u32> TrackIMNMXValue(const CFGRebuildState& state, u32& pos, 199std::optional<u32> TrackIMNMXValue(const CFGRebuildState& state, u32& pos,
199 u64 shl_tracked_register) { 200 u64 shl_tracked_register) {
200 return TrackInstruction<u32>(state, pos, 201 return TrackInstruction<u32>(
201 [shl_tracked_register](auto instr, const auto& opcode) { 202 state, pos,
202 return opcode.GetId() == OpCode::Id::IMNMX_IMM && 203 [shl_tracked_register](auto instr, const auto& opcode) {
203 instr.gpr0.Value() == shl_tracked_register; 204 return opcode.GetId() == OpCode::Id::IMNMX_IMM &&
204 }, 205 instr.gpr0.Value() == shl_tracked_register;
205 [](auto instr, const auto&) { 206 },
206 return static_cast<u32>(instr.alu.GetSignedImm20_20() + 1); 207 [](auto instr, const auto&) {
207 }); 208 return static_cast<u32>(instr.alu.GetSignedImm20_20() + 1);
209 });
208} 210}
209 211
210std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& state, u32 pos) { 212std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& state, u32 pos) {
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index 63adbc4a3..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) {
386 break; 386 break;
387 } 387 }
388 case OpCode::Id::RED: { 388 case OpCode::Id::RED: {
389 UNIMPLEMENTED_IF_MSG(instr.red.type != GlobalAtomicType::U32); 389 UNIMPLEMENTED_IF_MSG(instr.red.type != GlobalAtomicType::U32, "type={}",
390 static_cast<int>(instr.red.type.Value()));
390 const auto [real_address, base_address, descriptor] = 391 const auto [real_address, base_address, descriptor] =
391 TrackGlobalMemory(bb, instr, true, true); 392 TrackGlobalMemory(bb, instr, true, true);
392 if (!real_address || !base_address) { 393 if (!real_address || !base_address) {
@@ -471,9 +472,9 @@ std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackGlobalMemory(NodeBlock&
471 472
472 const auto [base_address, index, offset] = 473 const auto [base_address, index, offset] =
473 TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size())); 474 TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()));
474 ASSERT_OR_EXECUTE_MSG(base_address != nullptr, 475 ASSERT_OR_EXECUTE_MSG(
475 { return std::make_tuple(nullptr, nullptr, GlobalMemoryBase{}); }, 476 base_address != nullptr, { return std::make_tuple(nullptr, nullptr, GlobalMemoryBase{}); },
476 "Global memory tracking failed"); 477 "Global memory tracking failed");
477 478
478 bb.push_back(Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", index, offset))); 479 bb.push_back(Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", index, offset)));
479 480
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
index 9a98f0e98..e614a92df 100644
--- a/src/video_core/texture_cache/surface_params.cpp
+++ b/src/video_core/texture_cache/surface_params.cpp
@@ -96,7 +96,6 @@ SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_ta
96 } 96 }
97 params.type = GetFormatType(params.pixel_format); 97 params.type = GetFormatType(params.pixel_format);
98 } 98 }
99 params.type = GetFormatType(params.pixel_format);
100 // TODO: on 1DBuffer we should use the tic info. 99 // TODO: on 1DBuffer we should use the tic info.
101 if (tic.IsBuffer()) { 100 if (tic.IsBuffer()) {
102 params.target = SurfaceTarget::TextureBuffer; 101 params.target = SurfaceTarget::TextureBuffer;
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 474ae620a..16d46a018 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -228,24 +228,30 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
228 } 228 }
229} 229}
230 230
231void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, 231void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel,
232 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, 232 u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) {
233 u32 block_height_bit, u32 offset_x, u32 offset_y) { 233 const u32 stride = width * bytes_per_pixel;
234 const u32 block_height = 1U << block_height_bit; 234 const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
235 for (u32 line = 0; line < subrect_height; ++line) { 235 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height);
236 const u32 y2 = line + offset_y; 236
237 const u32 gob_address_y = (y2 / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height + 237 const u32 block_height_mask = (1U << block_height) - 1;
238 ((y2 % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE; 238 const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height;
239 const auto& table = LEGACY_SWIZZLE_TABLE[y2 % GOB_SIZE_Y]; 239
240 for (u32 x = 0; x < subrect_width; ++x) { 240 for (u32 line = 0; line < line_count; ++line) {
241 const u32 x2 = (x + offset_x) * bytes_per_pixel; 241 const u32 src_y = line + origin_y;
242 const u32 gob_address = gob_address_y + (x2 / GOB_SIZE_X) * GOB_SIZE * block_height; 242 const auto& table = LEGACY_SWIZZLE_TABLE[src_y % GOB_SIZE_Y];
243 const u32 swizzled_offset = gob_address + table[x2 % GOB_SIZE_X]; 243
244 const u32 unswizzled_offset = line * dest_pitch + x * bytes_per_pixel; 244 const u32 block_y = src_y >> GOB_SIZE_Y_SHIFT;
245 u8* dest_line = unswizzled_data + unswizzled_offset; 245 const u32 src_offset_y = (block_y >> block_height) * block_size +
246 u8* source_addr = swizzled_data + swizzled_offset; 246 ((block_y & block_height_mask) << GOB_SIZE_SHIFT);
247 for (u32 column = 0; column < line_length_in; ++column) {
248 const u32 src_x = (column + origin_x) * bytes_per_pixel;
249 const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift;
250
251 const u32 swizzled_offset = src_offset_y + src_offset_x + table[src_x % GOB_SIZE_X];
252 const u32 unswizzled_offset = line * pitch + column * bytes_per_pixel;
247 253
248 std::memcpy(dest_line, source_addr, bytes_per_pixel); 254 std::memcpy(output + unswizzled_offset, input + swizzled_offset, bytes_per_pixel);
249 } 255 }
250 } 256 }
251} 257}
@@ -261,7 +267,7 @@ void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 widt
261 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); 267 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
262 268
263 const u32 block_height_mask = (1U << block_height) - 1; 269 const u32 block_height_mask = (1U << block_height) - 1;
264 const u32 x_shift = Common::CountTrailingZeroes32(GOB_SIZE << (block_height + block_depth)); 270 const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height + block_depth;
265 271
266 for (u32 line = 0; line < line_count; ++line) { 272 for (u32 line = 0; line < line_count; ++line) {
267 const auto& table = LEGACY_SWIZZLE_TABLE[line % GOB_SIZE_Y]; 273 const auto& table = LEGACY_SWIZZLE_TABLE[line % GOB_SIZE_Y];
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index d6fe35d37..01e156bc8 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -48,9 +48,8 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
48 u32 block_height_bit, u32 offset_x, u32 offset_y); 48 u32 block_height_bit, u32 offset_x, u32 offset_y);
49 49
50/// Copies a tiled subrectangle into a linear surface. 50/// Copies a tiled subrectangle into a linear surface.
51void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, 51void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel,
52 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height, 52 u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input);
53 u32 offset_x, u32 offset_y);
54 53
55/// @brief Swizzles a 2D array of pixels into a 3D texture 54/// @brief Swizzles a 2D array of pixels into a 3D texture
56/// @param line_length_in Number of pixels per line 55/// @param line_length_in Number of pixels per line
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 45f360bdd..4e3a092c7 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6
6#include "common/logging/log.h" 7#include "common/logging/log.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/settings.h" 9#include "core/settings.h"
@@ -16,37 +17,46 @@
16#include "video_core/video_core.h" 17#include "video_core/video_core.h"
17 18
18namespace { 19namespace {
19std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, 20
20 Core::System& system, 21std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
21 Core::Frontend::GraphicsContext& context) { 22 Core::System& system, Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
23 std::unique_ptr<Core::Frontend::GraphicsContext> context) {
22 switch (Settings::values.renderer_backend.GetValue()) { 24 switch (Settings::values.renderer_backend.GetValue()) {
23 case Settings::RendererBackend::OpenGL: 25 case Settings::RendererBackend::OpenGL:
24 return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context); 26 return std::make_unique<OpenGL::RendererOpenGL>(system, emu_window, gpu,
27 std::move(context));
25#ifdef HAS_VULKAN 28#ifdef HAS_VULKAN
26 case Settings::RendererBackend::Vulkan: 29 case Settings::RendererBackend::Vulkan:
27 return std::make_unique<Vulkan::RendererVulkan>(emu_window, system); 30 return std::make_unique<Vulkan::RendererVulkan>(system, emu_window, gpu,
31 std::move(context));
28#endif 32#endif
29 default: 33 default:
30 return nullptr; 34 return nullptr;
31 } 35 }
32} 36}
37
33} // Anonymous namespace 38} // Anonymous namespace
34 39
35namespace VideoCore { 40namespace VideoCore {
36 41
37std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { 42std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
43 std::unique_ptr<Tegra::GPU> gpu;
44 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
45 gpu = std::make_unique<VideoCommon::GPUAsynch>(system);
46 } else {
47 gpu = std::make_unique<VideoCommon::GPUSynch>(system);
48 }
49
38 auto context = emu_window.CreateSharedContext(); 50 auto context = emu_window.CreateSharedContext();
39 const auto scope = context->Acquire(); 51 const auto scope = context->Acquire();
40 auto renderer = CreateRenderer(emu_window, system, *context); 52
53 auto renderer = CreateRenderer(system, emu_window, *gpu, std::move(context));
41 if (!renderer->Init()) { 54 if (!renderer->Init()) {
42 return nullptr; 55 return nullptr;
43 } 56 }
44 57
45 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) { 58 gpu->BindRenderer(std::move(renderer));
46 return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer), 59 return gpu;
47 std::move(context));
48 }
49 return std::make_unique<VideoCommon::GPUSynch>(system, std::move(renderer), std::move(context));
50} 60}
51 61
52u16 GetResolutionScaleFactor(const RendererBase& renderer) { 62u16 GetResolutionScaleFactor(const RendererBase& renderer) {
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt
index 06ab7c59d..7e484b906 100644
--- a/src/web_service/CMakeLists.txt
+++ b/src/web_service/CMakeLists.txt
@@ -5,6 +5,7 @@ add_library(web_service STATIC
5 verify_login.h 5 verify_login.h
6 web_backend.cpp 6 web_backend.cpp
7 web_backend.h 7 web_backend.h
8 web_result.h
8) 9)
9 10
10create_target_directory_groups(web_service) 11create_target_directory_groups(web_service)
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index 7a480e33c..6215c914f 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -4,12 +4,14 @@
4 4
5#include <nlohmann/json.hpp> 5#include <nlohmann/json.hpp>
6#include "common/detached_tasks.h" 6#include "common/detached_tasks.h"
7#include "common/web_result.h"
8#include "web_service/telemetry_json.h" 7#include "web_service/telemetry_json.h"
9#include "web_service/web_backend.h" 8#include "web_service/web_backend.h"
9#include "web_service/web_result.h"
10 10
11namespace WebService { 11namespace WebService {
12 12
13namespace Telemetry = Common::Telemetry;
14
13struct TelemetryJson::Impl { 15struct TelemetryJson::Impl {
14 Impl(std::string host, std::string username, std::string token) 16 Impl(std::string host, std::string username, std::string token)
15 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {} 17 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {}
@@ -123,7 +125,7 @@ bool TelemetryJson::SubmitTestcase() {
123 Client client(impl->host, impl->username, impl->token); 125 Client client(impl->host, impl->username, impl->token);
124 auto value = client.PostJson("/gamedb/testcase", content, false); 126 auto value = client.PostJson("/gamedb/testcase", content, false);
125 127
126 return value.result_code == Common::WebResult::Code::Success; 128 return value.result_code == WebResult::Code::Success;
127} 129}
128 130
129} // namespace WebService 131} // namespace WebService
diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h
index dfd202829..df51e00f8 100644
--- a/src/web_service/telemetry_json.h
+++ b/src/web_service/telemetry_json.h
@@ -14,25 +14,25 @@ namespace WebService {
14 * Implementation of VisitorInterface that serialized telemetry into JSON, and submits it to the 14 * Implementation of VisitorInterface that serialized telemetry into JSON, and submits it to the
15 * yuzu web service 15 * yuzu web service
16 */ 16 */
17class TelemetryJson : public Telemetry::VisitorInterface { 17class TelemetryJson : public Common::Telemetry::VisitorInterface {
18public: 18public:
19 TelemetryJson(std::string host, std::string username, std::string token); 19 TelemetryJson(std::string host, std::string username, std::string token);
20 ~TelemetryJson() override; 20 ~TelemetryJson() override;
21 21
22 void Visit(const Telemetry::Field<bool>& field) override; 22 void Visit(const Common::Telemetry::Field<bool>& field) override;
23 void Visit(const Telemetry::Field<double>& field) override; 23 void Visit(const Common::Telemetry::Field<double>& field) override;
24 void Visit(const Telemetry::Field<float>& field) override; 24 void Visit(const Common::Telemetry::Field<float>& field) override;
25 void Visit(const Telemetry::Field<u8>& field) override; 25 void Visit(const Common::Telemetry::Field<u8>& field) override;
26 void Visit(const Telemetry::Field<u16>& field) override; 26 void Visit(const Common::Telemetry::Field<u16>& field) override;
27 void Visit(const Telemetry::Field<u32>& field) override; 27 void Visit(const Common::Telemetry::Field<u32>& field) override;
28 void Visit(const Telemetry::Field<u64>& field) override; 28 void Visit(const Common::Telemetry::Field<u64>& field) override;
29 void Visit(const Telemetry::Field<s8>& field) override; 29 void Visit(const Common::Telemetry::Field<s8>& field) override;
30 void Visit(const Telemetry::Field<s16>& field) override; 30 void Visit(const Common::Telemetry::Field<s16>& field) override;
31 void Visit(const Telemetry::Field<s32>& field) override; 31 void Visit(const Common::Telemetry::Field<s32>& field) override;
32 void Visit(const Telemetry::Field<s64>& field) override; 32 void Visit(const Common::Telemetry::Field<s64>& field) override;
33 void Visit(const Telemetry::Field<std::string>& field) override; 33 void Visit(const Common::Telemetry::Field<std::string>& field) override;
34 void Visit(const Telemetry::Field<const char*>& field) override; 34 void Visit(const Common::Telemetry::Field<const char*>& field) override;
35 void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override; 35 void Visit(const Common::Telemetry::Field<std::chrono::microseconds>& field) override;
36 36
37 void Complete() override; 37 void Complete() override;
38 bool SubmitTestcase() override; 38 bool SubmitTestcase() override;
diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp
index bfaa5b70a..ceb55ca6b 100644
--- a/src/web_service/verify_login.cpp
+++ b/src/web_service/verify_login.cpp
@@ -3,9 +3,9 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <nlohmann/json.hpp> 5#include <nlohmann/json.hpp>
6#include "common/web_result.h"
7#include "web_service/verify_login.h" 6#include "web_service/verify_login.h"
8#include "web_service/web_backend.h" 7#include "web_service/web_backend.h"
8#include "web_service/web_result.h"
9 9
10namespace WebService { 10namespace WebService {
11 11
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 09d1651ac..74e287045 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -6,13 +6,14 @@
6#include <cstdlib> 6#include <cstdlib>
7#include <mutex> 7#include <mutex>
8#include <string> 8#include <string>
9
9#include <LUrlParser.h> 10#include <LUrlParser.h>
10#include <fmt/format.h> 11#include <fmt/format.h>
11#include <httplib.h> 12#include <httplib.h>
12#include "common/common_types.h" 13
13#include "common/logging/log.h" 14#include "common/logging/log.h"
14#include "common/web_result.h"
15#include "web_service/web_backend.h" 15#include "web_service/web_backend.h"
16#include "web_service/web_result.h"
16 17
17namespace WebService { 18namespace WebService {
18 19
@@ -33,17 +34,16 @@ struct Client::Impl {
33 } 34 }
34 35
35 /// A generic function handles POST, GET and DELETE request together 36 /// A generic function handles POST, GET and DELETE request together
36 Common::WebResult GenericRequest(const std::string& method, const std::string& path, 37 WebResult GenericRequest(const std::string& method, const std::string& path,
37 const std::string& data, bool allow_anonymous, 38 const std::string& data, bool allow_anonymous,
38 const std::string& accept) { 39 const std::string& accept) {
39 if (jwt.empty()) { 40 if (jwt.empty()) {
40 UpdateJWT(); 41 UpdateJWT();
41 } 42 }
42 43
43 if (jwt.empty() && !allow_anonymous) { 44 if (jwt.empty() && !allow_anonymous) {
44 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); 45 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
45 return Common::WebResult{Common::WebResult::Code::CredentialsMissing, 46 return WebResult{WebResult::Code::CredentialsMissing, "Credentials needed", ""};
46 "Credentials needed", ""};
47 } 47 }
48 48
49 auto result = GenericRequest(method, path, data, accept, jwt); 49 auto result = GenericRequest(method, path, data, accept, jwt);
@@ -62,10 +62,10 @@ struct Client::Impl {
62 * username + token is used if jwt is empty but username and token are 62 * username + token is used if jwt is empty but username and token are
63 * not empty anonymous if all of jwt, username and token are empty 63 * not empty anonymous if all of jwt, username and token are empty
64 */ 64 */
65 Common::WebResult GenericRequest(const std::string& method, const std::string& path, 65 WebResult GenericRequest(const std::string& method, const std::string& path,
66 const std::string& data, const std::string& accept, 66 const std::string& data, const std::string& accept,
67 const std::string& jwt = "", const std::string& username = "", 67 const std::string& jwt = "", const std::string& username = "",
68 const std::string& token = "") { 68 const std::string& token = "") {
69 if (cli == nullptr) { 69 if (cli == nullptr) {
70 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); 70 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
71 int port; 71 int port;
@@ -81,12 +81,12 @@ struct Client::Impl {
81 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port); 81 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port);
82 } else { 82 } else {
83 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme); 83 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
84 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme", ""}; 84 return WebResult{WebResult::Code::InvalidURL, "Bad URL scheme", ""};
85 } 85 }
86 } 86 }
87 if (cli == nullptr) { 87 if (cli == nullptr) {
88 LOG_ERROR(WebService, "Invalid URL {}", host + path); 88 LOG_ERROR(WebService, "Invalid URL {}", host + path);
89 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL", ""}; 89 return WebResult{WebResult::Code::InvalidURL, "Invalid URL", ""};
90 } 90 }
91 cli->set_timeout_sec(TIMEOUT_SECONDS); 91 cli->set_timeout_sec(TIMEOUT_SECONDS);
92 92
@@ -106,7 +106,7 @@ struct Client::Impl {
106 std::string(API_VERSION.begin(), API_VERSION.end())); 106 std::string(API_VERSION.begin(), API_VERSION.end()));
107 if (method != "GET") { 107 if (method != "GET") {
108 params.emplace(std::string("Content-Type"), std::string("application/json")); 108 params.emplace(std::string("Content-Type"), std::string("application/json"));
109 }; 109 }
110 110
111 httplib::Request request; 111 httplib::Request request;
112 request.method = method; 112 request.method = method;
@@ -118,29 +118,28 @@ struct Client::Impl {
118 118
119 if (!cli->send(request, response)) { 119 if (!cli->send(request, response)) {
120 LOG_ERROR(WebService, "{} to {} returned null", method, host + path); 120 LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
121 return Common::WebResult{Common::WebResult::Code::LibError, "Null response", ""}; 121 return WebResult{WebResult::Code::LibError, "Null response", ""};
122 } 122 }
123 123
124 if (response.status >= 400) { 124 if (response.status >= 400) {
125 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path, 125 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
126 response.status); 126 response.status);
127 return Common::WebResult{Common::WebResult::Code::HttpError, 127 return WebResult{WebResult::Code::HttpError, std::to_string(response.status), ""};
128 std::to_string(response.status), ""};
129 } 128 }
130 129
131 auto content_type = response.headers.find("content-type"); 130 auto content_type = response.headers.find("content-type");
132 131
133 if (content_type == response.headers.end()) { 132 if (content_type == response.headers.end()) {
134 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path); 133 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path);
135 return Common::WebResult{Common::WebResult::Code::WrongContent, "", ""}; 134 return WebResult{WebResult::Code::WrongContent, "", ""};
136 } 135 }
137 136
138 if (content_type->second.find(accept) == std::string::npos) { 137 if (content_type->second.find(accept) == std::string::npos) {
139 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path, 138 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path,
140 content_type->second); 139 content_type->second);
141 return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content", ""}; 140 return WebResult{WebResult::Code::WrongContent, "Wrong content", ""};
142 } 141 }
143 return Common::WebResult{Common::WebResult::Code::Success, "", response.body}; 142 return WebResult{WebResult::Code::Success, "", response.body};
144 } 143 }
145 144
146 // Retrieve a new JWT from given username and token 145 // Retrieve a new JWT from given username and token
@@ -150,7 +149,7 @@ struct Client::Impl {
150 } 149 }
151 150
152 auto result = GenericRequest("POST", "/jwt/internal", "", "text/html", "", username, token); 151 auto result = GenericRequest("POST", "/jwt/internal", "", "text/html", "", username, token);
153 if (result.result_code != Common::WebResult::Code::Success) { 152 if (result.result_code != WebResult::Code::Success) {
154 LOG_ERROR(WebService, "UpdateJWT failed"); 153 LOG_ERROR(WebService, "UpdateJWT failed");
155 } else { 154 } else {
156 std::lock_guard lock{jwt_cache.mutex}; 155 std::lock_guard lock{jwt_cache.mutex};
@@ -180,29 +179,28 @@ Client::Client(std::string host, std::string username, std::string token)
180 179
181Client::~Client() = default; 180Client::~Client() = default;
182 181
183Common::WebResult Client::PostJson(const std::string& path, const std::string& data, 182WebResult Client::PostJson(const std::string& path, const std::string& data, bool allow_anonymous) {
184 bool allow_anonymous) {
185 return impl->GenericRequest("POST", path, data, allow_anonymous, "application/json"); 183 return impl->GenericRequest("POST", path, data, allow_anonymous, "application/json");
186} 184}
187 185
188Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) { 186WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
189 return impl->GenericRequest("GET", path, "", allow_anonymous, "application/json"); 187 return impl->GenericRequest("GET", path, "", allow_anonymous, "application/json");
190} 188}
191 189
192Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data, 190WebResult Client::DeleteJson(const std::string& path, const std::string& data,
193 bool allow_anonymous) { 191 bool allow_anonymous) {
194 return impl->GenericRequest("DELETE", path, data, allow_anonymous, "application/json"); 192 return impl->GenericRequest("DELETE", path, data, allow_anonymous, "application/json");
195} 193}
196 194
197Common::WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) { 195WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) {
198 return impl->GenericRequest("GET", path, "", allow_anonymous, "text/plain"); 196 return impl->GenericRequest("GET", path, "", allow_anonymous, "text/plain");
199} 197}
200 198
201Common::WebResult Client::GetImage(const std::string& path, bool allow_anonymous) { 199WebResult Client::GetImage(const std::string& path, bool allow_anonymous) {
202 return impl->GenericRequest("GET", path, "", allow_anonymous, "image/png"); 200 return impl->GenericRequest("GET", path, "", allow_anonymous, "image/png");
203} 201}
204 202
205Common::WebResult Client::GetExternalJWT(const std::string& audience) { 203WebResult Client::GetExternalJWT(const std::string& audience) {
206 return impl->GenericRequest("POST", fmt::format("/jwt/external/{}", audience), "", false, 204 return impl->GenericRequest("POST", fmt::format("/jwt/external/{}", audience), "", false,
207 "text/html"); 205 "text/html");
208} 206}
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h
index 04121f17e..81f58583c 100644
--- a/src/web_service/web_backend.h
+++ b/src/web_service/web_backend.h
@@ -7,12 +7,10 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9 9
10namespace Common {
11struct WebResult;
12}
13
14namespace WebService { 10namespace WebService {
15 11
12struct WebResult;
13
16class Client { 14class Client {
17public: 15public:
18 Client(std::string host, std::string username, std::string token); 16 Client(std::string host, std::string username, std::string token);
@@ -25,8 +23,7 @@ public:
25 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 23 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
26 * @return the result of the request. 24 * @return the result of the request.
27 */ 25 */
28 Common::WebResult PostJson(const std::string& path, const std::string& data, 26 WebResult PostJson(const std::string& path, const std::string& data, bool allow_anonymous);
29 bool allow_anonymous);
30 27
31 /** 28 /**
32 * Gets JSON from the specified path. 29 * Gets JSON from the specified path.
@@ -34,7 +31,7 @@ public:
34 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 31 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
35 * @return the result of the request. 32 * @return the result of the request.
36 */ 33 */
37 Common::WebResult GetJson(const std::string& path, bool allow_anonymous); 34 WebResult GetJson(const std::string& path, bool allow_anonymous);
38 35
39 /** 36 /**
40 * Deletes JSON to the specified path. 37 * Deletes JSON to the specified path.
@@ -43,8 +40,7 @@ public:
43 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 40 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
44 * @return the result of the request. 41 * @return the result of the request.
45 */ 42 */
46 Common::WebResult DeleteJson(const std::string& path, const std::string& data, 43 WebResult DeleteJson(const std::string& path, const std::string& data, bool allow_anonymous);
47 bool allow_anonymous);
48 44
49 /** 45 /**
50 * Gets a plain string from the specified path. 46 * Gets a plain string from the specified path.
@@ -52,7 +48,7 @@ public:
52 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 48 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
53 * @return the result of the request. 49 * @return the result of the request.
54 */ 50 */
55 Common::WebResult GetPlain(const std::string& path, bool allow_anonymous); 51 WebResult GetPlain(const std::string& path, bool allow_anonymous);
56 52
57 /** 53 /**
58 * Gets an PNG image from the specified path. 54 * Gets an PNG image from the specified path.
@@ -60,14 +56,14 @@ public:
60 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 56 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
61 * @return the result of the request. 57 * @return the result of the request.
62 */ 58 */
63 Common::WebResult GetImage(const std::string& path, bool allow_anonymous); 59 WebResult GetImage(const std::string& path, bool allow_anonymous);
64 60
65 /** 61 /**
66 * Requests an external JWT for the specific audience provided. 62 * Requests an external JWT for the specific audience provided.
67 * @param audience the audience of the JWT requested. 63 * @param audience the audience of the JWT requested.
68 * @return the result of the request. 64 * @return the result of the request.
69 */ 65 */
70 Common::WebResult GetExternalJWT(const std::string& audience); 66 WebResult GetExternalJWT(const std::string& audience);
71 67
72private: 68private:
73 struct Impl; 69 struct Impl;
diff --git a/src/common/web_result.h b/src/web_service/web_result.h
index 8bfa2141d..3aeeb5288 100644
--- a/src/common/web_result.h
+++ b/src/web_service/web_result.h
@@ -7,7 +7,7 @@
7#include <string> 7#include <string>
8#include "common/common_types.h" 8#include "common/common_types.h"
9 9
10namespace Common { 10namespace WebService {
11struct WebResult { 11struct WebResult {
12 enum class Code : u32 { 12 enum class Code : u32 {
13 Success, 13 Success,
@@ -22,4 +22,4 @@ struct WebResult {
22 std::string result_string; 22 std::string result_string;
23 std::string returned_data; 23 std::string returned_data;
24}; 24};
25} // namespace Common 25} // namespace WebService
diff --git a/src/yuzu/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
39 configuration/configure_debug.cpp 39 configuration/configure_debug.cpp
40 configuration/configure_debug.h 40 configuration/configure_debug.h
41 configuration/configure_debug.ui 41 configuration/configure_debug.ui
42 configuration/configure_debug_controller.cpp
43 configuration/configure_debug_controller.h
44 configuration/configure_debug_controller.ui
42 configuration/configure_dialog.cpp 45 configuration/configure_dialog.cpp
43 configuration/configure_dialog.h 46 configuration/configure_dialog.h
44 configuration/configure_filesystem.cpp 47 configuration/configure_filesystem.cpp
@@ -62,9 +65,9 @@ add_executable(yuzu
62 configuration/configure_input_player.cpp 65 configuration/configure_input_player.cpp
63 configuration/configure_input_player.h 66 configuration/configure_input_player.h
64 configuration/configure_input_player.ui 67 configuration/configure_input_player.ui
65 configuration/configure_input_simple.cpp 68 configuration/configure_input_advanced.cpp
66 configuration/configure_input_simple.h 69 configuration/configure_input_advanced.h
67 configuration/configure_input_simple.ui 70 configuration/configure_input_advanced.ui
68 configuration/configure_mouse_advanced.cpp 71 configuration/configure_mouse_advanced.cpp
69 configuration/configure_mouse_advanced.h 72 configuration/configure_mouse_advanced.h
70 configuration/configure_mouse_advanced.ui 73 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) {
26} 26}
27 27
28QString GetImagePath(Common::UUID uuid) { 28QString GetImagePath(Common::UUID uuid) {
29 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 29 const auto path = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
30 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; 30 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
31 return QString::fromStdString(path); 31 return QString::fromStdString(path);
32} 32}
diff --git a/src/yuzu/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*
304 return wsi; 304 return wsi;
305} 305}
306 306
307GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_) 307GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
308 : QWidget(parent_), emu_thread(emu_thread_) { 308 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_)
309 : QWidget(parent), emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)} {
309 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") 310 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
310 .arg(QString::fromUtf8(Common::g_build_name), 311 .arg(QString::fromUtf8(Common::g_build_name),
311 QString::fromUtf8(Common::g_scm_branch), 312 QString::fromUtf8(Common::g_scm_branch),
@@ -314,15 +315,15 @@ GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_)
314 auto layout = new QHBoxLayout(this); 315 auto layout = new QHBoxLayout(this);
315 layout->setMargin(0); 316 layout->setMargin(0);
316 setLayout(layout); 317 setLayout(layout);
317 InputCommon::Init(); 318 input_subsystem->Initialize();
318 319
319 this->setMouseTracking(true); 320 this->setMouseTracking(true);
320 321
321 connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete); 322 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
322} 323}
323 324
324GRenderWindow::~GRenderWindow() { 325GRenderWindow::~GRenderWindow() {
325 InputCommon::Shutdown(); 326 input_subsystem->Shutdown();
326} 327}
327 328
328void GRenderWindow::PollEvents() { 329void GRenderWindow::PollEvents() {
@@ -391,11 +392,11 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
391} 392}
392 393
393void GRenderWindow::keyPressEvent(QKeyEvent* event) { 394void GRenderWindow::keyPressEvent(QKeyEvent* event) {
394 InputCommon::GetKeyboard()->PressKey(event->key()); 395 input_subsystem->GetKeyboard()->PressKey(event->key());
395} 396}
396 397
397void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { 398void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
398 InputCommon::GetKeyboard()->ReleaseKey(event->key()); 399 input_subsystem->GetKeyboard()->ReleaseKey(event->key());
399} 400}
400 401
401void GRenderWindow::mousePressEvent(QMouseEvent* event) { 402void GRenderWindow::mousePressEvent(QMouseEvent* event) {
@@ -409,7 +410,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
409 const auto [x, y] = ScaleTouch(pos); 410 const auto [x, y] = ScaleTouch(pos);
410 this->TouchPressed(x, y); 411 this->TouchPressed(x, y);
411 } else if (event->button() == Qt::RightButton) { 412 } else if (event->button() == Qt::RightButton) {
412 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); 413 input_subsystem->GetMotionEmu()->BeginTilt(pos.x(), pos.y());
413 } 414 }
414 QWidget::mousePressEvent(event); 415 QWidget::mousePressEvent(event);
415} 416}
@@ -423,7 +424,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
423 auto pos = event->pos(); 424 auto pos = event->pos();
424 const auto [x, y] = ScaleTouch(pos); 425 const auto [x, y] = ScaleTouch(pos);
425 this->TouchMoved(x, y); 426 this->TouchMoved(x, y);
426 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); 427 input_subsystem->GetMotionEmu()->Tilt(pos.x(), pos.y());
427 QWidget::mouseMoveEvent(event); 428 QWidget::mouseMoveEvent(event);
428} 429}
429 430
@@ -436,7 +437,7 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
436 if (event->button() == Qt::LeftButton) { 437 if (event->button() == Qt::LeftButton) {
437 this->TouchReleased(); 438 this->TouchReleased();
438 } else if (event->button() == Qt::RightButton) { 439 } else if (event->button() == Qt::RightButton) {
439 InputCommon::GetMotionEmu()->EndTilt(); 440 input_subsystem->GetMotionEmu()->EndTilt();
440 } 441 }
441} 442}
442 443
@@ -451,7 +452,7 @@ void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
451 int active_points = 0; 452 int active_points = 0;
452 453
453 // average all active touch points 454 // average all active touch points
454 for (const auto tp : event->touchPoints()) { 455 for (const auto& tp : event->touchPoints()) {
455 if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) { 456 if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) {
456 active_points++; 457 active_points++;
457 pos += tp.pos(); 458 pos += tp.pos();
@@ -485,7 +486,7 @@ bool GRenderWindow::event(QEvent* event) {
485 486
486void GRenderWindow::focusOutEvent(QFocusEvent* event) { 487void GRenderWindow::focusOutEvent(QFocusEvent* event) {
487 QWidget::focusOutEvent(event); 488 QWidget::focusOutEvent(event);
488 InputCommon::GetKeyboard()->ReleaseAllKeys(); 489 input_subsystem->GetKeyboard()->ReleaseAllKeys();
489} 490}
490 491
491void GRenderWindow::resizeEvent(QResizeEvent* event) { 492void 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 @@
6 6
7#include <atomic> 7#include <atomic>
8#include <condition_variable> 8#include <condition_variable>
9#include <memory>
9#include <mutex> 10#include <mutex>
10 11
11#include <QImage> 12#include <QImage>
@@ -23,6 +24,10 @@ class QKeyEvent;
23class QTouchEvent; 24class QTouchEvent;
24class QStringList; 25class QStringList;
25 26
27namespace InputCommon {
28class InputSubsystem;
29}
30
26namespace VideoCore { 31namespace VideoCore {
27enum class LoadCallbackStage; 32enum class LoadCallbackStage;
28} 33}
@@ -121,7 +126,8 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
121 Q_OBJECT 126 Q_OBJECT
122 127
123public: 128public:
124 GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); 129 explicit GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
130 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_);
125 ~GRenderWindow() override; 131 ~GRenderWindow() override;
126 132
127 // EmuWindow implementation. 133 // EmuWindow implementation.
@@ -183,6 +189,7 @@ private:
183 QStringList GetUnsupportedGLExtensions() const; 189 QStringList GetUnsupportedGLExtensions() const;
184 190
185 EmuThread* emu_thread; 191 EmuThread* emu_thread;
192 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
186 193
187 // Main context that will be shared with all other contexts that are requested. 194 // Main context that will be shared with all other contexts that are requested.
188 // If this is used in a shared context setting, then this should not be used directly, but 195 // 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() {
54 back(); 54 back();
55 LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); 55 LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
56 Core::System::GetInstance().TelemetrySession().AddField( 56 Core::System::GetInstance().TelemetrySession().AddField(
57 Telemetry::FieldType::UserFeedback, "Compatibility", compatibility->checkedId()); 57 Common::Telemetry::FieldType::UserFeedback, "Compatibility",
58 compatibility->checkedId());
58 59
59 button(NextButton)->setEnabled(false); 60 button(NextButton)->setEnabled(false);
60 button(NextButton)->setText(tr("Submitting")); 61 button(NextButton)->setText(tr("Submitting"));
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index cb71b8d11..588bbd677 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -6,17 +6,18 @@
6#include <QKeySequence> 6#include <QKeySequence>
7#include <QSettings> 7#include <QSettings>
8#include "common/file_util.h" 8#include "common/file_util.h"
9#include "configure_input_simple.h"
10#include "core/hle/service/acc/profile_manager.h" 9#include "core/hle/service/acc/profile_manager.h"
11#include "core/hle/service/hid/controllers/npad.h" 10#include "core/hle/service/hid/controllers/npad.h"
12#include "input_common/main.h" 11#include "input_common/main.h"
13#include "input_common/udp/client.h" 12#include "input_common/udp/client.h"
14#include "yuzu/configuration/config.h" 13#include "yuzu/configuration/config.h"
15 14
15namespace FS = Common::FS;
16
16Config::Config(const std::string& config_file, bool is_global) { 17Config::Config(const std::string& config_file, bool is_global) {
17 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 18 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
18 qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + config_file; 19 qt_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + config_file;
19 FileUtil::CreateFullPath(qt_config_loc); 20 FS::CreateFullPath(qt_config_loc);
20 qt_config = 21 qt_config =
21 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); 22 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
22 global = is_global; 23 global = is_global;
@@ -30,29 +31,31 @@ Config::~Config() {
30} 31}
31 32
32const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { 33const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
33 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, 34 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q,
34 Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, 35 Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T,
35 Qt::Key_H, Qt::Key_G, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, Qt::Key_Down, Qt::Key_J, 36 Qt::Key_H, Qt::Key_G, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
36 Qt::Key_I, Qt::Key_L, Qt::Key_K, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
37}; 37};
38 38
39const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ 39const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
40 { 40 {
41 Qt::Key_Up, 41 Qt::Key_Up,
42 Qt::Key_Down, 42 Qt::Key_Down,
43 Qt::Key_Left, 43 Qt::Key_Left,
44 Qt::Key_Right, 44 Qt::Key_Right,
45 Qt::Key_E,
46 }, 45 },
47 { 46 {
48 Qt::Key_I, 47 Qt::Key_I,
49 Qt::Key_K, 48 Qt::Key_K,
50 Qt::Key_J, 49 Qt::Key_J,
51 Qt::Key_L, 50 Qt::Key_L,
52 Qt::Key_R,
53 }, 51 },
54}}; 52}};
55 53
54const std::array<int, 2> Config::default_stick_mod = {
55 Qt::Key_E,
56 Qt::Key_R,
57};
58
56const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons = 59const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons =
57 { 60 {
58 Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal, 61 Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal,
@@ -241,10 +244,10 @@ void Config::ReadPlayerValues() {
241 player.connected = 244 player.connected =
242 ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool(); 245 ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool();
243 246
244 player.type = static_cast<Settings::ControllerType>( 247 player.controller_type = static_cast<Settings::ControllerType>(
245 qt_config 248 qt_config
246 ->value(QStringLiteral("player_%1_type").arg(p), 249 ->value(QStringLiteral("player_%1_type").arg(p),
247 static_cast<u8>(Settings::ControllerType::DualJoycon)) 250 static_cast<u8>(Settings::ControllerType::ProController))
248 .toUInt()); 251 .toUInt());
249 252
250 player.body_color_left = qt_config 253 player.body_color_left = qt_config
@@ -284,7 +287,7 @@ void Config::ReadPlayerValues() {
284 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 287 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
285 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 288 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
286 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 289 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
287 default_analogs[i][3], default_analogs[i][4], 0.5f); 290 default_analogs[i][3], default_stick_mod[i], 0.5f);
288 auto& player_analogs = player.analogs[i]; 291 auto& player_analogs = player.analogs[i];
289 292
290 player_analogs = qt_config 293 player_analogs = qt_config
@@ -298,12 +301,6 @@ void Config::ReadPlayerValues() {
298 } 301 }
299 } 302 }
300 } 303 }
301
302 std::stable_partition(
303 Settings::values.players.begin(),
304 Settings::values.players.begin() +
305 Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
306 [](const auto& player) { return player.connected; });
307} 304}
308 305
309void Config::ReadDebugValues() { 306void Config::ReadDebugValues() {
@@ -328,7 +325,7 @@ void Config::ReadDebugValues() {
328 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 325 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
329 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 326 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
330 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 327 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
331 default_analogs[i][3], default_analogs[i][4], 0.5f); 328 default_analogs[i][3], default_stick_mod[i], 0.5f);
332 auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; 329 auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
333 330
334 debug_pad_analogs = qt_config 331 debug_pad_analogs = qt_config
@@ -395,13 +392,6 @@ void Config::ReadTouchscreenValues() {
395 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt(); 392 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt();
396} 393}
397 394
398void Config::ApplyDefaultProfileIfInputInvalid() {
399 if (!std::any_of(Settings::values.players.begin(), Settings::values.players.end(),
400 [](const Settings::PlayerInput& in) { return in.connected; })) {
401 ApplyInputProfileConfiguration(UISettings::values.profile_index);
402 }
403}
404
405void Config::ReadAudioValues() { 395void Config::ReadAudioValues() {
406 qt_config->beginGroup(QStringLiteral("Audio")); 396 qt_config->beginGroup(QStringLiteral("Audio"));
407 397
@@ -431,6 +421,8 @@ void Config::ReadControlValues() {
431 ReadMouseValues(); 421 ReadMouseValues();
432 ReadTouchscreenValues(); 422 ReadTouchscreenValues();
433 423
424 Settings::values.vibration_enabled =
425 ReadSetting(QStringLiteral("vibration_enabled"), true).toBool();
434 Settings::values.motion_device = 426 Settings::values.motion_device =
435 ReadSetting(QStringLiteral("motion_device"), 427 ReadSetting(QStringLiteral("motion_device"),
436 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")) 428 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"))
@@ -464,47 +456,42 @@ void Config::ReadDataStorageValues() {
464 qt_config->beginGroup(QStringLiteral("Data Storage")); 456 qt_config->beginGroup(QStringLiteral("Data Storage"));
465 457
466 Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool(); 458 Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool();
467 FileUtil::GetUserPath( 459 FS::GetUserPath(FS::UserPath::NANDDir,
468 FileUtil::UserPath::NANDDir, 460 qt_config
469 qt_config 461 ->value(QStringLiteral("nand_directory"),
470 ->value(QStringLiteral("nand_directory"), 462 QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir)))
471 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))) 463 .toString()
472 .toString() 464 .toStdString());
473 .toStdString()); 465 FS::GetUserPath(FS::UserPath::SDMCDir,
474 FileUtil::GetUserPath( 466 qt_config
475 FileUtil::UserPath::SDMCDir, 467 ->value(QStringLiteral("sdmc_directory"),
476 qt_config 468 QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir)))
477 ->value(QStringLiteral("sdmc_directory"), 469 .toString()
478 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))) 470 .toStdString());
479 .toString() 471 FS::GetUserPath(FS::UserPath::LoadDir,
480 .toStdString()); 472 qt_config
481 FileUtil::GetUserPath( 473 ->value(QStringLiteral("load_directory"),
482 FileUtil::UserPath::LoadDir, 474 QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir)))
483 qt_config 475 .toString()
484 ->value(QStringLiteral("load_directory"), 476 .toStdString());
485 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))) 477 FS::GetUserPath(FS::UserPath::DumpDir,
486 .toString() 478 qt_config
487 .toStdString()); 479 ->value(QStringLiteral("dump_directory"),
488 FileUtil::GetUserPath( 480 QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)))
489 FileUtil::UserPath::DumpDir, 481 .toString()
490 qt_config 482 .toStdString());
491 ->value(QStringLiteral("dump_directory"), 483 FS::GetUserPath(FS::UserPath::CacheDir,
492 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))) 484 qt_config
493 .toString() 485 ->value(QStringLiteral("cache_directory"),
494 .toStdString()); 486 QString::fromStdString(FS::GetUserPath(FS::UserPath::CacheDir)))
495 FileUtil::GetUserPath( 487 .toString()
496 FileUtil::UserPath::CacheDir, 488 .toStdString());
497 qt_config
498 ->value(QStringLiteral("cache_directory"),
499 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)))
500 .toString()
501 .toStdString());
502 Settings::values.gamecard_inserted = 489 Settings::values.gamecard_inserted =
503 ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool(); 490 ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool();
504 Settings::values.gamecard_current_game = 491 Settings::values.gamecard_current_game =
505 ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool(); 492 ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool();
506 Settings::values.gamecard_path = 493 Settings::values.gamecard_path =
507 ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString(); 494 ReadSetting(QStringLiteral("gamecard_path"), QString{}).toString().toStdString();
508 495
509 qt_config->endGroup(); 496 qt_config->endGroup();
510} 497}
@@ -518,7 +505,7 @@ void Config::ReadDebuggingValues() {
518 Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); 505 Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
519 Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); 506 Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
520 Settings::values.program_args = 507 Settings::values.program_args =
521 ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString(); 508 ReadSetting(QStringLiteral("program_args"), QString{}).toString().toStdString();
522 Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); 509 Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool();
523 Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); 510 Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool();
524 Settings::values.reporting_services = 511 Settings::values.reporting_services =
@@ -551,8 +538,7 @@ void Config::ReadDisabledAddOnValues() {
551 const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled")); 538 const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled"));
552 for (int j = 0; j < d_size; ++j) { 539 for (int j = 0; j < d_size; ++j) {
553 qt_config->setArrayIndex(j); 540 qt_config->setArrayIndex(j);
554 out.push_back( 541 out.push_back(ReadSetting(QStringLiteral("d"), QString{}).toString().toStdString());
555 ReadSetting(QStringLiteral("d"), QStringLiteral("")).toString().toStdString());
556 } 542 }
557 qt_config->endArray(); 543 qt_config->endArray();
558 Settings::values.disabled_addons.insert_or_assign(title_id, out); 544 Settings::values.disabled_addons.insert_or_assign(title_id, out);
@@ -638,6 +624,11 @@ void Config::ReadCpuValues() {
638 ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool(); 624 ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool();
639 Settings::values.cpuopt_reduce_misalign_checks = 625 Settings::values.cpuopt_reduce_misalign_checks =
640 ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool(); 626 ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool();
627
628 Settings::values.cpuopt_unsafe_unfuse_fma =
629 ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool();
630 Settings::values.cpuopt_unsafe_reduce_fp_error =
631 ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool();
641 } 632 }
642 633
643 qt_config->endGroup(); 634 qt_config->endGroup();
@@ -677,11 +668,11 @@ void Config::ReadScreenshotValues() {
677 668
678 UISettings::values.enable_screenshot_save_as = 669 UISettings::values.enable_screenshot_save_as =
679 ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool(); 670 ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool();
680 FileUtil::GetUserPath( 671 FS::GetUserPath(
681 FileUtil::UserPath::ScreenshotsDir, 672 FS::UserPath::ScreenshotsDir,
682 qt_config 673 qt_config
683 ->value(QStringLiteral("screenshot_path"), QString::fromStdString(FileUtil::GetUserPath( 674 ->value(QStringLiteral("screenshot_path"),
684 FileUtil::UserPath::ScreenshotsDir))) 675 QString::fromStdString(FS::GetUserPath(FS::UserPath::ScreenshotsDir)))
685 .toString() 676 .toString()
686 .toStdString()); 677 .toStdString());
687 678
@@ -786,14 +777,11 @@ void Config::ReadUIValues() {
786 UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool(); 777 UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool();
787 UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt(); 778 UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt();
788 UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool(); 779 UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool();
789 UISettings::values.profile_index = ReadSetting(QStringLiteral("profileIndex"), 0).toUInt();
790 UISettings::values.pause_when_in_background = 780 UISettings::values.pause_when_in_background =
791 ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool(); 781 ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool();
792 UISettings::values.hide_mouse = 782 UISettings::values.hide_mouse =
793 ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool(); 783 ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool();
794 784
795 ApplyDefaultProfileIfInputInvalid();
796
797 qt_config->endGroup(); 785 qt_config->endGroup();
798} 786}
799 787
@@ -867,8 +855,9 @@ void Config::SavePlayerValues() {
867 const auto& player = Settings::values.players[p]; 855 const auto& player = Settings::values.players[p];
868 856
869 WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false); 857 WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false);
870 WriteSetting(QStringLiteral("player_%1_type").arg(p), static_cast<u8>(player.type), 858 WriteSetting(QStringLiteral("player_%1_type").arg(p),
871 static_cast<u8>(Settings::ControllerType::DualJoycon)); 859 static_cast<u8>(player.controller_type),
860 static_cast<u8>(Settings::ControllerType::ProController));
872 861
873 WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left, 862 WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left,
874 Settings::JOYCON_BODY_NEON_BLUE); 863 Settings::JOYCON_BODY_NEON_BLUE);
@@ -890,7 +879,7 @@ void Config::SavePlayerValues() {
890 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 879 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
891 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 880 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
892 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 881 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
893 default_analogs[i][3], default_analogs[i][4], 0.5f); 882 default_analogs[i][3], default_stick_mod[i], 0.5f);
894 WriteSetting(QStringLiteral("player_%1_").arg(p) + 883 WriteSetting(QStringLiteral("player_%1_").arg(p) +
895 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 884 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
896 QString::fromStdString(player.analogs[i]), 885 QString::fromStdString(player.analogs[i]),
@@ -911,7 +900,7 @@ void Config::SaveDebugValues() {
911 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 900 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
912 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 901 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
913 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 902 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
914 default_analogs[i][3], default_analogs[i][4], 0.5f); 903 default_analogs[i][3], default_stick_mod[i], 0.5f);
915 WriteSetting(QStringLiteral("debug_pad_") + 904 WriteSetting(QStringLiteral("debug_pad_") +
916 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 905 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
917 QString::fromStdString(Settings::values.debug_pad_analogs[i]), 906 QString::fromStdString(Settings::values.debug_pad_analogs[i]),
@@ -988,6 +977,7 @@ void Config::SaveControlValues() {
988 SaveMouseValues(); 977 SaveMouseValues();
989 SaveTouchscreenValues(); 978 SaveTouchscreenValues();
990 979
980 WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true);
991 WriteSetting(QStringLiteral("motion_device"), 981 WriteSetting(QStringLiteral("motion_device"),
992 QString::fromStdString(Settings::values.motion_device), 982 QString::fromStdString(Settings::values.motion_device),
993 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); 983 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
@@ -1016,25 +1006,25 @@ void Config::SaveDataStorageValues() {
1016 1006
1017 WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true); 1007 WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true);
1018 WriteSetting(QStringLiteral("nand_directory"), 1008 WriteSetting(QStringLiteral("nand_directory"),
1019 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)), 1009 QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir)),
1020 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); 1010 QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir)));
1021 WriteSetting(QStringLiteral("sdmc_directory"), 1011 WriteSetting(QStringLiteral("sdmc_directory"),
1022 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)), 1012 QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir)),
1023 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 1013 QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir)));
1024 WriteSetting(QStringLiteral("load_directory"), 1014 WriteSetting(QStringLiteral("load_directory"),
1025 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)), 1015 QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir)),
1026 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))); 1016 QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir)));
1027 WriteSetting(QStringLiteral("dump_directory"), 1017 WriteSetting(QStringLiteral("dump_directory"),
1028 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)), 1018 QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)),
1029 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))); 1019 QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)));
1030 WriteSetting(QStringLiteral("cache_directory"), 1020 WriteSetting(QStringLiteral("cache_directory"),
1031 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)), 1021 QString::fromStdString(FS::GetUserPath(FS::UserPath::CacheDir)),
1032 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))); 1022 QString::fromStdString(FS::GetUserPath(FS::UserPath::CacheDir)));
1033 WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false); 1023 WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false);
1034 WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, 1024 WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game,
1035 false); 1025 false);
1036 WriteSetting(QStringLiteral("gamecard_path"), 1026 WriteSetting(QStringLiteral("gamecard_path"),
1037 QString::fromStdString(Settings::values.gamecard_path), QStringLiteral("")); 1027 QString::fromStdString(Settings::values.gamecard_path), QString{});
1038 1028
1039 qt_config->endGroup(); 1029 qt_config->endGroup();
1040} 1030}
@@ -1047,7 +1037,7 @@ void Config::SaveDebuggingValues() {
1047 WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false); 1037 WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
1048 WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689); 1038 WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
1049 WriteSetting(QStringLiteral("program_args"), 1039 WriteSetting(QStringLiteral("program_args"),
1050 QString::fromStdString(Settings::values.program_args), QStringLiteral("")); 1040 QString::fromStdString(Settings::values.program_args), QString{});
1051 WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false); 1041 WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false);
1052 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); 1042 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
1053 WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false); 1043 WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
@@ -1074,8 +1064,7 @@ void Config::SaveDisabledAddOnValues() {
1074 qt_config->beginWriteArray(QStringLiteral("disabled")); 1064 qt_config->beginWriteArray(QStringLiteral("disabled"));
1075 for (std::size_t j = 0; j < elem.second.size(); ++j) { 1065 for (std::size_t j = 0; j < elem.second.size(); ++j) {
1076 qt_config->setArrayIndex(static_cast<int>(j)); 1066 qt_config->setArrayIndex(static_cast<int>(j));
1077 WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), 1067 WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), QString{});
1078 QStringLiteral(""));
1079 } 1068 }
1080 qt_config->endArray(); 1069 qt_config->endArray();
1081 ++i; 1070 ++i;
@@ -1135,6 +1124,11 @@ void Config::SaveCpuValues() {
1135 WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true); 1124 WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true);
1136 WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), 1125 WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"),
1137 Settings::values.cpuopt_reduce_misalign_checks, true); 1126 Settings::values.cpuopt_reduce_misalign_checks, true);
1127
1128 WriteSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"),
1129 Settings::values.cpuopt_unsafe_unfuse_fma, true);
1130 WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"),
1131 Settings::values.cpuopt_unsafe_reduce_fp_error, true);
1138 } 1132 }
1139 1133
1140 qt_config->endGroup(); 1134 qt_config->endGroup();
@@ -1180,7 +1174,7 @@ void Config::SaveScreenshotValues() {
1180 WriteSetting(QStringLiteral("enable_screenshot_save_as"), 1174 WriteSetting(QStringLiteral("enable_screenshot_save_as"),
1181 UISettings::values.enable_screenshot_save_as); 1175 UISettings::values.enable_screenshot_save_as);
1182 WriteSetting(QStringLiteral("screenshot_path"), 1176 WriteSetting(QStringLiteral("screenshot_path"),
1183 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))); 1177 QString::fromStdString(FS::GetUserPath(FS::UserPath::ScreenshotsDir)));
1184 1178
1185 qt_config->endGroup(); 1179 qt_config->endGroup();
1186} 1180}
@@ -1259,7 +1253,6 @@ void Config::SaveUIValues() {
1259 WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true); 1253 WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true);
1260 WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0); 1254 WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0);
1261 WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false); 1255 WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false);
1262 WriteSetting(QStringLiteral("profileIndex"), UISettings::values.profile_index, 0);
1263 WriteSetting(QStringLiteral("pauseWhenInBackground"), 1256 WriteSetting(QStringLiteral("pauseWhenInBackground"),
1264 UISettings::values.pause_when_in_background, false); 1257 UISettings::values.pause_when_in_background, false);
1265 WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false); 1258 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:
23 void Save(); 23 void Save();
24 24
25 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; 25 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
26 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; 26 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
27 static const std::array<int, 2> default_stick_mod;
27 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> 28 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
28 default_mouse_buttons; 29 default_mouse_buttons;
29 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; 30 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
@@ -37,7 +38,6 @@ private:
37 void ReadKeyboardValues(); 38 void ReadKeyboardValues();
38 void ReadMouseValues(); 39 void ReadMouseValues();
39 void ReadTouchscreenValues(); 40 void ReadTouchscreenValues();
40 void ApplyDefaultProfileIfInputInvalid();
41 41
42 // Read functions bases off the respective config section names. 42 // Read functions bases off the respective config section names.
43 void ReadAudioValues(); 43 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(
72 ConfigurationShared::USE_GLOBAL_OFFSET); 72 ConfigurationShared::USE_GLOBAL_OFFSET);
73} 73}
74 74
75void ConfigurationShared::SetHighlight(QWidget* widget, const std::string& name, bool highlighted) { 75void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) {
76 if (highlighted) { 76 if (highlighted) {
77 widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }") 77 widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }")
78 .arg(QString::fromStdString(name))); 78 .arg(widget->objectName()));
79 } else { 79 } else {
80 widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,0,0,0) }") 80 widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,0,0,0) }")
81 .arg(QString::fromStdString(name))); 81 .arg(widget->objectName()));
82 } 82 }
83 widget->show(); 83 widget->show();
84} 84}
85 85
86void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, const std::string& name, 86void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox,
87 const Settings::Setting<bool>& setting, 87 const Settings::Setting<bool>& setting,
88 CheckState& tracker) { 88 CheckState& tracker) {
89 if (setting.UsingGlobal()) { 89 if (setting.UsingGlobal()) {
@@ -91,45 +91,39 @@ void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, const std::str
91 } else { 91 } else {
92 tracker = (setting.GetValue() == setting.GetValue(true)) ? CheckState::On : CheckState::Off; 92 tracker = (setting.GetValue() == setting.GetValue(true)) ? CheckState::On : CheckState::Off;
93 } 93 }
94 SetHighlight(checkbox, name, tracker != CheckState::Global); 94 SetHighlight(checkbox, tracker != CheckState::Global);
95 QObject::connect(checkbox, &QCheckBox::clicked, checkbox, 95 QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, setting, &tracker] {
96 [checkbox, name, setting, &tracker]() { 96 tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
97 tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) % 97 static_cast<int>(CheckState::Count));
98 static_cast<int>(CheckState::Count)); 98 if (tracker == CheckState::Global) {
99 if (tracker == CheckState::Global) { 99 checkbox->setChecked(setting.GetValue(true));
100 checkbox->setChecked(setting.GetValue(true)); 100 }
101 } 101 SetHighlight(checkbox, tracker != CheckState::Global);
102 SetHighlight(checkbox, name, tracker != CheckState::Global); 102 });
103 });
104} 103}
105 104
106void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, const std::string& name, 105void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, bool global, bool state,
107 bool global, bool state, bool global_state, 106 bool global_state, CheckState& tracker) {
108 CheckState& tracker) {
109 if (global) { 107 if (global) {
110 tracker = CheckState::Global; 108 tracker = CheckState::Global;
111 } else { 109 } else {
112 tracker = (state == global_state) ? CheckState::On : CheckState::Off; 110 tracker = (state == global_state) ? CheckState::On : CheckState::Off;
113 } 111 }
114 SetHighlight(checkbox, name, tracker != CheckState::Global); 112 SetHighlight(checkbox, tracker != CheckState::Global);
115 QObject::connect(checkbox, &QCheckBox::clicked, checkbox, 113 QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, global_state, &tracker] {
116 [checkbox, name, global_state, &tracker]() { 114 tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
117 tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) % 115 static_cast<int>(CheckState::Count));
118 static_cast<int>(CheckState::Count)); 116 if (tracker == CheckState::Global) {
119 if (tracker == CheckState::Global) { 117 checkbox->setChecked(global_state);
120 checkbox->setChecked(global_state); 118 }
121 } 119 SetHighlight(checkbox, tracker != CheckState::Global);
122 SetHighlight(checkbox, name, tracker != CheckState::Global); 120 });
123 });
124} 121}
125 122
126void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target, 123void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target, int global) {
127 const std::string& target_name, int global) {
128 InsertGlobalItem(combobox, global); 124 InsertGlobalItem(combobox, global);
129 QObject::connect(combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), target, 125 QObject::connect(combobox, qOverload<int>(&QComboBox::activated), target,
130 [target, target_name](int index) { 126 [target](int index) { SetHighlight(target, index != 0); });
131 ConfigurationShared::SetHighlight(target, target_name, index != 0);
132 });
133} 127}
134 128
135void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index) { 129void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index) {
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h
index 003148c68..312b9e549 100644
--- a/src/yuzu/configuration/configuration_shared.h
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -39,13 +39,12 @@ void SetPerGameSetting(QComboBox* combobox,
39void SetPerGameSetting(QComboBox* combobox, 39void SetPerGameSetting(QComboBox* combobox,
40 const Settings::Setting<Settings::GPUAccuracy>* setting); 40 const Settings::Setting<Settings::GPUAccuracy>* setting);
41 41
42void SetHighlight(QWidget* widget, const std::string& name, bool highlighted); 42void SetHighlight(QWidget* widget, bool highlighted);
43void SetColoredTristate(QCheckBox* checkbox, const std::string& name, 43void SetColoredTristate(QCheckBox* checkbox, const Settings::Setting<bool>& setting,
44 const Settings::Setting<bool>& setting, CheckState& tracker); 44 CheckState& tracker);
45void SetColoredTristate(QCheckBox* checkbox, const std::string& name, bool global, bool state, 45void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state,
46 bool global_state, CheckState& tracker); 46 CheckState& tracker);
47void SetColoredComboBox(QComboBox* combobox, QWidget* target, const std::string& target_name, 47void SetColoredComboBox(QComboBox* combobox, QWidget* target, int global);
48 int global);
49 48
50void InsertGlobalItem(QComboBox* combobox, int global_index); 49void InsertGlobalItem(QComboBox* combobox, int global_index);
51 50
diff --git a/src/yuzu/configuration/configure.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 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>382</width> 9 <width>650</width>
10 <height>650</height> 10 <height>650</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
@@ -26,13 +26,13 @@
26 <widget class="QListWidget" name="selectorList"> 26 <widget class="QListWidget" name="selectorList">
27 <property name="minimumSize"> 27 <property name="minimumSize">
28 <size> 28 <size>
29 <width>150</width> 29 <width>120</width>
30 <height>0</height> 30 <height>0</height>
31 </size> 31 </size>
32 </property> 32 </property>
33 <property name="maximumSize"> 33 <property name="maximumSize">
34 <size> 34 <size>
35 <width>150</width> 35 <width>120</width>
36 <height>16777215</height> 36 <height>16777215</height>
37 </size> 37 </size>
38 </property> 38 </property>
@@ -44,76 +44,121 @@
44 <number>0</number> 44 <number>0</number>
45 </property> 45 </property>
46 <widget class="ConfigureGeneral" name="generalTab"> 46 <widget class="ConfigureGeneral" name="generalTab">
47 <property name="accessibleName">
48 <string>General</string>
49 </property>
47 <attribute name="title"> 50 <attribute name="title">
48 <string>General</string> 51 <string>General</string>
49 </attribute> 52 </attribute>
50 </widget> 53 </widget>
51 <widget class="ConfigureUi" name="uiTab"> 54 <widget class="ConfigureUi" name="uiTab">
55 <property name="accessibleName">
56 <string>UI</string>
57 </property>
52 <attribute name="title"> 58 <attribute name="title">
53 <string>Game List</string> 59 <string>Game List</string>
54 </attribute> 60 </attribute>
55 </widget> 61 </widget>
56 <widget class="ConfigureSystem" name="systemTab"> 62 <widget class="ConfigureSystem" name="systemTab">
63 <property name="accessibleName">
64 <string>System</string>
65 </property>
57 <attribute name="title"> 66 <attribute name="title">
58 <string>System</string> 67 <string>System</string>
59 </attribute> 68 </attribute>
60 </widget> 69 </widget>
61 <widget class="ConfigureProfileManager" name="profileManagerTab"> 70 <widget class="ConfigureProfileManager" name="profileManagerTab">
71 <property name="accessibleName">
72 <string>Profiles</string>
73 </property>
62 <attribute name="title"> 74 <attribute name="title">
63 <string>Profiles</string> 75 <string>Profiles</string>
64 </attribute> 76 </attribute>
65 </widget> 77 </widget>
66 <widget class="ConfigureFilesystem" name="filesystemTab"> 78 <widget class="ConfigureFilesystem" name="filesystemTab">
79 <property name="accessibleName">
80 <string>Filesystem</string>
81 </property>
67 <attribute name="title"> 82 <attribute name="title">
68 <string>Filesystem</string> 83 <string>Filesystem</string>
69 </attribute> 84 </attribute>
70 </widget> 85 </widget>
71 <widget class="ConfigureInputSimple" name="inputTab"> 86 <widget class="ConfigureInput" name="inputTab">
87 <property name="accessibleName">
88 <string>Controls</string>
89 </property>
72 <attribute name="title"> 90 <attribute name="title">
73 <string>Input</string> 91 <string>Controls</string>
74 </attribute> 92 </attribute>
75 </widget> 93 </widget>
76 <widget class="ConfigureHotkeys" name="hotkeysTab"> 94 <widget class="ConfigureHotkeys" name="hotkeysTab">
95 <property name="accessibleName">
96 <string>Hotkeys</string>
97 </property>
77 <attribute name="title"> 98 <attribute name="title">
78 <string>Hotkeys</string> 99 <string>Hotkeys</string>
79 </attribute> 100 </attribute>
80 </widget> 101 </widget>
81 <widget class="ConfigureCpu" name="cpuTab"> 102 <widget class="ConfigureCpu" name="cpuTab">
103 <property name="accessibleName">
104 <string>CPU</string>
105 </property>
82 <attribute name="title"> 106 <attribute name="title">
83 <string>CPU</string> 107 <string>CPU</string>
84 </attribute> 108 </attribute>
85 </widget> 109 </widget>
86 <widget class="ConfigureCpuDebug" name="cpuDebugTab"> 110 <widget class="ConfigureCpuDebug" name="cpuDebugTab">
111 <property name="accessibleName">
112 <string>Debug</string>
113 </property>
87 <attribute name="title"> 114 <attribute name="title">
88 <string>Debug</string> 115 <string>Debug</string>
89 </attribute> 116 </attribute>
90 </widget> 117 </widget>
91 <widget class="ConfigureGraphics" name="graphicsTab"> 118 <widget class="ConfigureGraphics" name="graphicsTab">
119 <property name="accessibleName">
120 <string>Graphics</string>
121 </property>
92 <attribute name="title"> 122 <attribute name="title">
93 <string>Graphics</string> 123 <string>Graphics</string>
94 </attribute> 124 </attribute>
95 </widget> 125 </widget>
96 <widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab"> 126 <widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab">
127 <property name="accessibleName">
128 <string>Advanced</string>
129 </property>
97 <attribute name="title"> 130 <attribute name="title">
98 <string>GraphicsAdvanced</string> 131 <string>GraphicsAdvanced</string>
99 </attribute> 132 </attribute>
100 </widget> 133 </widget>
101 <widget class="ConfigureAudio" name="audioTab"> 134 <widget class="ConfigureAudio" name="audioTab">
135 <property name="accessibleName">
136 <string>Audio</string>
137 </property>
102 <attribute name="title"> 138 <attribute name="title">
103 <string>Audio</string> 139 <string>Audio</string>
104 </attribute> 140 </attribute>
105 </widget> 141 </widget>
106 <widget class="ConfigureDebug" name="debugTab"> 142 <widget class="ConfigureDebug" name="debugTab">
143 <property name="accessibleName">
144 <string>Debug</string>
145 </property>
107 <attribute name="title"> 146 <attribute name="title">
108 <string>Debug</string> 147 <string>Debug</string>
109 </attribute> 148 </attribute>
110 </widget> 149 </widget>
111 <widget class="ConfigureWeb" name="webTab"> 150 <widget class="ConfigureWeb" name="webTab">
151 <property name="accessibleName">
152 <string>Web</string>
153 </property>
112 <attribute name="title"> 154 <attribute name="title">
113 <string>Web</string> 155 <string>Web</string>
114 </attribute> 156 </attribute>
115 </widget> 157 </widget>
116 <widget class="ConfigureService" name="serviceTab"> 158 <widget class="ConfigureService" name="serviceTab">
159 <property name="accessibleName">
160 <string>Services</string>
161 </property>
117 <attribute name="title"> 162 <attribute name="title">
118 <string>Services</string> 163 <string>Services</string>
119 </attribute> 164 </attribute>
@@ -205,9 +250,9 @@
205 <container>1</container> 250 <container>1</container>
206 </customwidget> 251 </customwidget>
207 <customwidget> 252 <customwidget>
208 <class>ConfigureInputSimple</class> 253 <class>ConfigureInput</class>
209 <extends>QWidget</extends> 254 <extends>QWidget</extends>
210 <header>configuration/configure_input_simple.h</header> 255 <header>configuration/configure_input.h</header>
211 <container>1</container> 256 <container>1</container>
212 </customwidget> 257 </customwidget>
213 <customwidget> 258 <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() {
59 ui->volume_combo_box->setCurrentIndex(1); 59 ui->volume_combo_box->setCurrentIndex(1);
60 ui->volume_slider->setEnabled(true); 60 ui->volume_slider->setEnabled(true);
61 } 61 }
62 ConfigurationShared::SetHighlight(ui->volume_layout, "volume_layout", 62 ConfigurationShared::SetHighlight(ui->volume_layout,
63 !Settings::values.volume.UsingGlobal()); 63 !Settings::values.volume.UsingGlobal());
64 } 64 }
65 SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); 65 SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
@@ -173,14 +173,13 @@ void ConfigureAudio::SetupPerGameUI() {
173 return; 173 return;
174 } 174 }
175 175
176 ConfigurationShared::SetColoredTristate(ui->toggle_audio_stretching, "toggle_audio_stretching", 176 ConfigurationShared::SetColoredTristate(ui->toggle_audio_stretching,
177 Settings::values.enable_audio_stretching, 177 Settings::values.enable_audio_stretching,
178 enable_audio_stretching); 178 enable_audio_stretching);
179 connect(ui->volume_combo_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), 179 connect(ui->volume_combo_box, qOverload<int>(&QComboBox::activated), this, [this](int index) {
180 this, [this](int index) { 180 ui->volume_slider->setEnabled(index == 1);
181 ui->volume_slider->setEnabled(index == 1); 181 ConfigurationShared::SetHighlight(ui->volume_layout, index == 1);
182 ConfigurationShared::SetHighlight(ui->volume_layout, "volume_layout", index == 1); 182 });
183 });
184 183
185 ui->output_sink_combo_box->setVisible(false); 184 ui->output_sink_combo_box->setVisible(false);
186 ui->output_sink_label->setVisible(false); 185 ui->output_sink_label->setVisible(false);
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index 7493e5ffb..37fcd6adc 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -19,6 +19,8 @@ ConfigureCpu::ConfigureCpu(QWidget* parent) : QWidget(parent), ui(new Ui::Config
19 19
20 connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this, 20 connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this,
21 &ConfigureCpu::AccuracyUpdated); 21 &ConfigureCpu::AccuracyUpdated);
22 connect(ui->accuracy, qOverload<int>(&QComboBox::currentIndexChanged), this,
23 &ConfigureCpu::UpdateGroup);
22} 24}
23 25
24ConfigureCpu::~ConfigureCpu() = default; 26ConfigureCpu::~ConfigureCpu() = default;
@@ -28,6 +30,12 @@ void ConfigureCpu::SetConfiguration() {
28 30
29 ui->accuracy->setEnabled(runtime_lock); 31 ui->accuracy->setEnabled(runtime_lock);
30 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy)); 32 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy));
33 UpdateGroup(static_cast<int>(Settings::values.cpu_accuracy));
34
35 ui->cpuopt_unsafe_unfuse_fma->setEnabled(runtime_lock);
36 ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma);
37 ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock);
38 ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error);
31} 39}
32 40
33void ConfigureCpu::AccuracyUpdated(int index) { 41void ConfigureCpu::AccuracyUpdated(int index) {
@@ -38,14 +46,21 @@ void ConfigureCpu::AccuracyUpdated(int index) {
38 QMessageBox::Yes | QMessageBox::No); 46 QMessageBox::Yes | QMessageBox::No);
39 if (result == QMessageBox::No) { 47 if (result == QMessageBox::No) {
40 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate)); 48 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate));
41 return; 49 UpdateGroup(static_cast<int>(Settings::CPUAccuracy::Accurate));
42 } 50 }
43 } 51 }
44} 52}
45 53
54void ConfigureCpu::UpdateGroup(int index) {
55 ui->unsafe_group->setVisible(static_cast<Settings::CPUAccuracy>(index) ==
56 Settings::CPUAccuracy::Unsafe);
57}
58
46void ConfigureCpu::ApplyConfiguration() { 59void ConfigureCpu::ApplyConfiguration() {
47 Settings::values.cpu_accuracy = 60 Settings::values.cpu_accuracy =
48 static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()); 61 static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex());
62 Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked();
63 Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked();
49} 64}
50 65
51void ConfigureCpu::changeEvent(QEvent* event) { 66void ConfigureCpu::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h
index e4741d3a4..3c5683d81 100644
--- a/src/yuzu/configuration/configure_cpu.h
+++ b/src/yuzu/configuration/configure_cpu.h
@@ -26,6 +26,7 @@ private:
26 void RetranslateUI(); 26 void RetranslateUI();
27 27
28 void AccuracyUpdated(int index); 28 void AccuracyUpdated(int index);
29 void UpdateGroup(int index);
29 30
30 void SetConfiguration(); 31 void SetConfiguration();
31 32
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
index bf6ea79bb..ebdd2e6e9 100644
--- a/src/yuzu/configuration/configure_cpu.ui
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -40,6 +40,11 @@
40 </item> 40 </item>
41 <item> 41 <item>
42 <property name="text"> 42 <property name="text">
43 <string>Unsafe</string>
44 </property>
45 </item>
46 <item>
47 <property name="text">
43 <string>Enable Debug Mode</string> 48 <string>Enable Debug Mode</string>
44 </property> 49 </property>
45 </item> 50 </item>
@@ -63,6 +68,53 @@
63 </layout> 68 </layout>
64 </item> 69 </item>
65 <item> 70 <item>
71 <layout class="QVBoxLayout">
72 <item>
73 <widget class="QGroupBox" name="unsafe_group">
74 <property name="title">
75 <string>Unsafe CPU Optimization Settings</string>
76 </property>
77 <layout class="QVBoxLayout">
78 <item>
79 <widget class="QLabel">
80 <property name="wordWrap">
81 <bool>1</bool>
82 </property>
83 <property name="text">
84 <string>These settings reduce accuracy for speed.</string>
85 </property>
86 </widget>
87 </item>
88 <item>
89 <widget class="QCheckBox" name="cpuopt_unsafe_unfuse_fma">
90 <property name="text">
91 <string>Unfuse FMA (improve performance on CPUs without FMA)</string>
92 </property>
93 <property name="toolTip">
94 <string>
95 &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
96 </string>
97 </property>
98 </widget>
99 </item>
100 <item>
101 <widget class="QCheckBox" name="cpuopt_unsafe_reduce_fp_error">
102 <property name="text">
103 <string>Faster FRSQRTE and FRECPE</string>
104 </property>
105 <property name="toolTip">
106 <string>
107 &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
108 </string>
109 </property>
110 </widget>
111 </item>
112 </layout>
113 </widget>
114 </item>
115 </layout>
116 </item>
117 <item>
66 <spacer name="verticalSpacer"> 118 <spacer name="verticalSpacer">
67 <property name="orientation"> 119 <property name="orientation">
68 <enum>Qt::Vertical</enum> 120 <enum>Qt::Vertical</enum>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index d0e71dd60..2bfe2c306 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -19,7 +19,8 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
19 SetConfiguration(); 19 SetConfiguration();
20 20
21 connect(ui->open_log_button, &QPushButton::clicked, []() { 21 connect(ui->open_log_button, &QPushButton::clicked, []() {
22 QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir)); 22 const auto path =
23 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::LogDir));
23 QDesktopServices::openUrl(QUrl::fromLocalFile(path)); 24 QDesktopServices::openUrl(QUrl::fromLocalFile(path));
24 }); 25 });
25} 26}
diff --git a/src/yuzu/configuration/configure_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 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "ui_configure_debug_controller.h"
6#include "yuzu/configuration/configure_debug_controller.h"
7
8ConfigureDebugController::ConfigureDebugController(QWidget* parent,
9 InputCommon::InputSubsystem* input_subsystem)
10 : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()),
11 debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, true)) {
12 ui->setupUi(this);
13
14 ui->controllerLayout->addWidget(debug_controller);
15
16 connect(ui->clear_all_button, &QPushButton::clicked, this,
17 [this] { debug_controller->ClearAll(); });
18 connect(ui->restore_defaults_button, &QPushButton::clicked, this,
19 [this] { debug_controller->RestoreDefaults(); });
20
21 RetranslateUI();
22}
23
24ConfigureDebugController::~ConfigureDebugController() = default;
25
26void ConfigureDebugController::ApplyConfiguration() {
27 debug_controller->ApplyConfiguration();
28}
29
30void ConfigureDebugController::changeEvent(QEvent* event) {
31 if (event->type() == QEvent::LanguageChange) {
32 RetranslateUI();
33 }
34
35 QDialog::changeEvent(event);
36}
37
38void ConfigureDebugController::RetranslateUI() {
39 ui->retranslateUi(this);
40}
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 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QDialog>
9#include "yuzu/configuration/configure_input_player.h"
10
11class QPushButton;
12
13namespace InputCommon {
14class InputSubsystem;
15}
16
17namespace Ui {
18class ConfigureDebugController;
19}
20
21class ConfigureDebugController : public QDialog {
22 Q_OBJECT
23
24public:
25 explicit ConfigureDebugController(QWidget* parent,
26 InputCommon::InputSubsystem* input_subsystem);
27 ~ConfigureDebugController() override;
28
29 void ApplyConfiguration();
30
31private:
32 void changeEvent(QEvent* event) override;
33 void RetranslateUI();
34
35 std::unique_ptr<Ui::ConfigureDebugController> ui;
36
37 ConfigureInputPlayer* debug_controller;
38};
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 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureDebugController</class>
4 <widget class="QDialog" name="ConfigureDebugController">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>780</width>
10 <height>500</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Debug Controller</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <property name="spacing">
18 <number>2</number>
19 </property>
20 <property name="leftMargin">
21 <number>9</number>
22 </property>
23 <property name="topMargin">
24 <number>9</number>
25 </property>
26 <property name="rightMargin">
27 <number>9</number>
28 </property>
29 <property name="bottomMargin">
30 <number>9</number>
31 </property>
32 <item>
33 <layout class="QHBoxLayout" name="controllerLayout"/>
34 </item>
35 <item>
36 <layout class="QHBoxLayout" name="horizontalLayout">
37 <item>
38 <widget class="QPushButton" name="clear_all_button">
39 <property name="text">
40 <string>Clear</string>
41 </property>
42 </widget>
43 </item>
44 <item>
45 <widget class="QPushButton" name="restore_defaults_button">
46 <property name="text">
47 <string>Defaults</string>
48 </property>
49 </widget>
50 </item>
51 <item>
52 <widget class="QDialogButtonBox" name="buttonBox">
53 <property name="standardButtons">
54 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
55 </property>
56 </widget>
57 </item>
58 </layout>
59 </item>
60 </layout>
61 </widget>
62 <resources/>
63 <connections>
64 <connection>
65 <sender>buttonBox</sender>
66 <signal>accepted()</signal>
67 <receiver>ConfigureDebugController</receiver>
68 <slot>accept()</slot>
69 <hints>
70 <hint type="sourcelabel">
71 <x>140</x>
72 <y>318</y>
73 </hint>
74 <hint type="destinationlabel">
75 <x>140</x>
76 <y>169</y>
77 </hint>
78 </hints>
79 </connection>
80 <connection>
81 <sender>buttonBox</sender>
82 <signal>rejected()</signal>
83 <receiver>ConfigureDebugController</receiver>
84 <slot>reject()</slot>
85 <hints>
86 <hint type="sourcelabel">
87 <x>140</x>
88 <y>318</y>
89 </hint>
90 <hint type="destinationlabel">
91 <x>140</x>
92 <y>169</y>
93 </hint>
94 </hints>
95 </connection>
96 </connections>
97</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 @@
12#include "yuzu/configuration/configure_input_player.h" 12#include "yuzu/configuration/configure_input_player.h"
13#include "yuzu/hotkeys.h" 13#include "yuzu/hotkeys.h"
14 14
15ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) 15ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
16 InputCommon::InputSubsystem* input_subsystem)
16 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) { 17 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
17 Settings::configuring_global = true; 18 Settings::configuring_global = true;
18 19
@@ -20,6 +21,8 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
20 ui->hotkeysTab->Populate(registry); 21 ui->hotkeysTab->Populate(registry);
21 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); 22 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
22 23
24 ui->inputTab->Initialize(input_subsystem);
25
23 SetConfiguration(); 26 SetConfiguration();
24 PopulateSelectionList(); 27 PopulateSelectionList();
25 28
@@ -80,12 +83,12 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
80 83
81void ConfigureDialog::PopulateSelectionList() { 84void ConfigureDialog::PopulateSelectionList() {
82 const std::array<std::pair<QString, QList<QWidget*>>, 6> items{ 85 const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
83 {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}}, 86 {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}},
84 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, 87 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}},
85 {tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}}, 88 {tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}},
86 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}}, 89 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
87 {tr("Audio"), {ui->audioTab}}, 90 {tr("Audio"), {ui->audioTab}},
88 {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, 91 {tr("Controls"), ui->inputTab->GetSubTabs()}},
89 }; 92 };
90 93
91 [[maybe_unused]] const QSignalBlocker blocker(ui->selectorList); 94 [[maybe_unused]] const QSignalBlocker blocker(ui->selectorList);
@@ -117,7 +120,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
117 {ui->generalTab, tr("General")}, 120 {ui->generalTab, tr("General")},
118 {ui->systemTab, tr("System")}, 121 {ui->systemTab, tr("System")},
119 {ui->profileManagerTab, tr("Profiles")}, 122 {ui->profileManagerTab, tr("Profiles")},
120 {ui->inputTab, tr("Input")}, 123 {ui->inputTab, tr("Controls")},
121 {ui->hotkeysTab, tr("Hotkeys")}, 124 {ui->hotkeysTab, tr("Hotkeys")},
122 {ui->cpuTab, tr("CPU")}, 125 {ui->cpuTab, tr("CPU")},
123 {ui->cpuDebugTab, tr("Debug")}, 126 {ui->cpuDebugTab, tr("Debug")},
@@ -138,6 +141,6 @@ void ConfigureDialog::UpdateVisibleTabs() {
138 const QList<QWidget*> tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole)); 141 const QList<QWidget*> tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole));
139 142
140 for (const auto tab : tabs) { 143 for (const auto tab : tabs) {
141 ui->tabWidget->addTab(tab, widgets.at(tab)); 144 ui->tabWidget->addTab(tab, tab->accessibleName());
142 } 145 }
143} 146}
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 @@
9 9
10class HotkeyRegistry; 10class HotkeyRegistry;
11 11
12namespace InputCommon {
13class InputSubsystem;
14}
15
12namespace Ui { 16namespace Ui {
13class ConfigureDialog; 17class ConfigureDialog;
14} 18}
@@ -17,7 +21,8 @@ class ConfigureDialog : public QDialog {
17 Q_OBJECT 21 Q_OBJECT
18 22
19public: 23public:
20 explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry); 24 explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
25 InputCommon::InputSubsystem* input_subsystem);
21 ~ConfigureDialog() override; 26 ~ConfigureDialog() override;
22 27
23 void ApplyConfiguration(); 28 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;
42 42
43void ConfigureFilesystem::setConfiguration() { 43void ConfigureFilesystem::setConfiguration() {
44 ui->nand_directory_edit->setText( 44 ui->nand_directory_edit->setText(
45 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); 45 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)));
46 ui->sdmc_directory_edit->setText( 46 ui->sdmc_directory_edit->setText(
47 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 47 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)));
48 ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path)); 48 ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path));
49 ui->dump_path_edit->setText( 49 ui->dump_path_edit->setText(
50 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))); 50 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir)));
51 ui->load_path_edit->setText( 51 ui->load_path_edit->setText(
52 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))); 52 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir)));
53 ui->cache_directory_edit->setText( 53 ui->cache_directory_edit->setText(
54 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))); 54 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)));
55 55
56 ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted); 56 ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted);
57 ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game); 57 ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game);
@@ -64,14 +64,16 @@ void ConfigureFilesystem::setConfiguration() {
64} 64}
65 65
66void ConfigureFilesystem::applyConfiguration() { 66void ConfigureFilesystem::applyConfiguration() {
67 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir, 67 Common::FS::GetUserPath(Common::FS::UserPath::NANDDir,
68 ui->nand_directory_edit->text().toStdString()); 68 ui->nand_directory_edit->text().toStdString());
69 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, 69 Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir,
70 ui->sdmc_directory_edit->text().toStdString()); 70 ui->sdmc_directory_edit->text().toStdString());
71 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir, ui->dump_path_edit->text().toStdString()); 71 Common::FS::GetUserPath(Common::FS::UserPath::DumpDir,
72 FileUtil::GetUserPath(FileUtil::UserPath::LoadDir, ui->load_path_edit->text().toStdString()); 72 ui->dump_path_edit->text().toStdString());
73 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir, 73 Common::FS::GetUserPath(Common::FS::UserPath::LoadDir,
74 ui->cache_directory_edit->text().toStdString()); 74 ui->load_path_edit->text().toStdString());
75 Common::FS::GetUserPath(Common::FS::UserPath::CacheDir,
76 ui->cache_directory_edit->text().toStdString());
75 Settings::values.gamecard_path = ui->gamecard_path_edit->text().toStdString(); 77 Settings::values.gamecard_path = ui->gamecard_path_edit->text().toStdString();
76 78
77 Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked(); 79 Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked();
@@ -121,12 +123,13 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit)
121} 123}
122 124
123void ConfigureFilesystem::ResetMetadata() { 125void ConfigureFilesystem::ResetMetadata() {
124 if (!FileUtil::Exists(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + 126 if (!Common::FS::Exists(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
125 "game_list")) { 127 "game_list")) {
126 QMessageBox::information(this, tr("Reset Metadata Cache"), 128 QMessageBox::information(this, tr("Reset Metadata Cache"),
127 tr("The metadata cache is already empty.")); 129 tr("The metadata cache is already empty."));
128 } else if (FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + 130 } else if (Common::FS::DeleteDirRecursively(
129 DIR_SEP + "game_list")) { 131 Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
132 "game_list")) {
130 QMessageBox::information(this, tr("Reset Metadata Cache"), 133 QMessageBox::information(this, tr("Reset Metadata Cache"),
131 tr("The operation completed successfully.")); 134 tr("The operation completed successfully."));
132 UISettings::values.is_game_list_reload_pending.exchange(true); 135 UISettings::values.is_game_list_reload_pending.exchange(true);
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index c0dbd9855..830096ea0 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -105,10 +105,10 @@ void ConfigureGeneral::SetupPerGameUI() {
105 ui->toggle_background_pause->setVisible(false); 105 ui->toggle_background_pause->setVisible(false);
106 ui->toggle_hide_mouse->setVisible(false); 106 ui->toggle_hide_mouse->setVisible(false);
107 107
108 ConfigurationShared::SetColoredTristate(ui->toggle_frame_limit, "toggle_frame_limit", 108 ConfigurationShared::SetColoredTristate(ui->toggle_frame_limit,
109 Settings::values.use_frame_limit, use_frame_limit); 109 Settings::values.use_frame_limit, use_frame_limit);
110 ConfigurationShared::SetColoredTristate(ui->use_multi_core, "use_multi_core", 110 ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core,
111 Settings::values.use_multi_core, use_multi_core); 111 use_multi_core);
112 112
113 connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, [this]() { 113 connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, [this]() {
114 ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked() && 114 ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked() &&
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 3e42531c3..07d818548 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -34,9 +34,8 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
34 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] { 34 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
35 UpdateDeviceComboBox(); 35 UpdateDeviceComboBox();
36 if (!Settings::configuring_global) { 36 if (!Settings::configuring_global) {
37 ConfigurationShared::SetHighlight(ui->api_layout, "api_layout", 37 ConfigurationShared::SetHighlight(
38 ui->api->currentIndex() != 38 ui->api_layout, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX);
39 ConfigurationShared::USE_GLOBAL_INDEX);
40 } 39 }
41 }); 40 });
42 connect(ui->device, qOverload<int>(&QComboBox::activated), this, 41 connect(ui->device, qOverload<int>(&QComboBox::activated), this,
@@ -80,17 +79,16 @@ void ConfigureGraphics::SetConfiguration() {
80 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); 79 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
81 } else { 80 } else {
82 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); 81 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
83 ConfigurationShared::SetHighlight(ui->api_layout, "api_layout", 82 ConfigurationShared::SetHighlight(ui->api_layout,
84 !Settings::values.renderer_backend.UsingGlobal()); 83 !Settings::values.renderer_backend.UsingGlobal());
85 ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox, 84 ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox,
86 &Settings::values.aspect_ratio); 85 &Settings::values.aspect_ratio);
87 86
88 ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1); 87 ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
89 ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal()); 88 ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
90 ConfigurationShared::SetHighlight(ui->ar_label, "ar_label", 89 ConfigurationShared::SetHighlight(ui->ar_label,
91 !Settings::values.aspect_ratio.UsingGlobal()); 90 !Settings::values.aspect_ratio.UsingGlobal());
92 ConfigurationShared::SetHighlight(ui->bg_layout, "bg_layout", 91 ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal());
93 !Settings::values.bg_red.UsingGlobal());
94 } 92 }
95 93
96 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(), 94 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(),
@@ -248,20 +246,18 @@ void ConfigureGraphics::SetupPerGameUI() {
248 return; 246 return;
249 } 247 }
250 248
251 connect(ui->bg_combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, 249 connect(ui->bg_combobox, qOverload<int>(&QComboBox::activated), this, [this](int index) {
252 [this](int index) { 250 ui->bg_button->setEnabled(index == 1);
253 ui->bg_button->setEnabled(index == 1); 251 ConfigurationShared::SetHighlight(ui->bg_layout, index == 1);
254 ConfigurationShared::SetHighlight(ui->bg_layout, "bg_layout", index == 1); 252 });
255 });
256 253
257 ConfigurationShared::SetColoredTristate(ui->use_disk_shader_cache, "use_disk_shader_cache",
258 Settings::values.use_disk_shader_cache,
259 use_disk_shader_cache);
260 ConfigurationShared::SetColoredTristate( 254 ConfigurationShared::SetColoredTristate(
261 ui->use_asynchronous_gpu_emulation, "use_asynchronous_gpu_emulation", 255 ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache);
262 Settings::values.use_asynchronous_gpu_emulation, use_asynchronous_gpu_emulation); 256 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation,
257 Settings::values.use_asynchronous_gpu_emulation,
258 use_asynchronous_gpu_emulation);
263 259
264 ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label, "ar_label", 260 ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label,
265 Settings::values.aspect_ratio.GetValue(true)); 261 Settings::values.aspect_ratio.GetValue(true));
266 ConfigurationShared::InsertGlobalItem( 262 ConfigurationShared::InsertGlobalItem(
267 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); 263 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index c5d1a778c..73f276949 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -41,9 +41,9 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
41 ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy); 41 ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy);
42 ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox, 42 ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox,
43 &Settings::values.max_anisotropy); 43 &Settings::values.max_anisotropy);
44 ConfigurationShared::SetHighlight(ui->label_gpu_accuracy, "label_gpu_accuracy", 44 ConfigurationShared::SetHighlight(ui->label_gpu_accuracy,
45 !Settings::values.gpu_accuracy.UsingGlobal()); 45 !Settings::values.gpu_accuracy.UsingGlobal());
46 ConfigurationShared::SetHighlight(ui->af_label, "af_label", 46 ConfigurationShared::SetHighlight(ui->af_label,
47 !Settings::values.max_anisotropy.UsingGlobal()); 47 !Settings::values.max_anisotropy.UsingGlobal());
48 } 48 }
49} 49}
@@ -131,20 +131,18 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
131 return; 131 return;
132 } 132 }
133 133
134 ConfigurationShared::SetColoredTristate(ui->use_vsync, "use_vsync", Settings::values.use_vsync, 134 ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync);
135 use_vsync);
136 ConfigurationShared::SetColoredTristate(ui->use_assembly_shaders, "use_assembly_shaders",
137 Settings::values.use_assembly_shaders,
138 use_assembly_shaders);
139 ConfigurationShared::SetColoredTristate( 135 ConfigurationShared::SetColoredTristate(
140 ui->use_asynchronous_shaders, "use_asynchronous_shaders", 136 ui->use_assembly_shaders, Settings::values.use_assembly_shaders, use_assembly_shaders);
141 Settings::values.use_asynchronous_shaders, use_asynchronous_shaders); 137 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders,
142 ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, "use_fast_gpu_time", 138 Settings::values.use_asynchronous_shaders,
139 use_asynchronous_shaders);
140 ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time,
143 Settings::values.use_fast_gpu_time, use_fast_gpu_time); 141 Settings::values.use_fast_gpu_time, use_fast_gpu_time);
144 ConfigurationShared::SetColoredComboBox( 142 ConfigurationShared::SetColoredComboBox(
145 ui->gpu_accuracy, ui->label_gpu_accuracy, "label_gpu_accuracy", 143 ui->gpu_accuracy, ui->label_gpu_accuracy,
146 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); 144 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
147 ConfigurationShared::SetColoredComboBox( 145 ConfigurationShared::SetColoredComboBox(
148 ui->anisotropic_filtering_combobox, ui->af_label, "af_label", 146 ui->anisotropic_filtering_combobox, ui->af_label,
149 static_cast<int>(Settings::values.max_anisotropy.GetValue(true))); 147 static_cast<int>(Settings::values.max_anisotropy.GetValue(true)));
150} 148}
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index a793c803d..846a30586 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -92,7 +92,7 @@
92 <string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string> 92 <string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
93 </property> 93 </property>
94 <property name="text"> 94 <property name="text">
95 <string>Use asynchronous shader building (experimental, OpenGL or Assembly shaders only)</string> 95 <string>Use asynchronous shader building (experimental)</string>
96 </property> 96 </property>
97 </widget> 97 </widget>
98 </item> 98 </item>
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
index 6f7fd4414..cbee51a5e 100644
--- a/src/yuzu/configuration/configure_hotkeys.cpp
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -154,7 +154,7 @@ void ConfigureHotkeys::ClearAll() {
154 const QStandardItem* parent = model->item(r, 0); 154 const QStandardItem* parent = model->item(r, 0);
155 155
156 for (int r2 = 0; r2 < parent->rowCount(); ++r2) { 156 for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
157 model->item(r, 0)->child(r2, 1)->setText(tr("")); 157 model->item(r, 0)->child(r2, 1)->setText(QString{});
158 } 158 }
159 } 159 }
160} 160}
@@ -186,7 +186,7 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) {
186 model->setData(selected, default_key_sequence.toString(QKeySequence::NativeText)); 186 model->setData(selected, default_key_sequence.toString(QKeySequence::NativeText));
187 } 187 }
188 }); 188 });
189 connect(clear, &QAction::triggered, [this, selected] { model->setData(selected, tr("")); }); 189 connect(clear, &QAction::triggered, [this, selected] { model->setData(selected, QString{}); });
190 190
191 context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location)); 191 context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location));
192} 192}
diff --git a/src/yuzu/configuration/configure_input.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 @@
8#include <QSignalBlocker> 8#include <QSignalBlocker>
9#include <QTimer> 9#include <QTimer>
10 10
11#include "configuration/configure_touchscreen_advanced.h"
12#include "core/core.h" 11#include "core/core.h"
13#include "core/hle/service/am/am.h" 12#include "core/hle/service/am/am.h"
14#include "core/hle/service/am/applet_ae.h" 13#include "core/hle/service/am/applet_ae.h"
15#include "core/hle/service/am/applet_oe.h" 14#include "core/hle/service/am/applet_oe.h"
16#include "core/hle/service/hid/controllers/npad.h"
17#include "core/hle/service/sm/sm.h" 15#include "core/hle/service/sm/sm.h"
18#include "ui_configure_input.h" 16#include "ui_configure_input.h"
17#include "ui_configure_input_advanced.h"
19#include "ui_configure_input_player.h" 18#include "ui_configure_input_player.h"
19#include "yuzu/configuration/configure_debug_controller.h"
20#include "yuzu/configuration/configure_input.h" 20#include "yuzu/configuration/configure_input.h"
21#include "yuzu/configuration/configure_input_advanced.h"
21#include "yuzu/configuration/configure_input_player.h" 22#include "yuzu/configuration/configure_input_player.h"
22#include "yuzu/configuration/configure_mouse_advanced.h" 23#include "yuzu/configuration/configure_mouse_advanced.h"
24#include "yuzu/configuration/configure_touchscreen_advanced.h"
25
26namespace {
27template <typename Dialog, typename... Args>
28void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
29 Dialog dialog(&parent, std::forward<Args>(args)...);
30
31 const auto res = dialog.exec();
32 if (res == QDialog::Accepted) {
33 dialog.ApplyConfiguration();
34 }
35}
36} // Anonymous namespace
23 37
24void OnDockedModeChanged(bool last_state, bool new_state) { 38void OnDockedModeChanged(bool last_state, bool new_state) {
25 if (last_state == new_state) { 39 if (last_state == new_state) {
@@ -48,97 +62,98 @@ void OnDockedModeChanged(bool last_state, bool new_state) {
48 } 62 }
49} 63}
50 64
51namespace {
52template <typename Dialog, typename... Args>
53void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
54 parent.ApplyConfiguration();
55 Dialog dialog(&parent, std::forward<Args>(args)...);
56
57 const auto res = dialog.exec();
58 if (res == QDialog::Accepted) {
59 dialog.ApplyConfiguration();
60 }
61}
62} // Anonymous namespace
63
64ConfigureInput::ConfigureInput(QWidget* parent) 65ConfigureInput::ConfigureInput(QWidget* parent)
65 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInput>()) { 66 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
66 ui->setupUi(this); 67 ui->setupUi(this);
68}
67 69
68 players_controller = { 70ConfigureInput::~ConfigureInput() = default;
69 ui->player1_combobox, ui->player2_combobox, ui->player3_combobox, ui->player4_combobox,
70 ui->player5_combobox, ui->player6_combobox, ui->player7_combobox, ui->player8_combobox,
71 };
72 71
73 players_configure = { 72void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
74 ui->player1_configure, ui->player2_configure, ui->player3_configure, ui->player4_configure, 73 player_controllers = {
75 ui->player5_configure, ui->player6_configure, ui->player7_configure, ui->player8_configure, 74 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem),
75 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem),
76 new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem),
77 new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem),
78 new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem),
79 new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem),
80 new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem),
81 new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem),
76 }; 82 };
77 83
78 RetranslateUI(); 84 player_tabs = {
79 LoadConfiguration(); 85 ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4,
80 UpdateUIEnabled(); 86 ui->tabPlayer5, ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8,
87 };
81 88
82 connect(ui->restore_defaults_button, &QPushButton::clicked, this, 89 player_connected = {
83 &ConfigureInput::RestoreDefaults); 90 ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
91 ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
92 ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
93 };
84 94
85 for (auto* enabled : players_controller) { 95 for (std::size_t i = 0; i < player_tabs.size(); ++i) {
86 connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 96 player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
87 &ConfigureInput::UpdateUIEnabled); 97 player_tabs[i]->layout()->addWidget(player_controllers[i]);
88 } 98 connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) {
89 connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); 99 if (is_connected) {
90 connect(ui->handheld_connected, &QCheckBox::stateChanged, this, 100 for (std::size_t index = 0; index <= i; ++index) {
91 &ConfigureInput::UpdateUIEnabled); 101 player_connected[index]->setChecked(is_connected);
92 connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); 102 }
93 connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); 103 } else {
94 connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); 104 for (std::size_t index = i; index < player_tabs.size(); ++index) {
95 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this, 105 player_connected[index]->setChecked(is_connected);
96 &ConfigureInput::UpdateUIEnabled); 106 }
97 107 }
98 for (std::size_t i = 0; i < players_configure.size(); ++i) { 108 });
99 connect(players_configure[i], &QPushButton::clicked, this, 109 connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices,
100 [this, i] { CallConfigureDialog<ConfigureInputPlayer>(*this, i, false); }); 110 [this] { UpdateAllInputDevices(); });
111 connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) {
112 player_controllers[i]->ConnectPlayer(state == Qt::Checked);
113 });
101 } 114 }
115 // Only the first player can choose handheld mode so connect the signal just to player 1
116 connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged,
117 [this](bool is_handheld) { UpdateDockedState(is_handheld); });
118
119 advanced = new ConfigureInputAdvanced(this);
120 ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
121 ui->tabAdvanced->layout()->addWidget(advanced);
122 connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] {
123 CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem);
124 });
125 connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] {
126 CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem);
127 });
128 connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog,
129 [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); });
102 130
103 connect(ui->handheld_configure, &QPushButton::clicked, this, 131 connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
104 [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 8, false); }); 132 connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
105
106 connect(ui->debug_configure, &QPushButton::clicked, this,
107 [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 9, true); });
108
109 connect(ui->mouse_advanced, &QPushButton::clicked, this,
110 [this] { CallConfigureDialog<ConfigureMouseAdvanced>(*this); });
111 133
112 connect(ui->touchscreen_advanced, &QPushButton::clicked, this, 134 RetranslateUI();
113 [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); }); 135 LoadConfiguration();
114} 136}
115 137
116ConfigureInput::~ConfigureInput() = default; 138QList<QWidget*> ConfigureInput::GetSubTabs() const {
139 return {
140 ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5,
141 ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8, ui->tabAdvanced,
142 };
143}
117 144
118void ConfigureInput::ApplyConfiguration() { 145void ConfigureInput::ApplyConfiguration() {
119 for (std::size_t i = 0; i < players_controller.size(); ++i) { 146 for (auto controller : player_controllers) {
120 const auto controller_type_index = players_controller[i]->currentIndex(); 147 controller->ApplyConfiguration();
121
122 Settings::values.players[i].connected = controller_type_index != 0;
123
124 if (controller_type_index > 0) {
125 Settings::values.players[i].type =
126 static_cast<Settings::ControllerType>(controller_type_index - 1);
127 } else {
128 Settings::values.players[i].type = Settings::ControllerType::DualJoycon;
129 }
130 } 148 }
131 149
150 advanced->ApplyConfiguration();
151
132 const bool pre_docked_mode = Settings::values.use_docked_mode; 152 const bool pre_docked_mode = Settings::values.use_docked_mode;
133 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); 153 Settings::values.use_docked_mode = ui->radioDocked->isChecked();
134 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode); 154 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
135 Settings::values 155
136 .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)] 156 Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
137 .connected = ui->handheld_connected->isChecked();
138 Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
139 Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
140 Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
141 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
142} 157}
143 158
144void ConfigureInput::changeEvent(QEvent* event) { 159void ConfigureInput::changeEvent(QEvent* event) {
@@ -146,94 +161,63 @@ void ConfigureInput::changeEvent(QEvent* event) {
146 RetranslateUI(); 161 RetranslateUI();
147 } 162 }
148 163
149 QDialog::changeEvent(event); 164 QWidget::changeEvent(event);
150} 165}
151 166
152void ConfigureInput::RetranslateUI() { 167void ConfigureInput::RetranslateUI() {
153 ui->retranslateUi(this); 168 ui->retranslateUi(this);
154 RetranslateControllerComboBoxes();
155} 169}
156 170
157void ConfigureInput::RetranslateControllerComboBoxes() { 171void ConfigureInput::LoadConfiguration() {
158 for (auto* controller_box : players_controller) {
159 [[maybe_unused]] const QSignalBlocker blocker(controller_box);
160
161 controller_box->clear();
162 controller_box->addItems({tr("None"), tr("Pro Controller"), tr("Dual Joycons"),
163 tr("Single Right Joycon"), tr("Single Left Joycon")});
164 }
165
166 LoadPlayerControllerIndices(); 172 LoadPlayerControllerIndices();
167} 173 UpdateDockedState(Settings::values.players[0].controller_type ==
174 Settings::ControllerType::Handheld);
168 175
169void ConfigureInput::UpdateUIEnabled() { 176 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
170 bool hit_disabled = false; 177}
171 for (auto* player : players_controller) {
172 player->setDisabled(hit_disabled);
173 if (hit_disabled) {
174 player->setCurrentIndex(0);
175 }
176 if (!hit_disabled && player->currentIndex() == 0) {
177 hit_disabled = true;
178 }
179 }
180 178
181 for (std::size_t i = 0; i < players_controller.size(); ++i) { 179void ConfigureInput::LoadPlayerControllerIndices() {
182 players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0); 180 for (std::size_t i = 0; i < player_connected.size(); ++i) {
181 const auto connected = Settings::values.players[i].connected ||
182 (i == 0 && Settings::values.players[8].connected);
183 player_connected[i]->setChecked(connected);
183 } 184 }
185}
184 186
185 ui->handheld_connected->setChecked(ui->handheld_connected->isChecked() && 187void ConfigureInput::ClearAll() {
186 !ui->use_docked_mode->isChecked()); 188 // We don't have a good way to know what tab is active, but we can find out by getting the
187 ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked()); 189 // parent of the consoleInputSettings
188 ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() && 190 auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent());
189 !ui->use_docked_mode->isChecked()); 191 player_tab->ClearAll();
190 ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
191 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
192 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
193} 192}
194 193
195void ConfigureInput::LoadConfiguration() { 194void ConfigureInput::RestoreDefaults() {
196 std::stable_partition( 195 // We don't have a good way to know what tab is active, but we can find out by getting the
197 Settings::values.players.begin(), 196 // parent of the consoleInputSettings
198 Settings::values.players.begin() + 197 auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent());
199 Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD), 198 player_tab->RestoreDefaults();
200 [](const auto& player) { return player.connected; }); 199
200 ui->radioDocked->setChecked(true);
201 ui->radioUndocked->setChecked(false);
202 ui->vibrationGroup->setChecked(true);
203}
201 204
202 LoadPlayerControllerIndices(); 205void ConfigureInput::UpdateDockedState(bool is_handheld) {
206 // If the controller type is handheld only, disallow changing docked mode
207 ui->radioDocked->setEnabled(!is_handheld);
208 ui->radioUndocked->setEnabled(!is_handheld);
203 209
204 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); 210 ui->radioDocked->setChecked(Settings::values.use_docked_mode);
205 ui->handheld_connected->setChecked( 211 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode);
206 Settings::values
207 .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
208 .connected);
209 ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
210 ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
211 ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
212 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
213
214 UpdateUIEnabled();
215}
216 212
217void ConfigureInput::LoadPlayerControllerIndices() { 213 // If its handheld only, force docked mode off (since you can't play handheld in a dock)
218 for (std::size_t i = 0; i < players_controller.size(); ++i) { 214 if (is_handheld) {
219 const auto connected = Settings::values.players[i].connected; 215 ui->radioUndocked->setChecked(true);
220 players_controller[i]->setCurrentIndex(
221 connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0);
222 } 216 }
223} 217}
224 218
225void ConfigureInput::RestoreDefaults() { 219void ConfigureInput::UpdateAllInputDevices() {
226 players_controller[0]->setCurrentIndex(2); 220 for (const auto& player : player_controllers) {
227 221 player->UpdateInputDevices();
228 for (std::size_t i = 1; i < players_controller.size(); ++i) {
229 players_controller[i]->setCurrentIndex(0);
230 } 222 }
231
232 ui->use_docked_mode->setCheckState(Qt::Unchecked);
233 ui->handheld_connected->setCheckState(Qt::Unchecked);
234 ui->mouse_enabled->setCheckState(Qt::Unchecked);
235 ui->keyboard_enabled->setCheckState(Qt::Unchecked);
236 ui->debug_enabled->setCheckState(Qt::Unchecked);
237 ui->touchscreen_enabled->setCheckState(Qt::Checked);
238 UpdateUIEnabled();
239} 223}
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 @@
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9 9
10#include <QDialog>
11#include <QKeyEvent> 10#include <QKeyEvent>
11#include <QWidget>
12
13#include "yuzu/configuration/configure_input_advanced.h"
14#include "yuzu/configuration/configure_input_player.h"
12 15
13#include "ui_configure_input.h" 16#include "ui_configure_input.h"
14 17
15class QPushButton; 18class QCheckBox;
16class QString; 19class QString;
17class QTimer; 20class QTimer;
18 21
22namespace InputCommon {
23class InputSubsystem;
24}
25
19namespace Ui { 26namespace Ui {
20class ConfigureInput; 27class ConfigureInput;
21} 28}
22 29
23void OnDockedModeChanged(bool last_state, bool new_state); 30void OnDockedModeChanged(bool last_state, bool new_state);
24 31
25class ConfigureInput : public QDialog { 32class ConfigureInput : public QWidget {
26 Q_OBJECT 33 Q_OBJECT
27 34
28public: 35public:
29 explicit ConfigureInput(QWidget* parent = nullptr); 36 explicit ConfigureInput(QWidget* parent = nullptr);
30 ~ConfigureInput() override; 37 ~ConfigureInput() override;
31 38
32 /// Save all button configurations to settings file 39 /// Initializes the input dialog with the given input subsystem.
40 void Initialize(InputCommon::InputSubsystem* input_subsystem_);
41
42 /// Save all button configurations to settings file.
33 void ApplyConfiguration(); 43 void ApplyConfiguration();
34 44
45 QList<QWidget*> GetSubTabs() const;
46
35private: 47private:
36 void changeEvent(QEvent* event) override; 48 void changeEvent(QEvent* event) override;
37 void RetranslateUI(); 49 void RetranslateUI();
38 void RetranslateControllerComboBoxes(); 50 void ClearAll();
39 51
40 void UpdateUIEnabled(); 52 void UpdateDockedState(bool is_handheld);
53 void UpdateAllInputDevices();
41 54
42 /// Load configuration settings. 55 /// Load configuration settings.
43 void LoadConfiguration(); 56 void LoadConfiguration();
@@ -48,6 +61,8 @@ private:
48 61
49 std::unique_ptr<Ui::ConfigureInput> ui; 62 std::unique_ptr<Ui::ConfigureInput> ui;
50 63
51 std::array<QComboBox*, 8> players_controller; 64 std::array<ConfigureInputPlayer*, 8> player_controllers;
52 std::array<QPushButton*, 8> players_configure; 65 std::array<QWidget*, 8> player_tabs;
66 std::array<QCheckBox*, 8> player_connected;
67 ConfigureInputAdvanced* advanced;
53}; 68};
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 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureInput</class> 3 <class>ConfigureInput</class>
4 <widget class="QDialog" name="ConfigureInput"> 4 <widget class="QWidget" name="ConfigureInput">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>384</width> 9 <width>700</width>
10 <height>576</height> 10 <height>540</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
14 <string>Custom Input Settings</string> 14 <string>ConfigureInput</string>
15 </property> 15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5"> 16 <layout class="QVBoxLayout" name="verticalLayout_5">
17 <property name="spacing">
18 <number>2</number>
19 </property>
20 <property name="leftMargin">
21 <number>3</number>
22 </property>
23 <property name="topMargin">
24 <number>3</number>
25 </property>
26 <property name="rightMargin">
27 <number>3</number>
28 </property>
29 <property name="bottomMargin">
30 <number>3</number>
31 </property>
17 <item> 32 <item>
18 <layout class="QVBoxLayout" name="verticalLayout"> 33 <widget class="QTabWidget" name="tabWidget">
19 <item> 34 <property name="currentIndex">
20 <widget class="QGroupBox" name="gridGroupBox_1"> 35 <number>0</number>
21 <property name="title"> 36 </property>
22 <string>Players</string> 37 <widget class="QWidget" name="tabPlayer1">
23 </property> 38 <property name="accessibleName">
24 <layout class="QGridLayout" name="gridLayout"> 39 <string>Player 1</string>
25 <item row="1" column="2"> 40 </property>
26 <widget class="QComboBox" name="player1_combobox"> 41 <attribute name="title">
27 <property name="minimumSize"> 42 <string>Player 1</string>
28 <size> 43 </attribute>
29 <width>110</width> 44 </widget>
30 <height>0</height> 45 <widget class="QWidget" name="tabPlayer2">
31 </size> 46 <property name="accessibleName">
32 </property> 47 <string>Player 2</string>
33 </widget> 48 </property>
34 </item> 49 <attribute name="title">
35 <item row="1" column="3"> 50 <string>Player 2</string>
36 <widget class="QPushButton" name="player1_configure"> 51 </attribute>
37 <property name="text"> 52 </widget>
38 <string>Configure</string> 53 <widget class="QWidget" name="tabPlayer3">
39 </property> 54 <property name="accessibleName">
40 </widget> 55 <string>Player 3</string>
41 </item> 56 </property>
42 <item row="0" column="2"> 57 <attribute name="title">
43 <widget class="QLabel" name="label"> 58 <string>Player 3</string>
44 <property name="text"> 59 </attribute>
45 <string>Controller Type</string> 60 </widget>
46 </property> 61 <widget class="QWidget" name="tabPlayer4">
47 <property name="alignment"> 62 <property name="accessibleName">
48 <set>Qt::AlignCenter</set> 63 <string>Player 4</string>
49 </property> 64 </property>
50 </widget> 65 <attribute name="title">
51 </item> 66 <string>Player 4</string>
52 <item row="2" column="2"> 67 </attribute>
53 <widget class="QComboBox" name="player2_combobox"> 68 </widget>
54 <property name="minimumSize"> 69 <widget class="QWidget" name="tabPlayer5">
55 <size> 70 <property name="accessibleName">
56 <width>110</width> 71 <string>Player 5</string>
57 <height>0</height> 72 </property>
58 </size> 73 <attribute name="title">
59 </property> 74 <string>Player 5</string>
60 </widget> 75 </attribute>
61 </item> 76 </widget>
62 <item row="3" column="2"> 77 <widget class="QWidget" name="tabPlayer6">
63 <widget class="QComboBox" name="player3_combobox"> 78 <property name="accessibleName">
64 <property name="minimumSize"> 79 <string>Player 6</string>
65 <size> 80 </property>
66 <width>110</width> 81 <attribute name="title">
67 <height>0</height> 82 <string>Player 6</string>
68 </size> 83 </attribute>
69 </property> 84 </widget>
70 </widget> 85 <widget class="QWidget" name="tabPlayer7">
71 </item> 86 <property name="accessibleName">
72 <item row="4" column="2"> 87 <string>Player 7</string>
73 <widget class="QComboBox" name="player4_combobox"> 88 </property>
74 <property name="minimumSize"> 89 <attribute name="title">
75 <size> 90 <string>Player 7</string>
76 <width>110</width> 91 </attribute>
77 <height>0</height> 92 </widget>
78 </size> 93 <widget class="QWidget" name="tabPlayer8">
79 </property> 94 <property name="accessibleName">
80 </widget> 95 <string>Player 8</string>
81 </item> 96 </property>
82 <item row="5" column="2"> 97 <attribute name="title">
83 <widget class="QComboBox" name="player5_combobox"> 98 <string>Player 8</string>
84 <property name="minimumSize"> 99 </attribute>
85 <size> 100 </widget>
86 <width>110</width> 101 <widget class="QWidget" name="tabAdvanced">
87 <height>0</height> 102 <property name="accessibleName">
88 </size> 103 <string>Advanced</string>
89 </property> 104 </property>
90 </widget> 105 <attribute name="title">
91 </item> 106 <string>Advanced</string>
92 <item row="6" column="2"> 107 </attribute>
93 <widget class="QComboBox" name="player6_combobox"> 108 </widget>
94 <property name="minimumSize"> 109 </widget>
95 <size> 110 </item>
96 <width>110</width> 111 <item alignment="Qt::AlignVCenter">
97 <height>0</height> 112 <widget class="QWidget" name="consoleInputSettings" native="true">
98 </size> 113 <layout class="QHBoxLayout" name="buttonsBottomRightHorizontalLayout">
99 </property> 114 <property name="spacing">
100 </widget> 115 <number>3</number>
101 </item> 116 </property>
102 <item row="7" column="2"> 117 <property name="leftMargin">
103 <widget class="QComboBox" name="player7_combobox"> 118 <number>0</number>
104 <property name="minimumSize"> 119 </property>
105 <size> 120 <property name="topMargin">
106 <width>110</width> 121 <number>3</number>
107 <height>0</height> 122 </property>
108 </size> 123 <property name="rightMargin">
109 </property> 124 <number>0</number>
110 </widget> 125 </property>
111 </item> 126 <property name="bottomMargin">
112 <item row="8" column="2"> 127 <number>0</number>
113 <widget class="QComboBox" name="player8_combobox"> 128 </property>
114 <property name="minimumSize"> 129 <item alignment="Qt::AlignVCenter">
115 <size> 130 <widget class="QGroupBox" name="handheldGroup">
116 <width>110</width> 131 <property name="maximumSize">
117 <height>0</height> 132 <size>
118 </size> 133 <width>16777215</width>
119 </property> 134 <height>16777215</height>
120 </widget> 135 </size>
121 </item> 136 </property>
122 <item row="2" column="3"> 137 <property name="title">
123 <widget class="QPushButton" name="player2_configure"> 138 <string>Console Mode</string>
124 <property name="text"> 139 </property>
125 <string>Configure</string> 140 <layout class="QHBoxLayout" name="horizontalLayout">
126 </property> 141 <property name="spacing">
127 </widget> 142 <number>6</number>
128 </item> 143 </property>
129 <item row="3" column="3"> 144 <property name="leftMargin">
130 <widget class="QPushButton" name="player3_configure"> 145 <number>3</number>
131 <property name="text"> 146 </property>
132 <string>Configure</string> 147 <property name="topMargin">
133 </property> 148 <number>6</number>
134 </widget> 149 </property>
135 </item> 150 <property name="rightMargin">
136 <item row="4" column="3"> 151 <number>3</number>
137 <widget class="QPushButton" name="player4_configure"> 152 </property>
138 <property name="text"> 153 <property name="bottomMargin">
139 <string>Configure</string> 154 <number>6</number>
140 </property> 155 </property>
141 </widget> 156 <item>
142 </item> 157 <widget class="QRadioButton" name="radioDocked">
143 <item row="5" column="3"> 158 <property name="text">
144 <widget class="QPushButton" name="player5_configure"> 159 <string>Docked</string>
145 <property name="text"> 160 </property>
146 <string>Configure</string> 161 <property name="checked">
147 </property> 162 <bool>true</bool>
148 </widget> 163 </property>
149 </item> 164 </widget>
150 <item row="6" column="3"> 165 </item>
151 <widget class="QPushButton" name="player6_configure"> 166 <item>
152 <property name="text"> 167 <widget class="QRadioButton" name="radioUndocked">
153 <string>Configure</string> 168 <property name="text">
154 </property> 169 <string>Undocked</string>
155 </widget> 170 </property>
156 </item> 171 </widget>
157 <item row="7" column="3"> 172 </item>
158 <widget class="QPushButton" name="player7_configure"> 173 </layout>
159 <property name="text"> 174 </widget>
160 <string>Configure</string> 175 </item>
161 </property> 176 <item>
162 </widget> 177 <widget class="QGroupBox" name="vibrationGroup">
163 </item> 178 <property name="title">
164 <item row="8" column="3"> 179 <string>Vibration</string>
165 <widget class="QPushButton" name="player8_configure"> 180 </property>
166 <property name="text"> 181 <property name="checkable">
167 <string>Configure</string> 182 <bool>true</bool>
168 </property> 183 </property>
169 </widget> 184 <layout class="QHBoxLayout" name="horizontalLayout_2">
170 </item> 185 <property name="leftMargin">
171 <item row="0" column="0"> 186 <number>3</number>
172 <spacer name="horizontalSpacer"> 187 </property>
173 <property name="orientation"> 188 <property name="topMargin">
174 <enum>Qt::Horizontal</enum> 189 <number>3</number>
175 </property> 190 </property>
176 <property name="sizeHint" stdset="0"> 191 <property name="rightMargin">
177 <size> 192 <number>3</number>
178 <width>40</width> 193 </property>
179 <height>20</height> 194 <property name="bottomMargin">
180 </size> 195 <number>3</number>
181 </property> 196 </property>
182 </spacer> 197 <item>
183 </item> 198 <widget class="QSpinBox" name="vibrationSpin">
184 <item row="0" column="4"> 199 <property name="minimumSize">
185 <spacer name="horizontalSpacer_2"> 200 <size>
186 <property name="orientation"> 201 <width>65</width>
187 <enum>Qt::Horizontal</enum> 202 <height>21</height>
188 </property> 203 </size>
189 <property name="sizeHint" stdset="0"> 204 </property>
190 <size> 205 <property name="maximumSize">
191 <width>40</width> 206 <size>
192 <height>20</height> 207 <width>65</width>
193 </size> 208 <height>16777215</height>
194 </property> 209 </size>
195 </spacer> 210 </property>
196 </item> 211 <property name="suffix">
197 <item row="1" column="1"> 212 <string>%</string>
198 <widget class="QLabel" name="label_3"> 213 </property>
199 <property name="minimumSize"> 214 <property name="minimum">
200 <size> 215 <number>1</number>
201 <width>55</width> 216 </property>
202 <height>0</height> 217 <property name="maximum">
203 </size> 218 <number>200</number>
204 </property> 219 </property>
205 <property name="text"> 220 <property name="value">
206 <string>Player 1</string> 221 <number>100</number>
207 </property> 222 </property>
208 </widget> 223 </widget>
209 </item> 224 </item>
210 <item row="2" column="1"> 225 </layout>
211 <widget class="QLabel" name="label_4"> 226 </widget>
212 <property name="text"> 227 </item>
213 <string>Player 2</string> 228 <item>
214 </property> 229 <widget class="QGroupBox" name="motionGroup">
215 </widget> 230 <property name="title">
216 </item> 231 <string>Motion</string>
217 <item row="3" column="1"> 232 </property>
218 <widget class="QLabel" name="label_5"> 233 <property name="checkable">
219 <property name="text"> 234 <bool>true</bool>
220 <string>Player 3</string> 235 </property>
221 </property> 236 <layout class="QHBoxLayout" name="horizontalLayout_3">
222 </widget> 237 <property name="leftMargin">
223 </item> 238 <number>3</number>
224 <item row="4" column="1"> 239 </property>
225 <widget class="QLabel" name="label_6"> 240 <property name="topMargin">
226 <property name="text"> 241 <number>3</number>
227 <string>Player 4</string> 242 </property>
228 </property> 243 <property name="rightMargin">
229 </widget> 244 <number>3</number>
230 </item> 245 </property>
231 <item row="5" column="1"> 246 <property name="bottomMargin">
232 <widget class="QLabel" name="label_7"> 247 <number>3</number>
233 <property name="text"> 248 </property>
234 <string>Player 5</string> 249 <item>
235 </property> 250 <widget class="QPushButton" name="motionButton">
236 </widget> 251 <property name="minimumSize">
237 </item> 252 <size>
238 <item row="6" column="1"> 253 <width>57</width>
239 <widget class="QLabel" name="label_8"> 254 <height>0</height>
240 <property name="text"> 255 </size>
241 <string>Player 6</string> 256 </property>
242 </property> 257 <property name="maximumSize">
243 </widget> 258 <size>
244 </item> 259 <width>55</width>
245 <item row="7" column="1"> 260 <height>16777215</height>
246 <widget class="QLabel" name="label_9"> 261 </size>
247 <property name="text"> 262 </property>
248 <string>Player 7</string> 263 <property name="styleSheet">
249 </property> 264 <string notr="true">min-width: 55px;</string>
250 </widget> 265 </property>
251 </item> 266 <property name="text">
252 <item row="8" column="1"> 267 <string>Configure</string>
253 <widget class="QLabel" name="label_10"> 268 </property>
254 <property name="text"> 269 </widget>
255 <string>Player 8</string> 270 </item>
256 </property> 271 </layout>
257 </widget> 272 </widget>
258 </item> 273 </item>
259 </layout> 274 <item alignment="Qt::AlignVCenter">
260 </widget> 275 <widget class="QWidget" name="widget" native="true">
261 </item> 276 <layout class="QGridLayout" name="gridLayout_2">
262 <item> 277 <property name="leftMargin">
263 <widget class="QGroupBox" name="gridGroupBox_2"> 278 <number>5</number>
264 <property name="title"> 279 </property>
265 <string>Handheld</string> 280 <property name="topMargin">
266 </property> 281 <number>0</number>
267 <layout class="QGridLayout" name="gridLayout_2">
268 <item row="1" column="2">
269 <spacer name="horizontalSpacer_5">
270 <property name="orientation">
271 <enum>Qt::Horizontal</enum>
272 </property>
273 <property name="sizeType">
274 <enum>QSizePolicy::Fixed</enum>
275 </property>
276 <property name="sizeHint" stdset="0">
277 <size>
278 <width>72</width>
279 <height>20</height>
280 </size>
281 </property>
282 </spacer>
283 </item>
284 <item row="1" column="4">
285 <spacer name="horizontalSpacer_4">
286 <property name="orientation">
287 <enum>Qt::Horizontal</enum>
288 </property>
289 <property name="sizeHint" stdset="0">
290 <size>
291 <width>40</width>
292 <height>20</height>
293 </size>
294 </property>
295 </spacer>
296 </item>
297 <item row="1" column="3">
298 <widget class="QPushButton" name="handheld_configure">
299 <property name="text">
300 <string>Configure</string>
301 </property>
302 </widget>
303 </item>
304 <item row="1" column="0">
305 <spacer name="horizontalSpacer_3">
306 <property name="orientation">
307 <enum>Qt::Horizontal</enum>
308 </property>
309 <property name="sizeHint" stdset="0">
310 <size>
311 <width>40</width>
312 <height>20</height>
313 </size>
314 </property>
315 </spacer>
316 </item>
317 <item row="1" column="1">
318 <widget class="QCheckBox" name="handheld_connected">
319 <property name="text">
320 <string>Joycons Docked</string>
321 </property>
322 </widget>
323 </item>
324 <item row="0" column="1">
325 <widget class="QCheckBox" name="use_docked_mode">
326 <property name="text">
327 <string>Use Docked Mode</string>
328 </property>
329 </widget>
330 </item>
331 </layout>
332 </widget>
333 </item>
334 <item>
335 <widget class="QGroupBox" name="gridGroupBox_3">
336 <property name="title">
337 <string>Other</string>
338 </property>
339 <layout class="QGridLayout" name="gridLayout_3">
340 <item row="1" column="1">
341 <widget class="QCheckBox" name="keyboard_enabled">
342 <property name="minimumSize">
343 <size>
344 <width>0</width>
345 <height>23</height>
346 </size>
347 </property>
348 <property name="text">
349 <string>Keyboard</string>
350 </property>
351 </widget>
352 </item>
353 <item row="2" column="1">
354 <widget class="QCheckBox" name="debug_enabled">
355 <property name="text">
356 <string>Debug Controller</string>
357 </property>
358 </widget>
359 </item>
360 <item row="3" column="1">
361 <widget class="QCheckBox" name="touchscreen_enabled">
362 <property name="text">
363 <string>Touchscreen</string>
364 </property>
365 </widget>
366 </item>
367 <item row="0" column="1">
368 <widget class="QCheckBox" name="mouse_enabled">
369 <property name="minimumSize">
370 <size>
371 <width>0</width>
372 <height>23</height>
373 </size>
374 </property>
375 <property name="text">
376 <string>Mouse</string>
377 </property>
378 </widget>
379 </item>
380 <item row="0" column="4">
381 <spacer name="horizontalSpacer_7">
382 <property name="orientation">
383 <enum>Qt::Horizontal</enum>
384 </property>
385 <property name="sizeHint" stdset="0">
386 <size>
387 <width>40</width>
388 <height>20</height>
389 </size>
390 </property>
391 </spacer>
392 </item>
393 <item row="0" column="2">
394 <spacer name="horizontalSpacer_8">
395 <property name="orientation">
396 <enum>Qt::Horizontal</enum>
397 </property>
398 <property name="sizeType">
399 <enum>QSizePolicy::Fixed</enum>
400 </property>
401 <property name="sizeHint" stdset="0">
402 <size>
403 <width>76</width>
404 <height>20</height>
405 </size>
406 </property>
407 </spacer>
408 </item>
409 <item row="0" column="0">
410 <spacer name="horizontalSpacer_6">
411 <property name="orientation">
412 <enum>Qt::Horizontal</enum>
413 </property>
414 <property name="sizeHint" stdset="0">
415 <size>
416 <width>40</width>
417 <height>20</height>
418 </size>
419 </property>
420 </spacer>
421 </item>
422 <item row="3" column="3">
423 <widget class="QPushButton" name="touchscreen_advanced">
424 <property name="text">
425 <string>Advanced</string>
426 </property>
427 </widget>
428 </item>
429 <item row="2" column="3">
430 <widget class="QPushButton" name="debug_configure">
431 <property name="text">
432 <string>Configure</string>
433 </property>
434 </widget>
435 </item>
436 <item row="0" column="3">
437 <widget class="QPushButton" name="mouse_advanced">
438 <property name="text">
439 <string>Advanced</string>
440 </property>
441 </widget>
442 </item>
443 </layout>
444 </widget>
445 </item>
446 <item>
447 <spacer name="verticalSpacer">
448 <property name="orientation">
449 <enum>Qt::Vertical</enum>
450 </property>
451 <property name="sizeHint" stdset="0">
452 <size>
453 <width>20</width>
454 <height>40</height>
455 </size>
456 </property>
457 </spacer>
458 </item>
459 <item>
460 <layout class="QHBoxLayout" name="horizontalLayout">
461 <item>
462 <widget class="QPushButton" name="restore_defaults_button">
463 <property name="text">
464 <string>Restore Defaults</string>
465 </property> 282 </property>
466 </widget> 283 <property name="rightMargin">
467 </item> 284 <number>0</number>
468 <item>
469 <spacer name="horizontalSpacer_9">
470 <property name="orientation">
471 <enum>Qt::Horizontal</enum>
472 </property> 285 </property>
473 <property name="sizeHint" stdset="0"> 286 <property name="bottomMargin">
474 <size> 287 <number>0</number>
475 <width>40</width>
476 <height>20</height>
477 </size>
478 </property> 288 </property>
479 </spacer> 289 <property name="spacing">
480 </item> 290 <number>3</number>
481 <item>
482 <widget class="QDialogButtonBox" name="buttonBox">
483 <property name="standardButtons">
484 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
485 </property> 291 </property>
486 </widget> 292 <item row="1" column="2">
487 </item> 293 <widget class="QCheckBox" name="checkboxPlayer2Connected">
488 </layout> 294 <property name="text">
489 </item> 295 <string/>
490 </layout> 296 </property>
297 </widget>
298 </item>
299 <item row="1" column="0">
300 <widget class="QLabel" name="label_2">
301 <property name="text">
302 <string>Controllers</string>
303 </property>
304 </widget>
305 </item>
306 <item row="1" column="4">
307 <widget class="QCheckBox" name="checkboxPlayer4Connected">
308 <property name="text">
309 <string/>
310 </property>
311 </widget>
312 </item>
313 <item row="1" column="3">
314 <widget class="QCheckBox" name="checkboxPlayer3Connected">
315 <property name="text">
316 <string/>
317 </property>
318 </widget>
319 </item>
320 <item row="1" column="5">
321 <widget class="QCheckBox" name="checkboxPlayer5Connected">
322 <property name="text">
323 <string/>
324 </property>
325 </widget>
326 </item>
327 <item row="0" column="1">
328 <widget class="QLabel" name="label">
329 <property name="text">
330 <string>1</string>
331 </property>
332 <property name="alignment">
333 <set>Qt::AlignCenter</set>
334 </property>
335 </widget>
336 </item>
337 <item row="1" column="7">
338 <widget class="QCheckBox" name="checkboxPlayer7Connected">
339 <property name="text">
340 <string/>
341 </property>
342 </widget>
343 </item>
344 <item row="1" column="6">
345 <widget class="QCheckBox" name="checkboxPlayer6Connected">
346 <property name="text">
347 <string/>
348 </property>
349 </widget>
350 </item>
351 <item row="1" column="1">
352 <widget class="QCheckBox" name="checkboxPlayer1Connected">
353 <property name="layoutDirection">
354 <enum>Qt::LeftToRight</enum>
355 </property>
356 <property name="checked">
357 <bool>true</bool>
358 </property>
359 </widget>
360 </item>
361 <item row="1" column="8">
362 <widget class="QCheckBox" name="checkboxPlayer8Connected">
363 <property name="text">
364 <string/>
365 </property>
366 </widget>
367 </item>
368 <item row="0" column="2">
369 <widget class="QLabel" name="label_3">
370 <property name="text">
371 <string>2</string>
372 </property>
373 <property name="alignment">
374 <set>Qt::AlignCenter</set>
375 </property>
376 </widget>
377 </item>
378 <item row="0" column="3">
379 <widget class="QLabel" name="label_4">
380 <property name="text">
381 <string>3</string>
382 </property>
383 <property name="alignment">
384 <set>Qt::AlignCenter</set>
385 </property>
386 </widget>
387 </item>
388 <item row="0" column="4">
389 <widget class="QLabel" name="label_5">
390 <property name="text">
391 <string>4</string>
392 </property>
393 <property name="alignment">
394 <set>Qt::AlignCenter</set>
395 </property>
396 </widget>
397 </item>
398 <item row="0" column="5">
399 <widget class="QLabel" name="label_6">
400 <property name="text">
401 <string>5</string>
402 </property>
403 <property name="alignment">
404 <set>Qt::AlignCenter</set>
405 </property>
406 </widget>
407 </item>
408 <item row="0" column="6">
409 <widget class="QLabel" name="label_7">
410 <property name="text">
411 <string>6</string>
412 </property>
413 <property name="alignment">
414 <set>Qt::AlignCenter</set>
415 </property>
416 </widget>
417 </item>
418 <item row="0" column="7">
419 <widget class="QLabel" name="label_8">
420 <property name="text">
421 <string>7</string>
422 </property>
423 <property name="alignment">
424 <set>Qt::AlignCenter</set>
425 </property>
426 </widget>
427 </item>
428 <item row="0" column="8">
429 <widget class="QLabel" name="label_9">
430 <property name="text">
431 <string>8</string>
432 </property>
433 <property name="alignment">
434 <set>Qt::AlignCenter</set>
435 </property>
436 </widget>
437 </item>
438 <item row="0" column="0">
439 <widget class="QLabel" name="label_10">
440 <property name="text">
441 <string>Connected</string>
442 </property>
443 </widget>
444 </item>
445 </layout>
446 </widget>
447 </item>
448 <item>
449 <spacer name="horizontalSpacer">
450 <property name="orientation">
451 <enum>Qt::Horizontal</enum>
452 </property>
453 <property name="sizeHint" stdset="0">
454 <size>
455 <width>40</width>
456 <height>20</height>
457 </size>
458 </property>
459 </spacer>
460 </item>
461 <item alignment="Qt::AlignBottom">
462 <widget class="QPushButton" name="buttonRestoreDefaults">
463 <property name="sizePolicy">
464 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
465 <horstretch>0</horstretch>
466 <verstretch>0</verstretch>
467 </sizepolicy>
468 </property>
469 <property name="minimumSize">
470 <size>
471 <width>57</width>
472 <height>0</height>
473 </size>
474 </property>
475 <property name="maximumSize">
476 <size>
477 <width>55</width>
478 <height>16777215</height>
479 </size>
480 </property>
481 <property name="sizeIncrement">
482 <size>
483 <width>0</width>
484 <height>0</height>
485 </size>
486 </property>
487 <property name="baseSize">
488 <size>
489 <width>0</width>
490 <height>0</height>
491 </size>
492 </property>
493 <property name="layoutDirection">
494 <enum>Qt::LeftToRight</enum>
495 </property>
496 <property name="styleSheet">
497 <string notr="true">min-width: 55px;</string>
498 </property>
499 <property name="text">
500 <string>Defaults</string>
501 </property>
502 </widget>
503 </item>
504 <item alignment="Qt::AlignBottom">
505 <widget class="QPushButton" name="buttonClearAll">
506 <property name="sizePolicy">
507 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
508 <horstretch>0</horstretch>
509 <verstretch>0</verstretch>
510 </sizepolicy>
511 </property>
512 <property name="minimumSize">
513 <size>
514 <width>57</width>
515 <height>0</height>
516 </size>
517 </property>
518 <property name="maximumSize">
519 <size>
520 <width>55</width>
521 <height>16777215</height>
522 </size>
523 </property>
524 <property name="sizeIncrement">
525 <size>
526 <width>0</width>
527 <height>0</height>
528 </size>
529 </property>
530 <property name="baseSize">
531 <size>
532 <width>0</width>
533 <height>0</height>
534 </size>
535 </property>
536 <property name="layoutDirection">
537 <enum>Qt::LeftToRight</enum>
538 </property>
539 <property name="styleSheet">
540 <string notr="true">min-width: 55px;</string>
541 </property>
542 <property name="text">
543 <string>Clear</string>
544 </property>
545 </widget>
546 </item>
547 </layout>
548 </widget>
491 </item> 549 </item>
492 </layout> 550 </layout>
493 </widget> 551 </widget>
494 <resources/> 552 <resources/>
495 <connections> 553 <connections/>
496 <connection>
497 <sender>buttonBox</sender>
498 <signal>accepted()</signal>
499 <receiver>ConfigureInput</receiver>
500 <slot>accept()</slot>
501 <hints>
502 <hint type="sourcelabel">
503 <x>294</x>
504 <y>553</y>
505 </hint>
506 <hint type="destinationlabel">
507 <x>191</x>
508 <y>287</y>
509 </hint>
510 </hints>
511 </connection>
512 <connection>
513 <sender>buttonBox</sender>
514 <signal>rejected()</signal>
515 <receiver>ConfigureInput</receiver>
516 <slot>reject()</slot>
517 <hints>
518 <hint type="sourcelabel">
519 <x>294</x>
520 <y>553</y>
521 </hint>
522 <hint type="destinationlabel">
523 <x>191</x>
524 <y>287</y>
525 </hint>
526 </hints>
527 </connection>
528 </connections>
529</ui> 554</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 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QColorDialog>
6#include "core/core.h"
7#include "core/settings.h"
8#include "ui_configure_input_advanced.h"
9#include "yuzu/configuration/configure_input_advanced.h"
10
11ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
12 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()) {
13 ui->setupUi(this);
14
15 controllers_color_buttons = {{
16 {
17 ui->player1_left_body_button,
18 ui->player1_left_buttons_button,
19 ui->player1_right_body_button,
20 ui->player1_right_buttons_button,
21 },
22 {
23 ui->player2_left_body_button,
24 ui->player2_left_buttons_button,
25 ui->player2_right_body_button,
26 ui->player2_right_buttons_button,
27 },
28 {
29 ui->player3_left_body_button,
30 ui->player3_left_buttons_button,
31 ui->player3_right_body_button,
32 ui->player3_right_buttons_button,
33 },
34 {
35 ui->player4_left_body_button,
36 ui->player4_left_buttons_button,
37 ui->player4_right_body_button,
38 ui->player4_right_buttons_button,
39 },
40 {
41 ui->player5_left_body_button,
42 ui->player5_left_buttons_button,
43 ui->player5_right_body_button,
44 ui->player5_right_buttons_button,
45 },
46 {
47 ui->player6_left_body_button,
48 ui->player6_left_buttons_button,
49 ui->player6_right_body_button,
50 ui->player6_right_buttons_button,
51 },
52 {
53 ui->player7_left_body_button,
54 ui->player7_left_buttons_button,
55 ui->player7_right_body_button,
56 ui->player7_right_buttons_button,
57 },
58 {
59 ui->player8_left_body_button,
60 ui->player8_left_buttons_button,
61 ui->player8_right_body_button,
62 ui->player8_right_buttons_button,
63 },
64 }};
65
66 for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
67 auto& color_buttons = controllers_color_buttons[player_idx];
68 for (std::size_t button_idx = 0; button_idx < color_buttons.size(); ++button_idx) {
69 connect(color_buttons[button_idx], &QPushButton::clicked, this,
70 [this, player_idx, button_idx] {
71 OnControllerButtonClick(static_cast<int>(player_idx),
72 static_cast<int>(button_idx));
73 });
74 }
75 }
76
77 connect(ui->mouse_enabled, &QCheckBox::stateChanged, this,
78 &ConfigureInputAdvanced::UpdateUIEnabled);
79 connect(ui->debug_enabled, &QCheckBox::stateChanged, this,
80 &ConfigureInputAdvanced::UpdateUIEnabled);
81 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
82 &ConfigureInputAdvanced::UpdateUIEnabled);
83
84 connect(ui->debug_configure, &QPushButton::clicked, this,
85 [this] { CallDebugControllerDialog(); });
86 connect(ui->mouse_advanced, &QPushButton::clicked, this, [this] { CallMouseConfigDialog(); });
87 connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
88 [this] { CallTouchscreenConfigDialog(); });
89
90 LoadConfiguration();
91}
92
93ConfigureInputAdvanced::~ConfigureInputAdvanced() = default;
94
95void ConfigureInputAdvanced::OnControllerButtonClick(int player_idx, int button_idx) {
96 const QColor new_bg_color = QColorDialog::getColor(controllers_colors[player_idx][button_idx]);
97 if (!new_bg_color.isValid()) {
98 return;
99 }
100 controllers_colors[player_idx][button_idx] = new_bg_color;
101 controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
102 QStringLiteral("background-color: %1; min-width: 55px;")
103 .arg(controllers_colors[player_idx][button_idx].name()));
104}
105
106void ConfigureInputAdvanced::ApplyConfiguration() {
107 for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
108 auto& player = Settings::values.players[player_idx];
109 std::array<u32, 4> colors{};
110 std::transform(controllers_colors[player_idx].begin(), controllers_colors[player_idx].end(),
111 colors.begin(), [](QColor color) { return color.rgb(); });
112
113 player.body_color_left = colors[0];
114 player.button_color_left = colors[1];
115 player.body_color_right = colors[2];
116 player.button_color_right = colors[3];
117 }
118
119 Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
120 Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
121 Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
122 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
123}
124
125void ConfigureInputAdvanced::LoadConfiguration() {
126 for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
127 auto& player = Settings::values.players[player_idx];
128 std::array<u32, 4> colors = {
129 player.body_color_left,
130 player.button_color_left,
131 player.body_color_right,
132 player.button_color_right,
133 };
134
135 std::transform(colors.begin(), colors.end(), controllers_colors[player_idx].begin(),
136 [](u32 rgb) { return QColor::fromRgb(rgb); });
137
138 for (std::size_t button_idx = 0; button_idx < colors.size(); ++button_idx) {
139 controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
140 QStringLiteral("background-color: %1; min-width: 55px;")
141 .arg(controllers_colors[player_idx][button_idx].name()));
142 }
143 }
144
145 ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
146 ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
147 ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
148 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
149
150 UpdateUIEnabled();
151}
152
153void ConfigureInputAdvanced::changeEvent(QEvent* event) {
154 if (event->type() == QEvent::LanguageChange) {
155 RetranslateUI();
156 }
157
158 QWidget::changeEvent(event);
159}
160
161void ConfigureInputAdvanced::RetranslateUI() {
162 ui->retranslateUi(this);
163}
164
165void ConfigureInputAdvanced::UpdateUIEnabled() {
166 ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
167 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
168 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
169}
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 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <memory>
9#include <QWidget>
10
11class QColor;
12class QPushButton;
13
14namespace Ui {
15class ConfigureInputAdvanced;
16}
17
18class ConfigureInputAdvanced : public QWidget {
19 Q_OBJECT
20
21public:
22 explicit ConfigureInputAdvanced(QWidget* parent = nullptr);
23 ~ConfigureInputAdvanced() override;
24
25 void ApplyConfiguration();
26
27signals:
28 void CallDebugControllerDialog();
29 void CallMouseConfigDialog();
30 void CallTouchscreenConfigDialog();
31
32private:
33 void changeEvent(QEvent* event) override;
34 void RetranslateUI();
35 void UpdateUIEnabled();
36
37 void OnControllerButtonClick(int player_idx, int button_idx);
38
39 void LoadConfiguration();
40
41 std::unique_ptr<Ui::ConfigureInputAdvanced> ui;
42
43 std::array<std::array<QColor, 4>, 8> controllers_colors;
44 std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons;
45};
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 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureInputAdvanced</class>
4 <widget class="QWidget" name="ConfigureInputAdvanced">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>710</width>
10 <height>580</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Input</string>
15 </property>
16 <property name="styleSheet">
17 <string notr="true"/>
18 </property>
19 <layout class="QHBoxLayout" name="horizontalLayout">
20 <property name="spacing">
21 <number>0</number>
22 </property>
23 <property name="leftMargin">
24 <number>0</number>
25 </property>
26 <property name="topMargin">
27 <number>0</number>
28 </property>
29 <property name="rightMargin">
30 <number>0</number>
31 </property>
32 <property name="bottomMargin">
33 <number>0</number>
34 </property>
35 <item>
36 <widget class="QWidget" name="mainInputAdvanced" native="true">
37 <layout class="QHBoxLayout" name="main" stretch="1,1">
38 <property name="spacing">
39 <number>9</number>
40 </property>
41 <property name="leftMargin">
42 <number>0</number>
43 </property>
44 <property name="topMargin">
45 <number>0</number>
46 </property>
47 <property name="rightMargin">
48 <number>0</number>
49 </property>
50 <property name="bottomMargin">
51 <number>0</number>
52 </property>
53 <item>
54 <widget class="QWidget" name="leftInputAdvanced" native="true">
55 <layout class="QVBoxLayout" name="leftLayout" stretch="0">
56 <property name="spacing">
57 <number>3</number>
58 </property>
59 <property name="leftMargin">
60 <number>0</number>
61 </property>
62 <property name="topMargin">
63 <number>0</number>
64 </property>
65 <property name="rightMargin">
66 <number>0</number>
67 </property>
68 <property name="bottomMargin">
69 <number>0</number>
70 </property>
71 <item>
72 <widget class="QGroupBox" name="joyconColorsGroup">
73 <property name="title">
74 <string>Joycon Colors</string>
75 </property>
76 <layout class="QVBoxLayout" name="verticalLayout_3" stretch="1,1">
77 <property name="leftMargin">
78 <number>9</number>
79 </property>
80 <property name="topMargin">
81 <number>9</number>
82 </property>
83 <property name="rightMargin">
84 <number>9</number>
85 </property>
86 <property name="bottomMargin">
87 <number>9</number>
88 </property>
89 <item>
90 <widget class="QWidget" name="topLeftInputAdvanced" native="true">
91 <layout class="QVBoxLayout" name="verticalLayout_4">
92 <property name="spacing">
93 <number>6</number>
94 </property>
95 <property name="leftMargin">
96 <number>0</number>
97 </property>
98 <property name="topMargin">
99 <number>0</number>
100 </property>
101 <property name="rightMargin">
102 <number>0</number>
103 </property>
104 <property name="bottomMargin">
105 <number>0</number>
106 </property>
107 <item>
108 <widget class="QWidget" name="player12Widget" native="true">
109 <layout class="QHBoxLayout" name="horizontalLayout_4">
110 <property name="leftMargin">
111 <number>0</number>
112 </property>
113 <property name="topMargin">
114 <number>0</number>
115 </property>
116 <property name="rightMargin">
117 <number>0</number>
118 </property>
119 <property name="bottomMargin">
120 <number>0</number>
121 </property>
122 <item>
123 <widget class="QGroupBox" name="player1Group">
124 <property name="title">
125 <string>Player 1</string>
126 </property>
127 <layout class="QHBoxLayout" name="horizontalLayout_2">
128 <property name="spacing">
129 <number>6</number>
130 </property>
131 <property name="leftMargin">
132 <number>6</number>
133 </property>
134 <property name="topMargin">
135 <number>0</number>
136 </property>
137 <property name="rightMargin">
138 <number>6</number>
139 </property>
140 <property name="bottomMargin">
141 <number>6</number>
142 </property>
143 <item>
144 <widget class="QWidget" name="player1LeftJoycon" native="true">
145 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_14">
146 <property name="spacing">
147 <number>0</number>
148 </property>
149 <property name="leftMargin">
150 <number>0</number>
151 </property>
152 <property name="topMargin">
153 <number>0</number>
154 </property>
155 <property name="rightMargin">
156 <number>0</number>
157 </property>
158 <property name="bottomMargin">
159 <number>0</number>
160 </property>
161 <item alignment="Qt::AlignHCenter">
162 <widget class="QGroupBox" name="player1LeftBodyGroup">
163 <property name="title">
164 <string>L Body</string>
165 </property>
166 <property name="alignment">
167 <set>Qt::AlignCenter</set>
168 </property>
169 <layout class="QVBoxLayout" name="verticalLayout_66">
170 <property name="spacing">
171 <number>3</number>
172 </property>
173 <property name="leftMargin">
174 <number>3</number>
175 </property>
176 <property name="topMargin">
177 <number>3</number>
178 </property>
179 <property name="rightMargin">
180 <number>3</number>
181 </property>
182 <property name="bottomMargin">
183 <number>3</number>
184 </property>
185 <item>
186 <widget class="QPushButton" name="player1_left_body_button">
187 <property name="sizePolicy">
188 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
189 <horstretch>0</horstretch>
190 <verstretch>0</verstretch>
191 </sizepolicy>
192 </property>
193 <property name="minimumSize">
194 <size>
195 <width>57</width>
196 <height>0</height>
197 </size>
198 </property>
199 <property name="maximumSize">
200 <size>
201 <width>55</width>
202 <height>16777215</height>
203 </size>
204 </property>
205 <property name="styleSheet">
206 <string notr="true">min-width: 55px;</string>
207 </property>
208 <property name="text">
209 <string/>
210 </property>
211 </widget>
212 </item>
213 </layout>
214 </widget>
215 </item>
216 <item alignment="Qt::AlignHCenter">
217 <widget class="QGroupBox" name="player1LeftButtonsGroup">
218 <property name="title">
219 <string>L Button</string>
220 </property>
221 <property name="alignment">
222 <set>Qt::AlignCenter</set>
223 </property>
224 <layout class="QVBoxLayout" name="verticalLayout_67">
225 <property name="spacing">
226 <number>3</number>
227 </property>
228 <property name="leftMargin">
229 <number>3</number>
230 </property>
231 <property name="topMargin">
232 <number>3</number>
233 </property>
234 <property name="rightMargin">
235 <number>3</number>
236 </property>
237 <property name="bottomMargin">
238 <number>3</number>
239 </property>
240 <item>
241 <widget class="QPushButton" name="player1_left_buttons_button">
242 <property name="sizePolicy">
243 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
244 <horstretch>0</horstretch>
245 <verstretch>0</verstretch>
246 </sizepolicy>
247 </property>
248 <property name="minimumSize">
249 <size>
250 <width>57</width>
251 <height>0</height>
252 </size>
253 </property>
254 <property name="maximumSize">
255 <size>
256 <width>55</width>
257 <height>16777215</height>
258 </size>
259 </property>
260 <property name="styleSheet">
261 <string notr="true">min-width: 55px;</string>
262 </property>
263 <property name="text">
264 <string/>
265 </property>
266 </widget>
267 </item>
268 </layout>
269 </widget>
270 </item>
271 </layout>
272 </widget>
273 </item>
274 <item>
275 <widget class="QWidget" name="player1RightJoycon" native="true">
276 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_14">
277 <property name="spacing">
278 <number>0</number>
279 </property>
280 <property name="leftMargin">
281 <number>0</number>
282 </property>
283 <property name="topMargin">
284 <number>0</number>
285 </property>
286 <property name="rightMargin">
287 <number>0</number>
288 </property>
289 <property name="bottomMargin">
290 <number>0</number>
291 </property>
292 <item alignment="Qt::AlignHCenter">
293 <widget class="QGroupBox" name="player1RightBodyGroup">
294 <property name="title">
295 <string>R Body</string>
296 </property>
297 <property name="alignment">
298 <set>Qt::AlignCenter</set>
299 </property>
300 <layout class="QVBoxLayout" name="verticalLayout_64">
301 <property name="spacing">
302 <number>3</number>
303 </property>
304 <property name="leftMargin">
305 <number>3</number>
306 </property>
307 <property name="topMargin">
308 <number>3</number>
309 </property>
310 <property name="rightMargin">
311 <number>3</number>
312 </property>
313 <property name="bottomMargin">
314 <number>3</number>
315 </property>
316 <item>
317 <widget class="QPushButton" name="player1_right_body_button">
318 <property name="sizePolicy">
319 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
320 <horstretch>0</horstretch>
321 <verstretch>0</verstretch>
322 </sizepolicy>
323 </property>
324 <property name="minimumSize">
325 <size>
326 <width>57</width>
327 <height>0</height>
328 </size>
329 </property>
330 <property name="maximumSize">
331 <size>
332 <width>55</width>
333 <height>16777215</height>
334 </size>
335 </property>
336 <property name="styleSheet">
337 <string notr="true">min-width: 55px;</string>
338 </property>
339 <property name="text">
340 <string/>
341 </property>
342 </widget>
343 </item>
344 </layout>
345 </widget>
346 </item>
347 <item alignment="Qt::AlignHCenter">
348 <widget class="QGroupBox" name="player1RightButtonsGroup">
349 <property name="title">
350 <string>R Button</string>
351 </property>
352 <property name="alignment">
353 <set>Qt::AlignCenter</set>
354 </property>
355 <layout class="QVBoxLayout" name="verticalLayout_65">
356 <property name="spacing">
357 <number>3</number>
358 </property>
359 <property name="leftMargin">
360 <number>3</number>
361 </property>
362 <property name="topMargin">
363 <number>3</number>
364 </property>
365 <property name="rightMargin">
366 <number>3</number>
367 </property>
368 <property name="bottomMargin">
369 <number>3</number>
370 </property>
371 <item>
372 <widget class="QPushButton" name="player1_right_buttons_button">
373 <property name="sizePolicy">
374 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
375 <horstretch>0</horstretch>
376 <verstretch>0</verstretch>
377 </sizepolicy>
378 </property>
379 <property name="minimumSize">
380 <size>
381 <width>57</width>
382 <height>0</height>
383 </size>
384 </property>
385 <property name="maximumSize">
386 <size>
387 <width>55</width>
388 <height>16777215</height>
389 </size>
390 </property>
391 <property name="styleSheet">
392 <string notr="true">min-width: 55px;</string>
393 </property>
394 <property name="text">
395 <string/>
396 </property>
397 </widget>
398 </item>
399 </layout>
400 </widget>
401 </item>
402 </layout>
403 </widget>
404 </item>
405 </layout>
406 </widget>
407 </item>
408 <item>
409 <widget class="QGroupBox" name="player2Group">
410 <property name="title">
411 <string>Player 2</string>
412 </property>
413 <layout class="QHBoxLayout" name="horizontalLayout_14">
414 <property name="spacing">
415 <number>6</number>
416 </property>
417 <property name="leftMargin">
418 <number>6</number>
419 </property>
420 <property name="topMargin">
421 <number>0</number>
422 </property>
423 <property name="rightMargin">
424 <number>6</number>
425 </property>
426 <property name="bottomMargin">
427 <number>6</number>
428 </property>
429 <item>
430 <widget class="QWidget" name="player2LeftJoycon" native="true">
431 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_15">
432 <property name="spacing">
433 <number>0</number>
434 </property>
435 <property name="leftMargin">
436 <number>0</number>
437 </property>
438 <property name="topMargin">
439 <number>0</number>
440 </property>
441 <property name="rightMargin">
442 <number>0</number>
443 </property>
444 <property name="bottomMargin">
445 <number>0</number>
446 </property>
447 <item alignment="Qt::AlignHCenter">
448 <widget class="QGroupBox" name="player2LeftBodyGroup">
449 <property name="title">
450 <string>L Body</string>
451 </property>
452 <property name="alignment">
453 <set>Qt::AlignCenter</set>
454 </property>
455 <layout class="QVBoxLayout" name="verticalLayout_70">
456 <property name="spacing">
457 <number>3</number>
458 </property>
459 <property name="leftMargin">
460 <number>3</number>
461 </property>
462 <property name="topMargin">
463 <number>3</number>
464 </property>
465 <property name="rightMargin">
466 <number>3</number>
467 </property>
468 <property name="bottomMargin">
469 <number>3</number>
470 </property>
471 <item>
472 <widget class="QPushButton" name="player2_left_body_button">
473 <property name="sizePolicy">
474 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
475 <horstretch>0</horstretch>
476 <verstretch>0</verstretch>
477 </sizepolicy>
478 </property>
479 <property name="minimumSize">
480 <size>
481 <width>57</width>
482 <height>0</height>
483 </size>
484 </property>
485 <property name="maximumSize">
486 <size>
487 <width>55</width>
488 <height>16777215</height>
489 </size>
490 </property>
491 <property name="styleSheet">
492 <string notr="true">min-width: 55px;</string>
493 </property>
494 <property name="text">
495 <string/>
496 </property>
497 </widget>
498 </item>
499 </layout>
500 </widget>
501 </item>
502 <item alignment="Qt::AlignHCenter">
503 <widget class="QGroupBox" name="player2LeftButtonsGroup">
504 <property name="title">
505 <string>L Button</string>
506 </property>
507 <property name="alignment">
508 <set>Qt::AlignCenter</set>
509 </property>
510 <layout class="QVBoxLayout" name="verticalLayout_71">
511 <property name="spacing">
512 <number>3</number>
513 </property>
514 <property name="leftMargin">
515 <number>3</number>
516 </property>
517 <property name="topMargin">
518 <number>3</number>
519 </property>
520 <property name="rightMargin">
521 <number>3</number>
522 </property>
523 <property name="bottomMargin">
524 <number>3</number>
525 </property>
526 <item>
527 <widget class="QPushButton" name="player2_left_buttons_button">
528 <property name="sizePolicy">
529 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
530 <horstretch>0</horstretch>
531 <verstretch>0</verstretch>
532 </sizepolicy>
533 </property>
534 <property name="minimumSize">
535 <size>
536 <width>57</width>
537 <height>0</height>
538 </size>
539 </property>
540 <property name="maximumSize">
541 <size>
542 <width>55</width>
543 <height>16777215</height>
544 </size>
545 </property>
546 <property name="styleSheet">
547 <string notr="true">min-width: 55px;</string>
548 </property>
549 <property name="text">
550 <string/>
551 </property>
552 </widget>
553 </item>
554 </layout>
555 </widget>
556 </item>
557 </layout>
558 </widget>
559 </item>
560 <item>
561 <widget class="QWidget" name="player2RightJoycon" native="true">
562 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_15">
563 <property name="spacing">
564 <number>0</number>
565 </property>
566 <property name="leftMargin">
567 <number>0</number>
568 </property>
569 <property name="topMargin">
570 <number>0</number>
571 </property>
572 <property name="rightMargin">
573 <number>0</number>
574 </property>
575 <property name="bottomMargin">
576 <number>0</number>
577 </property>
578 <item alignment="Qt::AlignHCenter">
579 <widget class="QGroupBox" name="player2RightBodyGroup">
580 <property name="title">
581 <string>R Body</string>
582 </property>
583 <property name="alignment">
584 <set>Qt::AlignCenter</set>
585 </property>
586 <layout class="QVBoxLayout" name="verticalLayout_68">
587 <property name="spacing">
588 <number>3</number>
589 </property>
590 <property name="leftMargin">
591 <number>3</number>
592 </property>
593 <property name="topMargin">
594 <number>3</number>
595 </property>
596 <property name="rightMargin">
597 <number>3</number>
598 </property>
599 <property name="bottomMargin">
600 <number>3</number>
601 </property>
602 <item>
603 <widget class="QPushButton" name="player2_right_body_button">
604 <property name="sizePolicy">
605 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
606 <horstretch>0</horstretch>
607 <verstretch>0</verstretch>
608 </sizepolicy>
609 </property>
610 <property name="minimumSize">
611 <size>
612 <width>57</width>
613 <height>0</height>
614 </size>
615 </property>
616 <property name="maximumSize">
617 <size>
618 <width>55</width>
619 <height>16777215</height>
620 </size>
621 </property>
622 <property name="styleSheet">
623 <string notr="true">min-width: 55px;</string>
624 </property>
625 <property name="text">
626 <string/>
627 </property>
628 </widget>
629 </item>
630 </layout>
631 </widget>
632 </item>
633 <item alignment="Qt::AlignHCenter">
634 <widget class="QGroupBox" name="player2RightButtonsGroup">
635 <property name="title">
636 <string>R Button</string>
637 </property>
638 <property name="alignment">
639 <set>Qt::AlignCenter</set>
640 </property>
641 <layout class="QVBoxLayout" name="verticalLayout_69">
642 <property name="spacing">
643 <number>3</number>
644 </property>
645 <property name="leftMargin">
646 <number>3</number>
647 </property>
648 <property name="topMargin">
649 <number>3</number>
650 </property>
651 <property name="rightMargin">
652 <number>3</number>
653 </property>
654 <property name="bottomMargin">
655 <number>3</number>
656 </property>
657 <item>
658 <widget class="QPushButton" name="player2_right_buttons_button">
659 <property name="sizePolicy">
660 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
661 <horstretch>0</horstretch>
662 <verstretch>0</verstretch>
663 </sizepolicy>
664 </property>
665 <property name="minimumSize">
666 <size>
667 <width>57</width>
668 <height>0</height>
669 </size>
670 </property>
671 <property name="maximumSize">
672 <size>
673 <width>55</width>
674 <height>16777215</height>
675 </size>
676 </property>
677 <property name="styleSheet">
678 <string notr="true">min-width: 55px;</string>
679 </property>
680 <property name="text">
681 <string/>
682 </property>
683 </widget>
684 </item>
685 </layout>
686 </widget>
687 </item>
688 </layout>
689 </widget>
690 </item>
691 </layout>
692 </widget>
693 </item>
694 </layout>
695 </widget>
696 </item>
697 <item>
698 <widget class="QWidget" name="player34Widget" native="true">
699 <layout class="QHBoxLayout" name="horizontalLayout_5">
700 <property name="leftMargin">
701 <number>0</number>
702 </property>
703 <property name="topMargin">
704 <number>0</number>
705 </property>
706 <property name="rightMargin">
707 <number>0</number>
708 </property>
709 <property name="bottomMargin">
710 <number>0</number>
711 </property>
712 <item>
713 <widget class="QGroupBox" name="player3Group">
714 <property name="title">
715 <string>Player 3</string>
716 </property>
717 <layout class="QHBoxLayout" name="horizontalLayout_15">
718 <property name="spacing">
719 <number>6</number>
720 </property>
721 <property name="leftMargin">
722 <number>6</number>
723 </property>
724 <property name="topMargin">
725 <number>0</number>
726 </property>
727 <property name="rightMargin">
728 <number>6</number>
729 </property>
730 <property name="bottomMargin">
731 <number>6</number>
732 </property>
733 <item>
734 <widget class="QWidget" name="player3LeftJoycon" native="true">
735 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_16">
736 <property name="spacing">
737 <number>0</number>
738 </property>
739 <property name="leftMargin">
740 <number>0</number>
741 </property>
742 <property name="topMargin">
743 <number>0</number>
744 </property>
745 <property name="rightMargin">
746 <number>0</number>
747 </property>
748 <property name="bottomMargin">
749 <number>0</number>
750 </property>
751 <item alignment="Qt::AlignHCenter">
752 <widget class="QGroupBox" name="player3LeftBodyGroup">
753 <property name="title">
754 <string>L Body</string>
755 </property>
756 <property name="alignment">
757 <set>Qt::AlignCenter</set>
758 </property>
759 <layout class="QVBoxLayout" name="verticalLayout_74">
760 <property name="spacing">
761 <number>3</number>
762 </property>
763 <property name="leftMargin">
764 <number>3</number>
765 </property>
766 <property name="topMargin">
767 <number>3</number>
768 </property>
769 <property name="rightMargin">
770 <number>3</number>
771 </property>
772 <property name="bottomMargin">
773 <number>3</number>
774 </property>
775 <item>
776 <widget class="QPushButton" name="player3_left_body_button">
777 <property name="sizePolicy">
778 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
779 <horstretch>0</horstretch>
780 <verstretch>0</verstretch>
781 </sizepolicy>
782 </property>
783 <property name="minimumSize">
784 <size>
785 <width>57</width>
786 <height>0</height>
787 </size>
788 </property>
789 <property name="maximumSize">
790 <size>
791 <width>55</width>
792 <height>16777215</height>
793 </size>
794 </property>
795 <property name="styleSheet">
796 <string notr="true">min-width: 55px;</string>
797 </property>
798 <property name="text">
799 <string/>
800 </property>
801 </widget>
802 </item>
803 </layout>
804 </widget>
805 </item>
806 <item alignment="Qt::AlignHCenter">
807 <widget class="QGroupBox" name="player3LeftButtonsGroup">
808 <property name="title">
809 <string>L Button</string>
810 </property>
811 <property name="alignment">
812 <set>Qt::AlignCenter</set>
813 </property>
814 <layout class="QVBoxLayout" name="verticalLayout_75">
815 <property name="spacing">
816 <number>3</number>
817 </property>
818 <property name="leftMargin">
819 <number>3</number>
820 </property>
821 <property name="topMargin">
822 <number>3</number>
823 </property>
824 <property name="rightMargin">
825 <number>3</number>
826 </property>
827 <property name="bottomMargin">
828 <number>3</number>
829 </property>
830 <item>
831 <widget class="QPushButton" name="player3_left_buttons_button">
832 <property name="sizePolicy">
833 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
834 <horstretch>0</horstretch>
835 <verstretch>0</verstretch>
836 </sizepolicy>
837 </property>
838 <property name="minimumSize">
839 <size>
840 <width>57</width>
841 <height>0</height>
842 </size>
843 </property>
844 <property name="maximumSize">
845 <size>
846 <width>55</width>
847 <height>16777215</height>
848 </size>
849 </property>
850 <property name="styleSheet">
851 <string notr="true">min-width: 55px;</string>
852 </property>
853 <property name="text">
854 <string/>
855 </property>
856 </widget>
857 </item>
858 </layout>
859 </widget>
860 </item>
861 </layout>
862 </widget>
863 </item>
864 <item>
865 <widget class="QWidget" name="player3RightJoycon" native="true">
866 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_16">
867 <property name="spacing">
868 <number>0</number>
869 </property>
870 <property name="leftMargin">
871 <number>0</number>
872 </property>
873 <property name="topMargin">
874 <number>0</number>
875 </property>
876 <property name="rightMargin">
877 <number>0</number>
878 </property>
879 <property name="bottomMargin">
880 <number>0</number>
881 </property>
882 <item alignment="Qt::AlignHCenter">
883 <widget class="QGroupBox" name="player3RightBodyGroup">
884 <property name="title">
885 <string>R Body</string>
886 </property>
887 <property name="alignment">
888 <set>Qt::AlignCenter</set>
889 </property>
890 <layout class="QVBoxLayout" name="verticalLayout_72">
891 <property name="spacing">
892 <number>3</number>
893 </property>
894 <property name="leftMargin">
895 <number>3</number>
896 </property>
897 <property name="topMargin">
898 <number>3</number>
899 </property>
900 <property name="rightMargin">
901 <number>3</number>
902 </property>
903 <property name="bottomMargin">
904 <number>3</number>
905 </property>
906 <item>
907 <widget class="QPushButton" name="player3_right_body_button">
908 <property name="sizePolicy">
909 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
910 <horstretch>0</horstretch>
911 <verstretch>0</verstretch>
912 </sizepolicy>
913 </property>
914 <property name="minimumSize">
915 <size>
916 <width>57</width>
917 <height>0</height>
918 </size>
919 </property>
920 <property name="maximumSize">
921 <size>
922 <width>55</width>
923 <height>16777215</height>
924 </size>
925 </property>
926 <property name="styleSheet">
927 <string notr="true">min-width: 55px;</string>
928 </property>
929 <property name="text">
930 <string/>
931 </property>
932 </widget>
933 </item>
934 </layout>
935 </widget>
936 </item>
937 <item alignment="Qt::AlignHCenter">
938 <widget class="QGroupBox" name="player3RightButtonsGroup">
939 <property name="title">
940 <string>R Button</string>
941 </property>
942 <property name="alignment">
943 <set>Qt::AlignCenter</set>
944 </property>
945 <layout class="QVBoxLayout" name="verticalLayout_73">
946 <property name="spacing">
947 <number>3</number>
948 </property>
949 <property name="leftMargin">
950 <number>3</number>
951 </property>
952 <property name="topMargin">
953 <number>3</number>
954 </property>
955 <property name="rightMargin">
956 <number>3</number>
957 </property>
958 <property name="bottomMargin">
959 <number>3</number>
960 </property>
961 <item>
962 <widget class="QPushButton" name="player3_right_buttons_button">
963 <property name="sizePolicy">
964 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
965 <horstretch>0</horstretch>
966 <verstretch>0</verstretch>
967 </sizepolicy>
968 </property>
969 <property name="minimumSize">
970 <size>
971 <width>57</width>
972 <height>0</height>
973 </size>
974 </property>
975 <property name="maximumSize">
976 <size>
977 <width>55</width>
978 <height>16777215</height>
979 </size>
980 </property>
981 <property name="styleSheet">
982 <string notr="true">min-width: 55px;</string>
983 </property>
984 <property name="text">
985 <string/>
986 </property>
987 </widget>
988 </item>
989 </layout>
990 </widget>
991 </item>
992 </layout>
993 </widget>
994 </item>
995 </layout>
996 </widget>
997 </item>
998 <item>
999 <widget class="QGroupBox" name="player4Group">
1000 <property name="title">
1001 <string>Player 4</string>
1002 </property>
1003 <layout class="QHBoxLayout" name="horizontalLayout_16">
1004 <property name="spacing">
1005 <number>6</number>
1006 </property>
1007 <property name="leftMargin">
1008 <number>6</number>
1009 </property>
1010 <property name="topMargin">
1011 <number>0</number>
1012 </property>
1013 <property name="rightMargin">
1014 <number>6</number>
1015 </property>
1016 <property name="bottomMargin">
1017 <number>6</number>
1018 </property>
1019 <item>
1020 <widget class="QWidget" name="player4LeftJoycon" native="true">
1021 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_17">
1022 <property name="spacing">
1023 <number>0</number>
1024 </property>
1025 <property name="leftMargin">
1026 <number>0</number>
1027 </property>
1028 <property name="topMargin">
1029 <number>0</number>
1030 </property>
1031 <property name="rightMargin">
1032 <number>0</number>
1033 </property>
1034 <property name="bottomMargin">
1035 <number>0</number>
1036 </property>
1037 <item alignment="Qt::AlignHCenter">
1038 <widget class="QGroupBox" name="player4LeftBodyGroup">
1039 <property name="title">
1040 <string>L Body</string>
1041 </property>
1042 <property name="alignment">
1043 <set>Qt::AlignCenter</set>
1044 </property>
1045 <layout class="QVBoxLayout" name="verticalLayout_78">
1046 <property name="spacing">
1047 <number>3</number>
1048 </property>
1049 <property name="leftMargin">
1050 <number>3</number>
1051 </property>
1052 <property name="topMargin">
1053 <number>3</number>
1054 </property>
1055 <property name="rightMargin">
1056 <number>3</number>
1057 </property>
1058 <property name="bottomMargin">
1059 <number>3</number>
1060 </property>
1061 <item>
1062 <widget class="QPushButton" name="player4_left_body_button">
1063 <property name="sizePolicy">
1064 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1065 <horstretch>0</horstretch>
1066 <verstretch>0</verstretch>
1067 </sizepolicy>
1068 </property>
1069 <property name="minimumSize">
1070 <size>
1071 <width>57</width>
1072 <height>0</height>
1073 </size>
1074 </property>
1075 <property name="maximumSize">
1076 <size>
1077 <width>55</width>
1078 <height>16777215</height>
1079 </size>
1080 </property>
1081 <property name="styleSheet">
1082 <string notr="true">min-width: 55px;</string>
1083 </property>
1084 <property name="text">
1085 <string/>
1086 </property>
1087 </widget>
1088 </item>
1089 </layout>
1090 </widget>
1091 </item>
1092 <item alignment="Qt::AlignHCenter">
1093 <widget class="QGroupBox" name="player4LeftButtonsGroup">
1094 <property name="title">
1095 <string>L Button</string>
1096 </property>
1097 <property name="alignment">
1098 <set>Qt::AlignCenter</set>
1099 </property>
1100 <layout class="QVBoxLayout" name="verticalLayout_79">
1101 <property name="spacing">
1102 <number>3</number>
1103 </property>
1104 <property name="leftMargin">
1105 <number>3</number>
1106 </property>
1107 <property name="topMargin">
1108 <number>3</number>
1109 </property>
1110 <property name="rightMargin">
1111 <number>3</number>
1112 </property>
1113 <property name="bottomMargin">
1114 <number>3</number>
1115 </property>
1116 <item>
1117 <widget class="QPushButton" name="player4_left_buttons_button">
1118 <property name="sizePolicy">
1119 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1120 <horstretch>0</horstretch>
1121 <verstretch>0</verstretch>
1122 </sizepolicy>
1123 </property>
1124 <property name="minimumSize">
1125 <size>
1126 <width>57</width>
1127 <height>0</height>
1128 </size>
1129 </property>
1130 <property name="maximumSize">
1131 <size>
1132 <width>55</width>
1133 <height>16777215</height>
1134 </size>
1135 </property>
1136 <property name="styleSheet">
1137 <string notr="true">min-width: 55px;</string>
1138 </property>
1139 <property name="text">
1140 <string/>
1141 </property>
1142 </widget>
1143 </item>
1144 </layout>
1145 </widget>
1146 </item>
1147 </layout>
1148 </widget>
1149 </item>
1150 <item>
1151 <widget class="QWidget" name="player4RightJoycon" native="true">
1152 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_17">
1153 <property name="spacing">
1154 <number>0</number>
1155 </property>
1156 <property name="leftMargin">
1157 <number>0</number>
1158 </property>
1159 <property name="topMargin">
1160 <number>0</number>
1161 </property>
1162 <property name="rightMargin">
1163 <number>0</number>
1164 </property>
1165 <property name="bottomMargin">
1166 <number>0</number>
1167 </property>
1168 <item alignment="Qt::AlignHCenter">
1169 <widget class="QGroupBox" name="player4RightBodyGroup">
1170 <property name="title">
1171 <string>R Body</string>
1172 </property>
1173 <property name="alignment">
1174 <set>Qt::AlignCenter</set>
1175 </property>
1176 <layout class="QVBoxLayout" name="verticalLayout_76">
1177 <property name="spacing">
1178 <number>3</number>
1179 </property>
1180 <property name="leftMargin">
1181 <number>3</number>
1182 </property>
1183 <property name="topMargin">
1184 <number>3</number>
1185 </property>
1186 <property name="rightMargin">
1187 <number>3</number>
1188 </property>
1189 <property name="bottomMargin">
1190 <number>3</number>
1191 </property>
1192 <item>
1193 <widget class="QPushButton" name="player4_right_body_button">
1194 <property name="sizePolicy">
1195 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1196 <horstretch>0</horstretch>
1197 <verstretch>0</verstretch>
1198 </sizepolicy>
1199 </property>
1200 <property name="minimumSize">
1201 <size>
1202 <width>57</width>
1203 <height>0</height>
1204 </size>
1205 </property>
1206 <property name="maximumSize">
1207 <size>
1208 <width>55</width>
1209 <height>16777215</height>
1210 </size>
1211 </property>
1212 <property name="styleSheet">
1213 <string notr="true">min-width: 55px;</string>
1214 </property>
1215 <property name="text">
1216 <string/>
1217 </property>
1218 </widget>
1219 </item>
1220 </layout>
1221 </widget>
1222 </item>
1223 <item alignment="Qt::AlignHCenter">
1224 <widget class="QGroupBox" name="player4RightButtonsGroup">
1225 <property name="title">
1226 <string>R Button</string>
1227 </property>
1228 <property name="alignment">
1229 <set>Qt::AlignCenter</set>
1230 </property>
1231 <layout class="QVBoxLayout" name="verticalLayout_77">
1232 <property name="spacing">
1233 <number>3</number>
1234 </property>
1235 <property name="leftMargin">
1236 <number>3</number>
1237 </property>
1238 <property name="topMargin">
1239 <number>3</number>
1240 </property>
1241 <property name="rightMargin">
1242 <number>3</number>
1243 </property>
1244 <property name="bottomMargin">
1245 <number>3</number>
1246 </property>
1247 <item>
1248 <widget class="QPushButton" name="player4_right_buttons_button">
1249 <property name="sizePolicy">
1250 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1251 <horstretch>0</horstretch>
1252 <verstretch>0</verstretch>
1253 </sizepolicy>
1254 </property>
1255 <property name="minimumSize">
1256 <size>
1257 <width>57</width>
1258 <height>0</height>
1259 </size>
1260 </property>
1261 <property name="maximumSize">
1262 <size>
1263 <width>55</width>
1264 <height>16777215</height>
1265 </size>
1266 </property>
1267 <property name="styleSheet">
1268 <string notr="true">min-width: 55px;</string>
1269 </property>
1270 <property name="text">
1271 <string/>
1272 </property>
1273 </widget>
1274 </item>
1275 </layout>
1276 </widget>
1277 </item>
1278 </layout>
1279 </widget>
1280 </item>
1281 </layout>
1282 </widget>
1283 </item>
1284 </layout>
1285 </widget>
1286 </item>
1287 </layout>
1288 </widget>
1289 </item>
1290 <item>
1291 <widget class="QWidget" name="bottomLeftInputAdvanced" native="true">
1292 <layout class="QVBoxLayout" name="verticalLayout_5">
1293 <property name="spacing">
1294 <number>6</number>
1295 </property>
1296 <property name="leftMargin">
1297 <number>0</number>
1298 </property>
1299 <property name="topMargin">
1300 <number>0</number>
1301 </property>
1302 <property name="rightMargin">
1303 <number>0</number>
1304 </property>
1305 <property name="bottomMargin">
1306 <number>0</number>
1307 </property>
1308 <item>
1309 <widget class="QWidget" name="player56Widget" native="true">
1310 <layout class="QHBoxLayout" name="horizontalLayout_6">
1311 <property name="leftMargin">
1312 <number>0</number>
1313 </property>
1314 <property name="topMargin">
1315 <number>0</number>
1316 </property>
1317 <property name="rightMargin">
1318 <number>0</number>
1319 </property>
1320 <property name="bottomMargin">
1321 <number>0</number>
1322 </property>
1323 <item>
1324 <widget class="QGroupBox" name="player5Group">
1325 <property name="title">
1326 <string>Player 5</string>
1327 </property>
1328 <layout class="QHBoxLayout" name="horizontalLayout_10">
1329 <property name="spacing">
1330 <number>6</number>
1331 </property>
1332 <property name="leftMargin">
1333 <number>6</number>
1334 </property>
1335 <property name="topMargin">
1336 <number>0</number>
1337 </property>
1338 <property name="rightMargin">
1339 <number>6</number>
1340 </property>
1341 <property name="bottomMargin">
1342 <number>6</number>
1343 </property>
1344 <item>
1345 <widget class="QWidget" name="player5LeftJoycon" native="true">
1346 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_10">
1347 <property name="spacing">
1348 <number>0</number>
1349 </property>
1350 <property name="leftMargin">
1351 <number>0</number>
1352 </property>
1353 <property name="topMargin">
1354 <number>0</number>
1355 </property>
1356 <property name="rightMargin">
1357 <number>0</number>
1358 </property>
1359 <property name="bottomMargin">
1360 <number>0</number>
1361 </property>
1362 <item alignment="Qt::AlignHCenter">
1363 <widget class="QGroupBox" name="player5LeftBodyGroup">
1364 <property name="title">
1365 <string>L Body</string>
1366 </property>
1367 <property name="alignment">
1368 <set>Qt::AlignCenter</set>
1369 </property>
1370 <layout class="QVBoxLayout" name="verticalLayout_50">
1371 <property name="spacing">
1372 <number>3</number>
1373 </property>
1374 <property name="leftMargin">
1375 <number>3</number>
1376 </property>
1377 <property name="topMargin">
1378 <number>3</number>
1379 </property>
1380 <property name="rightMargin">
1381 <number>3</number>
1382 </property>
1383 <property name="bottomMargin">
1384 <number>3</number>
1385 </property>
1386 <item>
1387 <widget class="QPushButton" name="player5_left_body_button">
1388 <property name="sizePolicy">
1389 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1390 <horstretch>0</horstretch>
1391 <verstretch>0</verstretch>
1392 </sizepolicy>
1393 </property>
1394 <property name="minimumSize">
1395 <size>
1396 <width>57</width>
1397 <height>0</height>
1398 </size>
1399 </property>
1400 <property name="maximumSize">
1401 <size>
1402 <width>55</width>
1403 <height>16777215</height>
1404 </size>
1405 </property>
1406 <property name="styleSheet">
1407 <string notr="true">min-width: 55px;</string>
1408 </property>
1409 <property name="text">
1410 <string/>
1411 </property>
1412 </widget>
1413 </item>
1414 </layout>
1415 </widget>
1416 </item>
1417 <item alignment="Qt::AlignHCenter">
1418 <widget class="QGroupBox" name="player5LeftButtonsGroup">
1419 <property name="title">
1420 <string>L Button</string>
1421 </property>
1422 <property name="alignment">
1423 <set>Qt::AlignCenter</set>
1424 </property>
1425 <layout class="QVBoxLayout" name="verticalLayout_51">
1426 <property name="spacing">
1427 <number>3</number>
1428 </property>
1429 <property name="leftMargin">
1430 <number>3</number>
1431 </property>
1432 <property name="topMargin">
1433 <number>3</number>
1434 </property>
1435 <property name="rightMargin">
1436 <number>3</number>
1437 </property>
1438 <property name="bottomMargin">
1439 <number>3</number>
1440 </property>
1441 <item>
1442 <widget class="QPushButton" name="player5_left_buttons_button">
1443 <property name="sizePolicy">
1444 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1445 <horstretch>0</horstretch>
1446 <verstretch>0</verstretch>
1447 </sizepolicy>
1448 </property>
1449 <property name="minimumSize">
1450 <size>
1451 <width>57</width>
1452 <height>0</height>
1453 </size>
1454 </property>
1455 <property name="maximumSize">
1456 <size>
1457 <width>55</width>
1458 <height>16777215</height>
1459 </size>
1460 </property>
1461 <property name="styleSheet">
1462 <string notr="true">min-width: 55px;</string>
1463 </property>
1464 <property name="text">
1465 <string/>
1466 </property>
1467 </widget>
1468 </item>
1469 </layout>
1470 </widget>
1471 </item>
1472 </layout>
1473 </widget>
1474 </item>
1475 <item>
1476 <widget class="QWidget" name="player5RightJoycon" native="true">
1477 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_10">
1478 <property name="spacing">
1479 <number>0</number>
1480 </property>
1481 <property name="leftMargin">
1482 <number>0</number>
1483 </property>
1484 <property name="topMargin">
1485 <number>0</number>
1486 </property>
1487 <property name="rightMargin">
1488 <number>0</number>
1489 </property>
1490 <property name="bottomMargin">
1491 <number>0</number>
1492 </property>
1493 <item alignment="Qt::AlignHCenter">
1494 <widget class="QGroupBox" name="player5RightBodyGroup">
1495 <property name="title">
1496 <string>R Body</string>
1497 </property>
1498 <property name="alignment">
1499 <set>Qt::AlignCenter</set>
1500 </property>
1501 <layout class="QVBoxLayout" name="verticalLayout_48">
1502 <property name="spacing">
1503 <number>3</number>
1504 </property>
1505 <property name="leftMargin">
1506 <number>3</number>
1507 </property>
1508 <property name="topMargin">
1509 <number>3</number>
1510 </property>
1511 <property name="rightMargin">
1512 <number>3</number>
1513 </property>
1514 <property name="bottomMargin">
1515 <number>3</number>
1516 </property>
1517 <item>
1518 <widget class="QPushButton" name="player5_right_body_button">
1519 <property name="sizePolicy">
1520 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1521 <horstretch>0</horstretch>
1522 <verstretch>0</verstretch>
1523 </sizepolicy>
1524 </property>
1525 <property name="minimumSize">
1526 <size>
1527 <width>57</width>
1528 <height>0</height>
1529 </size>
1530 </property>
1531 <property name="maximumSize">
1532 <size>
1533 <width>55</width>
1534 <height>16777215</height>
1535 </size>
1536 </property>
1537 <property name="styleSheet">
1538 <string notr="true">min-width: 55px;</string>
1539 </property>
1540 <property name="text">
1541 <string/>
1542 </property>
1543 </widget>
1544 </item>
1545 </layout>
1546 </widget>
1547 </item>
1548 <item alignment="Qt::AlignHCenter">
1549 <widget class="QGroupBox" name="player5RightButtonsGroup">
1550 <property name="title">
1551 <string>R Button</string>
1552 </property>
1553 <property name="alignment">
1554 <set>Qt::AlignCenter</set>
1555 </property>
1556 <layout class="QVBoxLayout" name="verticalLayout_49">
1557 <property name="spacing">
1558 <number>3</number>
1559 </property>
1560 <property name="leftMargin">
1561 <number>3</number>
1562 </property>
1563 <property name="topMargin">
1564 <number>3</number>
1565 </property>
1566 <property name="rightMargin">
1567 <number>3</number>
1568 </property>
1569 <property name="bottomMargin">
1570 <number>3</number>
1571 </property>
1572 <item>
1573 <widget class="QPushButton" name="player5_right_buttons_button">
1574 <property name="sizePolicy">
1575 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1576 <horstretch>0</horstretch>
1577 <verstretch>0</verstretch>
1578 </sizepolicy>
1579 </property>
1580 <property name="minimumSize">
1581 <size>
1582 <width>57</width>
1583 <height>0</height>
1584 </size>
1585 </property>
1586 <property name="maximumSize">
1587 <size>
1588 <width>55</width>
1589 <height>16777215</height>
1590 </size>
1591 </property>
1592 <property name="styleSheet">
1593 <string notr="true">min-width: 55px;</string>
1594 </property>
1595 <property name="text">
1596 <string/>
1597 </property>
1598 </widget>
1599 </item>
1600 </layout>
1601 </widget>
1602 </item>
1603 </layout>
1604 </widget>
1605 </item>
1606 </layout>
1607 </widget>
1608 </item>
1609 <item>
1610 <widget class="QGroupBox" name="player6Group">
1611 <property name="title">
1612 <string>Player 6</string>
1613 </property>
1614 <layout class="QHBoxLayout" name="horizontalLayout_11">
1615 <property name="spacing">
1616 <number>6</number>
1617 </property>
1618 <property name="leftMargin">
1619 <number>6</number>
1620 </property>
1621 <property name="topMargin">
1622 <number>0</number>
1623 </property>
1624 <property name="rightMargin">
1625 <number>6</number>
1626 </property>
1627 <property name="bottomMargin">
1628 <number>6</number>
1629 </property>
1630 <item>
1631 <widget class="QWidget" name="player6LeftJoycon" native="true">
1632 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_11">
1633 <property name="spacing">
1634 <number>0</number>
1635 </property>
1636 <property name="leftMargin">
1637 <number>0</number>
1638 </property>
1639 <property name="topMargin">
1640 <number>0</number>
1641 </property>
1642 <property name="rightMargin">
1643 <number>0</number>
1644 </property>
1645 <property name="bottomMargin">
1646 <number>0</number>
1647 </property>
1648 <item alignment="Qt::AlignHCenter">
1649 <widget class="QGroupBox" name="player6LeftBodyGroup">
1650 <property name="title">
1651 <string>L Body</string>
1652 </property>
1653 <property name="alignment">
1654 <set>Qt::AlignCenter</set>
1655 </property>
1656 <layout class="QVBoxLayout" name="verticalLayout_54">
1657 <property name="spacing">
1658 <number>3</number>
1659 </property>
1660 <property name="leftMargin">
1661 <number>3</number>
1662 </property>
1663 <property name="topMargin">
1664 <number>3</number>
1665 </property>
1666 <property name="rightMargin">
1667 <number>3</number>
1668 </property>
1669 <property name="bottomMargin">
1670 <number>3</number>
1671 </property>
1672 <item>
1673 <widget class="QPushButton" name="player6_left_body_button">
1674 <property name="sizePolicy">
1675 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1676 <horstretch>0</horstretch>
1677 <verstretch>0</verstretch>
1678 </sizepolicy>
1679 </property>
1680 <property name="minimumSize">
1681 <size>
1682 <width>57</width>
1683 <height>0</height>
1684 </size>
1685 </property>
1686 <property name="maximumSize">
1687 <size>
1688 <width>55</width>
1689 <height>16777215</height>
1690 </size>
1691 </property>
1692 <property name="styleSheet">
1693 <string notr="true">min-width: 55px;</string>
1694 </property>
1695 <property name="text">
1696 <string/>
1697 </property>
1698 </widget>
1699 </item>
1700 </layout>
1701 </widget>
1702 </item>
1703 <item alignment="Qt::AlignHCenter">
1704 <widget class="QGroupBox" name="player6LeftButtonsGroup">
1705 <property name="title">
1706 <string>L Button</string>
1707 </property>
1708 <property name="alignment">
1709 <set>Qt::AlignCenter</set>
1710 </property>
1711 <layout class="QVBoxLayout" name="verticalLayout_55">
1712 <property name="spacing">
1713 <number>3</number>
1714 </property>
1715 <property name="leftMargin">
1716 <number>3</number>
1717 </property>
1718 <property name="topMargin">
1719 <number>3</number>
1720 </property>
1721 <property name="rightMargin">
1722 <number>3</number>
1723 </property>
1724 <property name="bottomMargin">
1725 <number>3</number>
1726 </property>
1727 <item>
1728 <widget class="QPushButton" name="player6_left_buttons_button">
1729 <property name="sizePolicy">
1730 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1731 <horstretch>0</horstretch>
1732 <verstretch>0</verstretch>
1733 </sizepolicy>
1734 </property>
1735 <property name="minimumSize">
1736 <size>
1737 <width>57</width>
1738 <height>0</height>
1739 </size>
1740 </property>
1741 <property name="maximumSize">
1742 <size>
1743 <width>55</width>
1744 <height>16777215</height>
1745 </size>
1746 </property>
1747 <property name="styleSheet">
1748 <string notr="true">min-width: 55px;</string>
1749 </property>
1750 <property name="text">
1751 <string/>
1752 </property>
1753 </widget>
1754 </item>
1755 </layout>
1756 </widget>
1757 </item>
1758 </layout>
1759 </widget>
1760 </item>
1761 <item>
1762 <widget class="QWidget" name="player6RightJoycon" native="true">
1763 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_11">
1764 <property name="spacing">
1765 <number>0</number>
1766 </property>
1767 <property name="leftMargin">
1768 <number>0</number>
1769 </property>
1770 <property name="topMargin">
1771 <number>0</number>
1772 </property>
1773 <property name="rightMargin">
1774 <number>0</number>
1775 </property>
1776 <property name="bottomMargin">
1777 <number>0</number>
1778 </property>
1779 <item alignment="Qt::AlignHCenter">
1780 <widget class="QGroupBox" name="player6RightBodyGroup">
1781 <property name="title">
1782 <string>R Body</string>
1783 </property>
1784 <property name="alignment">
1785 <set>Qt::AlignCenter</set>
1786 </property>
1787 <layout class="QVBoxLayout" name="verticalLayout_52">
1788 <property name="spacing">
1789 <number>3</number>
1790 </property>
1791 <property name="leftMargin">
1792 <number>3</number>
1793 </property>
1794 <property name="topMargin">
1795 <number>3</number>
1796 </property>
1797 <property name="rightMargin">
1798 <number>3</number>
1799 </property>
1800 <property name="bottomMargin">
1801 <number>3</number>
1802 </property>
1803 <item>
1804 <widget class="QPushButton" name="player6_right_body_button">
1805 <property name="sizePolicy">
1806 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1807 <horstretch>0</horstretch>
1808 <verstretch>0</verstretch>
1809 </sizepolicy>
1810 </property>
1811 <property name="minimumSize">
1812 <size>
1813 <width>57</width>
1814 <height>0</height>
1815 </size>
1816 </property>
1817 <property name="maximumSize">
1818 <size>
1819 <width>55</width>
1820 <height>16777215</height>
1821 </size>
1822 </property>
1823 <property name="styleSheet">
1824 <string notr="true">min-width: 55px;</string>
1825 </property>
1826 <property name="text">
1827 <string/>
1828 </property>
1829 </widget>
1830 </item>
1831 </layout>
1832 </widget>
1833 </item>
1834 <item alignment="Qt::AlignHCenter">
1835 <widget class="QGroupBox" name="player6RightButtonsGroup">
1836 <property name="title">
1837 <string>R Button</string>
1838 </property>
1839 <property name="alignment">
1840 <set>Qt::AlignCenter</set>
1841 </property>
1842 <layout class="QVBoxLayout" name="verticalLayout_53">
1843 <property name="spacing">
1844 <number>3</number>
1845 </property>
1846 <property name="leftMargin">
1847 <number>3</number>
1848 </property>
1849 <property name="topMargin">
1850 <number>3</number>
1851 </property>
1852 <property name="rightMargin">
1853 <number>3</number>
1854 </property>
1855 <property name="bottomMargin">
1856 <number>3</number>
1857 </property>
1858 <item>
1859 <widget class="QPushButton" name="player6_right_buttons_button">
1860 <property name="sizePolicy">
1861 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1862 <horstretch>0</horstretch>
1863 <verstretch>0</verstretch>
1864 </sizepolicy>
1865 </property>
1866 <property name="minimumSize">
1867 <size>
1868 <width>57</width>
1869 <height>0</height>
1870 </size>
1871 </property>
1872 <property name="maximumSize">
1873 <size>
1874 <width>55</width>
1875 <height>16777215</height>
1876 </size>
1877 </property>
1878 <property name="styleSheet">
1879 <string notr="true">min-width: 55px;</string>
1880 </property>
1881 <property name="text">
1882 <string/>
1883 </property>
1884 </widget>
1885 </item>
1886 </layout>
1887 </widget>
1888 </item>
1889 </layout>
1890 </widget>
1891 </item>
1892 </layout>
1893 </widget>
1894 </item>
1895 </layout>
1896 </widget>
1897 </item>
1898 <item>
1899 <widget class="QWidget" name="player78Widget" native="true">
1900 <layout class="QHBoxLayout" name="horizontalLayout_7">
1901 <property name="leftMargin">
1902 <number>0</number>
1903 </property>
1904 <property name="topMargin">
1905 <number>0</number>
1906 </property>
1907 <property name="rightMargin">
1908 <number>0</number>
1909 </property>
1910 <property name="bottomMargin">
1911 <number>0</number>
1912 </property>
1913 <item>
1914 <widget class="QGroupBox" name="player7Group">
1915 <property name="title">
1916 <string>Player 7</string>
1917 </property>
1918 <layout class="QHBoxLayout" name="horizontalLayout_12">
1919 <property name="spacing">
1920 <number>6</number>
1921 </property>
1922 <property name="leftMargin">
1923 <number>6</number>
1924 </property>
1925 <property name="topMargin">
1926 <number>0</number>
1927 </property>
1928 <property name="rightMargin">
1929 <number>6</number>
1930 </property>
1931 <property name="bottomMargin">
1932 <number>6</number>
1933 </property>
1934 <item>
1935 <widget class="QWidget" name="player7LeftJoycon" native="true">
1936 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_12">
1937 <property name="spacing">
1938 <number>0</number>
1939 </property>
1940 <property name="leftMargin">
1941 <number>0</number>
1942 </property>
1943 <property name="topMargin">
1944 <number>0</number>
1945 </property>
1946 <property name="rightMargin">
1947 <number>0</number>
1948 </property>
1949 <property name="bottomMargin">
1950 <number>0</number>
1951 </property>
1952 <item alignment="Qt::AlignHCenter">
1953 <widget class="QGroupBox" name="player7LeftBodyGroup">
1954 <property name="title">
1955 <string>L Body</string>
1956 </property>
1957 <property name="alignment">
1958 <set>Qt::AlignCenter</set>
1959 </property>
1960 <layout class="QVBoxLayout" name="verticalLayout_58">
1961 <property name="spacing">
1962 <number>3</number>
1963 </property>
1964 <property name="leftMargin">
1965 <number>3</number>
1966 </property>
1967 <property name="topMargin">
1968 <number>3</number>
1969 </property>
1970 <property name="rightMargin">
1971 <number>3</number>
1972 </property>
1973 <property name="bottomMargin">
1974 <number>3</number>
1975 </property>
1976 <item>
1977 <widget class="QPushButton" name="player7_left_body_button">
1978 <property name="sizePolicy">
1979 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1980 <horstretch>0</horstretch>
1981 <verstretch>0</verstretch>
1982 </sizepolicy>
1983 </property>
1984 <property name="minimumSize">
1985 <size>
1986 <width>57</width>
1987 <height>0</height>
1988 </size>
1989 </property>
1990 <property name="maximumSize">
1991 <size>
1992 <width>55</width>
1993 <height>16777215</height>
1994 </size>
1995 </property>
1996 <property name="styleSheet">
1997 <string notr="true">min-width: 55px;</string>
1998 </property>
1999 <property name="text">
2000 <string/>
2001 </property>
2002 </widget>
2003 </item>
2004 </layout>
2005 </widget>
2006 </item>
2007 <item alignment="Qt::AlignHCenter">
2008 <widget class="QGroupBox" name="player7LeftButtonsGroup">
2009 <property name="title">
2010 <string>L Button</string>
2011 </property>
2012 <property name="alignment">
2013 <set>Qt::AlignCenter</set>
2014 </property>
2015 <layout class="QVBoxLayout" name="verticalLayout_59">
2016 <property name="spacing">
2017 <number>3</number>
2018 </property>
2019 <property name="leftMargin">
2020 <number>3</number>
2021 </property>
2022 <property name="topMargin">
2023 <number>3</number>
2024 </property>
2025 <property name="rightMargin">
2026 <number>3</number>
2027 </property>
2028 <property name="bottomMargin">
2029 <number>3</number>
2030 </property>
2031 <item>
2032 <widget class="QPushButton" name="player7_left_buttons_button">
2033 <property name="sizePolicy">
2034 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2035 <horstretch>0</horstretch>
2036 <verstretch>0</verstretch>
2037 </sizepolicy>
2038 </property>
2039 <property name="minimumSize">
2040 <size>
2041 <width>57</width>
2042 <height>0</height>
2043 </size>
2044 </property>
2045 <property name="maximumSize">
2046 <size>
2047 <width>55</width>
2048 <height>16777215</height>
2049 </size>
2050 </property>
2051 <property name="styleSheet">
2052 <string notr="true">min-width: 55px;</string>
2053 </property>
2054 <property name="text">
2055 <string/>
2056 </property>
2057 </widget>
2058 </item>
2059 </layout>
2060 </widget>
2061 </item>
2062 </layout>
2063 </widget>
2064 </item>
2065 <item>
2066 <widget class="QWidget" name="player7RightJoycon" native="true">
2067 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_12">
2068 <property name="spacing">
2069 <number>0</number>
2070 </property>
2071 <property name="leftMargin">
2072 <number>0</number>
2073 </property>
2074 <property name="topMargin">
2075 <number>0</number>
2076 </property>
2077 <property name="rightMargin">
2078 <number>0</number>
2079 </property>
2080 <property name="bottomMargin">
2081 <number>0</number>
2082 </property>
2083 <item alignment="Qt::AlignHCenter">
2084 <widget class="QGroupBox" name="player7RightBodyGroup">
2085 <property name="title">
2086 <string>R Body</string>
2087 </property>
2088 <property name="alignment">
2089 <set>Qt::AlignCenter</set>
2090 </property>
2091 <layout class="QVBoxLayout" name="verticalLayout_56">
2092 <property name="spacing">
2093 <number>3</number>
2094 </property>
2095 <property name="leftMargin">
2096 <number>3</number>
2097 </property>
2098 <property name="topMargin">
2099 <number>3</number>
2100 </property>
2101 <property name="rightMargin">
2102 <number>3</number>
2103 </property>
2104 <property name="bottomMargin">
2105 <number>3</number>
2106 </property>
2107 <item>
2108 <widget class="QPushButton" name="player7_right_body_button">
2109 <property name="sizePolicy">
2110 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2111 <horstretch>0</horstretch>
2112 <verstretch>0</verstretch>
2113 </sizepolicy>
2114 </property>
2115 <property name="minimumSize">
2116 <size>
2117 <width>57</width>
2118 <height>0</height>
2119 </size>
2120 </property>
2121 <property name="maximumSize">
2122 <size>
2123 <width>55</width>
2124 <height>16777215</height>
2125 </size>
2126 </property>
2127 <property name="styleSheet">
2128 <string notr="true">min-width: 55px;</string>
2129 </property>
2130 <property name="text">
2131 <string/>
2132 </property>
2133 </widget>
2134 </item>
2135 </layout>
2136 </widget>
2137 </item>
2138 <item alignment="Qt::AlignHCenter">
2139 <widget class="QGroupBox" name="player7RightButtonsGroup">
2140 <property name="title">
2141 <string>R Button</string>
2142 </property>
2143 <property name="alignment">
2144 <set>Qt::AlignCenter</set>
2145 </property>
2146 <layout class="QVBoxLayout" name="verticalLayout_57">
2147 <property name="spacing">
2148 <number>3</number>
2149 </property>
2150 <property name="leftMargin">
2151 <number>3</number>
2152 </property>
2153 <property name="topMargin">
2154 <number>3</number>
2155 </property>
2156 <property name="rightMargin">
2157 <number>3</number>
2158 </property>
2159 <property name="bottomMargin">
2160 <number>3</number>
2161 </property>
2162 <item>
2163 <widget class="QPushButton" name="player7_right_buttons_button">
2164 <property name="sizePolicy">
2165 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2166 <horstretch>0</horstretch>
2167 <verstretch>0</verstretch>
2168 </sizepolicy>
2169 </property>
2170 <property name="minimumSize">
2171 <size>
2172 <width>57</width>
2173 <height>0</height>
2174 </size>
2175 </property>
2176 <property name="maximumSize">
2177 <size>
2178 <width>55</width>
2179 <height>16777215</height>
2180 </size>
2181 </property>
2182 <property name="styleSheet">
2183 <string notr="true">min-width: 55px;</string>
2184 </property>
2185 <property name="text">
2186 <string/>
2187 </property>
2188 </widget>
2189 </item>
2190 </layout>
2191 </widget>
2192 </item>
2193 </layout>
2194 </widget>
2195 </item>
2196 </layout>
2197 </widget>
2198 </item>
2199 <item>
2200 <widget class="QGroupBox" name="player8Group">
2201 <property name="title">
2202 <string>Player 8</string>
2203 </property>
2204 <layout class="QHBoxLayout" name="horizontalLayout_13">
2205 <property name="spacing">
2206 <number>6</number>
2207 </property>
2208 <property name="leftMargin">
2209 <number>6</number>
2210 </property>
2211 <property name="topMargin">
2212 <number>0</number>
2213 </property>
2214 <property name="rightMargin">
2215 <number>6</number>
2216 </property>
2217 <property name="bottomMargin">
2218 <number>6</number>
2219 </property>
2220 <item>
2221 <widget class="QWidget" name="player8LeftJoycon" native="true">
2222 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_13">
2223 <property name="spacing">
2224 <number>0</number>
2225 </property>
2226 <property name="leftMargin">
2227 <number>0</number>
2228 </property>
2229 <property name="topMargin">
2230 <number>0</number>
2231 </property>
2232 <property name="rightMargin">
2233 <number>0</number>
2234 </property>
2235 <property name="bottomMargin">
2236 <number>0</number>
2237 </property>
2238 <item alignment="Qt::AlignHCenter">
2239 <widget class="QGroupBox" name="player8LeftBodyGroup">
2240 <property name="title">
2241 <string>L Body</string>
2242 </property>
2243 <property name="alignment">
2244 <set>Qt::AlignCenter</set>
2245 </property>
2246 <layout class="QVBoxLayout" name="verticalLayout_62">
2247 <property name="spacing">
2248 <number>3</number>
2249 </property>
2250 <property name="leftMargin">
2251 <number>3</number>
2252 </property>
2253 <property name="topMargin">
2254 <number>3</number>
2255 </property>
2256 <property name="rightMargin">
2257 <number>3</number>
2258 </property>
2259 <property name="bottomMargin">
2260 <number>3</number>
2261 </property>
2262 <item>
2263 <widget class="QPushButton" name="player8_left_body_button">
2264 <property name="sizePolicy">
2265 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2266 <horstretch>0</horstretch>
2267 <verstretch>0</verstretch>
2268 </sizepolicy>
2269 </property>
2270 <property name="minimumSize">
2271 <size>
2272 <width>57</width>
2273 <height>0</height>
2274 </size>
2275 </property>
2276 <property name="maximumSize">
2277 <size>
2278 <width>55</width>
2279 <height>16777215</height>
2280 </size>
2281 </property>
2282 <property name="styleSheet">
2283 <string notr="true">min-width: 55px;</string>
2284 </property>
2285 <property name="text">
2286 <string/>
2287 </property>
2288 </widget>
2289 </item>
2290 </layout>
2291 </widget>
2292 </item>
2293 <item alignment="Qt::AlignHCenter">
2294 <widget class="QGroupBox" name="player8LeftButtonsGroup">
2295 <property name="title">
2296 <string>L Button</string>
2297 </property>
2298 <property name="alignment">
2299 <set>Qt::AlignCenter</set>
2300 </property>
2301 <layout class="QVBoxLayout" name="verticalLayout_63">
2302 <property name="spacing">
2303 <number>3</number>
2304 </property>
2305 <property name="leftMargin">
2306 <number>3</number>
2307 </property>
2308 <property name="topMargin">
2309 <number>3</number>
2310 </property>
2311 <property name="rightMargin">
2312 <number>3</number>
2313 </property>
2314 <property name="bottomMargin">
2315 <number>3</number>
2316 </property>
2317 <item>
2318 <widget class="QPushButton" name="player8_left_buttons_button">
2319 <property name="sizePolicy">
2320 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2321 <horstretch>0</horstretch>
2322 <verstretch>0</verstretch>
2323 </sizepolicy>
2324 </property>
2325 <property name="minimumSize">
2326 <size>
2327 <width>57</width>
2328 <height>0</height>
2329 </size>
2330 </property>
2331 <property name="maximumSize">
2332 <size>
2333 <width>55</width>
2334 <height>16777215</height>
2335 </size>
2336 </property>
2337 <property name="styleSheet">
2338 <string notr="true">min-width: 55px;</string>
2339 </property>
2340 <property name="text">
2341 <string/>
2342 </property>
2343 </widget>
2344 </item>
2345 </layout>
2346 </widget>
2347 </item>
2348 </layout>
2349 </widget>
2350 </item>
2351 <item>
2352 <widget class="QWidget" name="player8RightJoycon" native="true">
2353 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_13">
2354 <property name="spacing">
2355 <number>0</number>
2356 </property>
2357 <property name="leftMargin">
2358 <number>0</number>
2359 </property>
2360 <property name="topMargin">
2361 <number>0</number>
2362 </property>
2363 <property name="rightMargin">
2364 <number>0</number>
2365 </property>
2366 <property name="bottomMargin">
2367 <number>0</number>
2368 </property>
2369 <item alignment="Qt::AlignHCenter">
2370 <widget class="QGroupBox" name="player8RightBodyGroup">
2371 <property name="title">
2372 <string>R Body</string>
2373 </property>
2374 <property name="alignment">
2375 <set>Qt::AlignCenter</set>
2376 </property>
2377 <layout class="QVBoxLayout" name="verticalLayout_60">
2378 <property name="spacing">
2379 <number>3</number>
2380 </property>
2381 <property name="leftMargin">
2382 <number>3</number>
2383 </property>
2384 <property name="topMargin">
2385 <number>3</number>
2386 </property>
2387 <property name="rightMargin">
2388 <number>3</number>
2389 </property>
2390 <property name="bottomMargin">
2391 <number>3</number>
2392 </property>
2393 <item>
2394 <widget class="QPushButton" name="player8_right_body_button">
2395 <property name="sizePolicy">
2396 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2397 <horstretch>0</horstretch>
2398 <verstretch>0</verstretch>
2399 </sizepolicy>
2400 </property>
2401 <property name="minimumSize">
2402 <size>
2403 <width>57</width>
2404 <height>0</height>
2405 </size>
2406 </property>
2407 <property name="maximumSize">
2408 <size>
2409 <width>55</width>
2410 <height>16777215</height>
2411 </size>
2412 </property>
2413 <property name="styleSheet">
2414 <string notr="true">min-width: 55px;</string>
2415 </property>
2416 <property name="text">
2417 <string/>
2418 </property>
2419 </widget>
2420 </item>
2421 </layout>
2422 </widget>
2423 </item>
2424 <item alignment="Qt::AlignHCenter">
2425 <widget class="QGroupBox" name="player8RightButtonsGroup">
2426 <property name="title">
2427 <string>R Button</string>
2428 </property>
2429 <property name="alignment">
2430 <set>Qt::AlignCenter</set>
2431 </property>
2432 <layout class="QVBoxLayout" name="verticalLayout_61">
2433 <property name="spacing">
2434 <number>3</number>
2435 </property>
2436 <property name="leftMargin">
2437 <number>3</number>
2438 </property>
2439 <property name="topMargin">
2440 <number>3</number>
2441 </property>
2442 <property name="rightMargin">
2443 <number>3</number>
2444 </property>
2445 <property name="bottomMargin">
2446 <number>3</number>
2447 </property>
2448 <item>
2449 <widget class="QPushButton" name="player8_right_buttons_button">
2450 <property name="sizePolicy">
2451 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2452 <horstretch>0</horstretch>
2453 <verstretch>0</verstretch>
2454 </sizepolicy>
2455 </property>
2456 <property name="minimumSize">
2457 <size>
2458 <width>57</width>
2459 <height>0</height>
2460 </size>
2461 </property>
2462 <property name="maximumSize">
2463 <size>
2464 <width>55</width>
2465 <height>16777215</height>
2466 </size>
2467 </property>
2468 <property name="styleSheet">
2469 <string notr="true">min-width: 55px;</string>
2470 </property>
2471 <property name="text">
2472 <string/>
2473 </property>
2474 </widget>
2475 </item>
2476 </layout>
2477 </widget>
2478 </item>
2479 </layout>
2480 </widget>
2481 </item>
2482 </layout>
2483 </widget>
2484 </item>
2485 </layout>
2486 </widget>
2487 </item>
2488 </layout>
2489 </widget>
2490 </item>
2491 </layout>
2492 </widget>
2493 </item>
2494 </layout>
2495 </widget>
2496 </item>
2497 <item>
2498 <widget class="QWidget" name="rightInputAdvanced" native="true">
2499 <layout class="QVBoxLayout" name="rightLayout" stretch="1,1">
2500 <property name="spacing">
2501 <number>3</number>
2502 </property>
2503 <property name="leftMargin">
2504 <number>0</number>
2505 </property>
2506 <property name="topMargin">
2507 <number>0</number>
2508 </property>
2509 <property name="rightMargin">
2510 <number>0</number>
2511 </property>
2512 <property name="bottomMargin">
2513 <number>0</number>
2514 </property>
2515 <item>
2516 <widget class="QWidget" name="topRightInputAdvanced" native="true">
2517 <layout class="QVBoxLayout" name="verticalLayout">
2518 <property name="leftMargin">
2519 <number>0</number>
2520 </property>
2521 <property name="topMargin">
2522 <number>0</number>
2523 </property>
2524 <property name="rightMargin">
2525 <number>0</number>
2526 </property>
2527 <property name="bottomMargin">
2528 <number>0</number>
2529 </property>
2530 <item>
2531 <widget class="QGroupBox" name="gridGroupBox_3">
2532 <property name="title">
2533 <string>Other</string>
2534 </property>
2535 <layout class="QGridLayout" name="gridLayout_3">
2536 <item row="0" column="0">
2537 <widget class="QCheckBox" name="keyboard_enabled">
2538 <property name="minimumSize">
2539 <size>
2540 <width>0</width>
2541 <height>23</height>
2542 </size>
2543 </property>
2544 <property name="text">
2545 <string>Keyboard</string>
2546 </property>
2547 </widget>
2548 </item>
2549 <item row="4" column="2">
2550 <widget class="QPushButton" name="touchscreen_advanced">
2551 <property name="text">
2552 <string>Advanced</string>
2553 </property>
2554 </widget>
2555 </item>
2556 <item row="1" column="1">
2557 <spacer name="horizontalSpacer_8">
2558 <property name="orientation">
2559 <enum>Qt::Horizontal</enum>
2560 </property>
2561 <property name="sizeType">
2562 <enum>QSizePolicy::Fixed</enum>
2563 </property>
2564 <property name="sizeHint" stdset="0">
2565 <size>
2566 <width>76</width>
2567 <height>20</height>
2568 </size>
2569 </property>
2570 </spacer>
2571 </item>
2572 <item row="1" column="2">
2573 <widget class="QPushButton" name="mouse_advanced">
2574 <property name="text">
2575 <string>Advanced</string>
2576 </property>
2577 </widget>
2578 </item>
2579 <item row="4" column="0">
2580 <widget class="QCheckBox" name="touchscreen_enabled">
2581 <property name="text">
2582 <string>Touchscreen</string>
2583 </property>
2584 </widget>
2585 </item>
2586 <item row="1" column="0">
2587 <widget class="QCheckBox" name="mouse_enabled">
2588 <property name="minimumSize">
2589 <size>
2590 <width>0</width>
2591 <height>23</height>
2592 </size>
2593 </property>
2594 <property name="text">
2595 <string>Mouse</string>
2596 </property>
2597 </widget>
2598 </item>
2599 <item row="6" column="0">
2600 <widget class="QLabel" name="motion_touch">
2601 <property name="text">
2602 <string>Motion / Touch</string>
2603 </property>
2604 </widget>
2605 </item>
2606 <item row="6" column="2">
2607 <widget class="QPushButton" name="buttonMotionTouch">
2608 <property name="text">
2609 <string>Configure</string>
2610 </property>
2611 </widget>
2612 </item>
2613 <item row="5" column="0">
2614 <widget class="QCheckBox" name="debug_enabled">
2615 <property name="text">
2616 <string>Debug Controller</string>
2617 </property>
2618 </widget>
2619 </item>
2620 <item row="5" column="2">
2621 <widget class="QPushButton" name="debug_configure">
2622 <property name="text">
2623 <string>Configure</string>
2624 </property>
2625 </widget>
2626 </item>
2627 </layout>
2628 </widget>
2629 </item>
2630 <item>
2631 <spacer name="verticalSpacer">
2632 <property name="orientation">
2633 <enum>Qt::Vertical</enum>
2634 </property>
2635 <property name="sizeHint" stdset="0">
2636 <size>
2637 <width>20</width>
2638 <height>40</height>
2639 </size>
2640 </property>
2641 </spacer>
2642 </item>
2643 </layout>
2644 </widget>
2645 </item>
2646 <item>
2647 <widget class="QWidget" name="bottomRightInputAdvanced" native="true">
2648 <layout class="QVBoxLayout" name="verticalLayout_2">
2649 <property name="leftMargin">
2650 <number>0</number>
2651 </property>
2652 <property name="topMargin">
2653 <number>0</number>
2654 </property>
2655 <property name="rightMargin">
2656 <number>0</number>
2657 </property>
2658 <property name="bottomMargin">
2659 <number>0</number>
2660 </property>
2661 <item>
2662 <spacer name="mainVerticalSpacer">
2663 <property name="orientation">
2664 <enum>Qt::Vertical</enum>
2665 </property>
2666 <property name="sizeHint" stdset="0">
2667 <size>
2668 <width>20</width>
2669 <height>40</height>
2670 </size>
2671 </property>
2672 </spacer>
2673 </item>
2674 </layout>
2675 </widget>
2676 </item>
2677 </layout>
2678 </widget>
2679 </item>
2680 </layout>
2681 </widget>
2682 </item>
2683 </layout>
2684 </widget>
2685 <resources>
2686 </resources>
2687 <connections/>
2688</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 @@
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <utility> 7#include <utility>
8#include <QColorDialog>
9#include <QGridLayout> 8#include <QGridLayout>
9#include <QInputDialog>
10#include <QKeyEvent> 10#include <QKeyEvent>
11#include <QMenu> 11#include <QMenu>
12#include <QMessageBox> 12#include <QMessageBox>
13#include <QTimer> 13#include <QTimer>
14#include "common/assert.h"
15#include "common/param_package.h" 14#include "common/param_package.h"
15#include "core/core.h"
16#include "core/hle/service/hid/controllers/npad.h"
17#include "core/hle/service/hid/hid.h"
18#include "core/hle/service/sm/sm.h"
19#include "input_common/gcadapter/gc_poller.h"
16#include "input_common/main.h" 20#include "input_common/main.h"
17#include "ui_configure_input_player.h" 21#include "ui_configure_input_player.h"
18#include "yuzu/configuration/config.h" 22#include "yuzu/configuration/config.h"
19#include "yuzu/configuration/configure_input_player.h" 23#include "yuzu/configuration/configure_input_player.h"
20 24
25constexpr std::size_t HANDHELD_INDEX = 8;
26
21const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> 27const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
22 ConfigureInputPlayer::analog_sub_buttons{{ 28 ConfigureInputPlayer::analog_sub_buttons{{
23 "up", 29 "up",
24 "down", 30 "down",
25 "left", 31 "left",
26 "right", 32 "right",
27 "modifier",
28 }}; 33 }};
29 34
30static void LayerGridElements(QGridLayout* grid, QWidget* item, QWidget* onTopOf) { 35namespace {
31 const int index1 = grid->indexOf(item); 36
32 const int index2 = grid->indexOf(onTopOf); 37void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
33 int row, column, rowSpan, columnSpan; 38 bool connected) {
34 grid->getItemPosition(index2, &row, &column, &rowSpan, &columnSpan); 39 Core::System& system{Core::System::GetInstance()};
35 grid->takeAt(index1); 40 if (!system.IsPoweredOn()) {
36 grid->addWidget(item, row, column, rowSpan, columnSpan); 41 return;
42 }
43 Service::SM::ServiceManager& sm = system.ServiceManager();
44
45 auto& npad =
46 sm.GetService<Service::HID::Hid>("hid")
47 ->GetAppletResource()
48 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
49
50 npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
37} 51}
38 52
39static QString GetKeyName(int key_code) { 53/// Maps the controller type combobox index to Controller Type enum
54constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
55 switch (index) {
56 case 0:
57 default:
58 return Settings::ControllerType::ProController;
59 case 1:
60 return Settings::ControllerType::DualJoyconDetached;
61 case 2:
62 return Settings::ControllerType::LeftJoycon;
63 case 3:
64 return Settings::ControllerType::RightJoycon;
65 case 4:
66 return Settings::ControllerType::Handheld;
67 }
68}
69
70/// Maps the Controller Type enum to controller type combobox index
71constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
72 switch (type) {
73 case Settings::ControllerType::ProController:
74 default:
75 return 0;
76 case Settings::ControllerType::DualJoyconDetached:
77 return 1;
78 case Settings::ControllerType::LeftJoycon:
79 return 2;
80 case Settings::ControllerType::RightJoycon:
81 return 3;
82 case Settings::ControllerType::Handheld:
83 return 4;
84 }
85}
86
87QString GetKeyName(int key_code) {
40 switch (key_code) { 88 switch (key_code) {
89 case Qt::LeftButton:
90 return QObject::tr("Click 0");
91 case Qt::RightButton:
92 return QObject::tr("Click 1");
93 case Qt::MiddleButton:
94 return QObject::tr("Click 2");
95 case Qt::BackButton:
96 return QObject::tr("Click 3");
97 case Qt::ForwardButton:
98 return QObject::tr("Click 4");
41 case Qt::Key_Shift: 99 case Qt::Key_Shift:
42 return QObject::tr("Shift"); 100 return QObject::tr("Shift");
43 case Qt::Key_Control: 101 case Qt::Key_Control:
@@ -51,9 +109,16 @@ static QString GetKeyName(int key_code) {
51 } 109 }
52} 110}
53 111
54static void SetAnalogButton(const Common::ParamPackage& input_param, 112void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
55 Common::ParamPackage& analog_param, const std::string& button_name) { 113 const std::string& button_name) {
56 if (analog_param.Get("engine", "") != "analog_from_button") { 114 // The poller returned a complete axis, so set all the buttons
115 if (input_param.Has("axis_x") && input_param.Has("axis_y")) {
116 analog_param = input_param;
117 return;
118 }
119 // Check if the current configuration has either no engine or an axis binding.
120 // Clears out the old binding and adds one with analog_from_button.
121 if (!analog_param.Has("engine") || analog_param.Has("axis_x") || analog_param.Has("axis_y")) {
57 analog_param = { 122 analog_param = {
58 {"engine", "analog_from_button"}, 123 {"engine", "analog_from_button"},
59 }; 124 };
@@ -61,7 +126,7 @@ static void SetAnalogButton(const Common::ParamPackage& input_param,
61 analog_param.Set(button_name, input_param.Serialize()); 126 analog_param.Set(button_name, input_param.Serialize());
62} 127}
63 128
64static QString ButtonToText(const Common::ParamPackage& param) { 129QString ButtonToText(const Common::ParamPackage& param) {
65 if (!param.Has("engine")) { 130 if (!param.Has("engine")) {
66 return QObject::tr("[not set]"); 131 return QObject::tr("[not set]");
67 } 132 }
@@ -111,7 +176,7 @@ static QString ButtonToText(const Common::ParamPackage& param) {
111 return QObject::tr("[unknown]"); 176 return QObject::tr("[unknown]");
112} 177}
113 178
114static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) { 179QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
115 if (!param.Has("engine")) { 180 if (!param.Has("engine")) {
116 return QObject::tr("[not set]"); 181 return QObject::tr("[not set]");
117 } 182 }
@@ -161,22 +226,25 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string
161 } 226 }
162 return QObject::tr("[unknown]"); 227 return QObject::tr("[unknown]");
163} 228}
164 229} // namespace
165ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug) 230
166 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), 231ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index,
167 debug(debug), timeout_timer(std::make_unique<QTimer>()), 232 QWidget* bottom_row,
168 poll_timer(std::make_unique<QTimer>()) { 233 InputCommon::InputSubsystem* input_subsystem_,
234 bool debug)
235 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
236 debug(debug), input_subsystem{input_subsystem_}, timeout_timer(std::make_unique<QTimer>()),
237 poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row) {
169 ui->setupUi(this); 238 ui->setupUi(this);
239
170 setFocusPolicy(Qt::ClickFocus); 240 setFocusPolicy(Qt::ClickFocus);
171 241
172 button_map = { 242 button_map = {
173 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, 243 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
174 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, 244 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
175 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, 245 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
176 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, 246 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
177 ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown, 247 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
178 ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
179 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
180 }; 248 };
181 249
182 analog_map_buttons = {{ 250 analog_map_buttons = {{
@@ -185,220 +253,165 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
185 ui->buttonLStickDown, 253 ui->buttonLStickDown,
186 ui->buttonLStickLeft, 254 ui->buttonLStickLeft,
187 ui->buttonLStickRight, 255 ui->buttonLStickRight,
188 ui->buttonLStickMod,
189 }, 256 },
190 { 257 {
191 ui->buttonRStickUp, 258 ui->buttonRStickUp,
192 ui->buttonRStickDown, 259 ui->buttonRStickDown,
193 ui->buttonRStickLeft, 260 ui->buttonRStickLeft,
194 ui->buttonRStickRight, 261 ui->buttonRStickRight,
195 ui->buttonRStickMod,
196 }, 262 },
197 }}; 263 }};
198 264
199 debug_hidden = { 265 analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone};
200 ui->buttonSL, ui->labelSL, 266 analog_map_deadzone_slider = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone};
201 ui->buttonSR, ui->labelSR, 267 analog_map_modifier_groupbox = {ui->buttonLStickModGroup, ui->buttonRStickModGroup};
202 ui->buttonLStick, ui->labelLStickPressed, 268 analog_map_modifier_label = {ui->labelLStickModifierRange, ui->labelRStickModifierRange};
203 ui->buttonRStick, ui->labelRStickPressed, 269 analog_map_modifier_slider = {ui->sliderLStickModifierRange, ui->sliderRStickModifierRange};
204 ui->buttonHome, ui->labelHome, 270 analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup};
205 ui->buttonScreenshot, ui->labelScreenshot, 271 analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange};
206 };
207
208 auto layout = Settings::values.players[player_index].type;
209 if (debug)
210 layout = Settings::ControllerType::DualJoycon;
211
212 switch (layout) {
213 case Settings::ControllerType::ProController:
214 case Settings::ControllerType::DualJoycon:
215 layout_hidden = {
216 ui->buttonSL,
217 ui->labelSL,
218 ui->buttonSR,
219 ui->labelSR,
220 };
221 break;
222 case Settings::ControllerType::LeftJoycon:
223 layout_hidden = {
224 ui->right_body_button,
225 ui->right_buttons_button,
226 ui->right_body_label,
227 ui->right_buttons_label,
228 ui->buttonR,
229 ui->labelR,
230 ui->buttonZR,
231 ui->labelZR,
232 ui->labelHome,
233 ui->buttonHome,
234 ui->buttonPlus,
235 ui->labelPlus,
236 ui->RStick,
237 ui->faceButtons,
238 };
239 break;
240 case Settings::ControllerType::RightJoycon:
241 layout_hidden = {
242 ui->left_body_button, ui->left_buttons_button,
243 ui->left_body_label, ui->left_buttons_label,
244 ui->buttonL, ui->labelL,
245 ui->buttonZL, ui->labelZL,
246 ui->labelScreenshot, ui->buttonScreenshot,
247 ui->buttonMinus, ui->labelMinus,
248 ui->LStick, ui->Dpad,
249 };
250 break;
251 }
252
253 if (debug || layout == Settings::ControllerType::ProController) {
254 ui->controller_color->hide();
255 } else {
256 if (layout == Settings::ControllerType::LeftJoycon ||
257 layout == Settings::ControllerType::RightJoycon) {
258 ui->horizontalSpacer_4->setGeometry({0, 0, 0, 0});
259
260 LayerGridElements(ui->buttons, ui->shoulderButtons, ui->Dpad);
261 LayerGridElements(ui->buttons, ui->misc, ui->RStick);
262 LayerGridElements(ui->buttons, ui->Dpad, ui->faceButtons);
263 LayerGridElements(ui->buttons, ui->RStick, ui->LStick);
264 }
265 }
266 272
267 for (auto* widget : layout_hidden) 273 const auto ConfigureButtonClick = [&](QPushButton* button, Common::ParamPackage* param,
268 widget->setVisible(false); 274 int default_val) {
269 275 connect(button, &QPushButton::clicked, [=, this] {
270 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; 276 HandleClick(
271 analog_map_deadzone_and_modifier_slider = {ui->sliderLStickDeadzoneAndModifier, 277 button,
272 ui->sliderRStickDeadzoneAndModifier}; 278 [=, this](Common::ParamPackage params) {
273 analog_map_deadzone_and_modifier_slider_label = {ui->labelLStickDeadzoneAndModifier, 279 // Workaround for ZL & ZR for analog triggers like on XBOX
274 ui->labelRStickDeadzoneAndModifier}; 280 // controllers. Analog triggers (from controllers like the XBOX
281 // controller) would not work due to a different range of their
282 // signals (from 0 to 255 on analog triggers instead of -32768 to
283 // 32768 on analog joysticks). The SDL driver misinterprets analog
284 // triggers as analog joysticks.
285 // TODO: reinterpret the signal range for analog triggers to map the
286 // values correctly. This is required for the correct emulation of
287 // the analog triggers of the GameCube controller.
288 if (button == ui->buttonZL || button == ui->buttonZR) {
289 params.Set("direction", "+");
290 params.Set("threshold", "0.5");
291 }
292 *param = std::move(params);
293 },
294 InputCommon::Polling::DeviceType::Button);
295 });
296 };
275 297
276 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 298 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
277 auto* const button = button_map[button_id]; 299 auto* const button = button_map[button_id];
278 if (button == nullptr) { 300 if (button == nullptr) {
279 continue; 301 continue;
280 } 302 }
281 303 ConfigureButtonClick(button_map[button_id], &buttons_param[button_id],
282 button->setContextMenuPolicy(Qt::CustomContextMenu); 304 Config::default_buttons[button_id]);
283 connect(button, &QPushButton::clicked, [=, this] {
284 HandleClick(button_map[button_id],
285 [=, this](Common::ParamPackage params) {
286 // Workaround for ZL & ZR for analog triggers like on XBOX controllors.
287 // Analog triggers (from controllers like the XBOX controller) would not
288 // work due to a different range of their signals (from 0 to 255 on
289 // analog triggers instead of -32768 to 32768 on analog joysticks). The
290 // SDL driver misinterprets analog triggers as analog joysticks.
291 // TODO: reinterpret the signal range for analog triggers to map the
292 // values correctly. This is required for the correct emulation of the
293 // analog triggers of the GameCube controller.
294 if (button_id == Settings::NativeButton::ZL ||
295 button_id == Settings::NativeButton::ZR) {
296 params.Set("direction", "+");
297 params.Set("threshold", "0.5");
298 }
299 buttons_param[button_id] = std::move(params);
300 },
301 InputCommon::Polling::DeviceType::Button);
302 });
303 connect(button, &QPushButton::customContextMenuRequested,
304 [=, this](const QPoint& menu_location) {
305 QMenu context_menu;
306 context_menu.addAction(tr("Clear"), [&] {
307 buttons_param[button_id].Clear();
308 button_map[button_id]->setText(tr("[not set]"));
309 });
310 context_menu.addAction(tr("Restore Default"), [&] {
311 buttons_param[button_id] = Common::ParamPackage{
312 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
313 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
314 });
315 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
316 });
317 } 305 }
318 306
319 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 307 // Handle clicks for the modifier buttons as well.
320 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 308 ConfigureButtonClick(ui->buttonLStickMod, &lstick_mod, Config::default_stick_mod[0]);
309 ConfigureButtonClick(ui->buttonRStickMod, &rstick_mod, Config::default_stick_mod[1]);
310
311 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
312 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
321 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; 313 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
314
322 if (analog_button == nullptr) { 315 if (analog_button == nullptr) {
323 continue; 316 continue;
324 } 317 }
325 318
326 analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
327 connect(analog_button, &QPushButton::clicked, [=, this] { 319 connect(analog_button, &QPushButton::clicked, [=, this] {
328 HandleClick(analog_map_buttons[analog_id][sub_button_id], 320 HandleClick(
329 [=, this](const Common::ParamPackage& params) { 321 analog_map_buttons[analog_id][sub_button_id],
330 SetAnalogButton(params, analogs_param[analog_id], 322 [=, this](const Common::ParamPackage& params) {
331 analog_sub_buttons[sub_button_id]); 323 SetAnalogParam(params, analogs_param[analog_id],
332 }, 324 analog_sub_buttons[sub_button_id]);
333 InputCommon::Polling::DeviceType::Button); 325 },
326 InputCommon::Polling::DeviceType::AnalogPreferred);
334 }); 327 });
335 connect(analog_button, &QPushButton::customContextMenuRequested,
336 [=, this](const QPoint& menu_location) {
337 QMenu context_menu;
338 context_menu.addAction(tr("Clear"), [&] {
339 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
340 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
341 });
342 context_menu.addAction(tr("Restore Default"), [&] {
343 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
344 Config::default_analogs[analog_id][sub_button_id])};
345 SetAnalogButton(params, analogs_param[analog_id],
346 analog_sub_buttons[sub_button_id]);
347 analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
348 analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
349 });
350 context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
351 menu_location));
352 });
353 } 328 }
354 connect(analog_map_stick[analog_id], &QPushButton::clicked, [=, this] {
355 if (QMessageBox::information(
356 this, tr("Information"),
357 tr("After pressing OK, first move your joystick horizontally, "
358 "and then vertically."),
359 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
360 HandleClick(analog_map_stick[analog_id],
361 [=, this](const Common::ParamPackage& params) {
362 analogs_param[analog_id] = params;
363 },
364 InputCommon::Polling::DeviceType::Analog);
365 }
366 });
367 329
368 connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, 330 connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged),
369 [=, this] { 331 [=, this] {
370 const float slider_value = 332 const auto spinbox_value = analog_map_range_spinbox[analog_id]->value();
371 analog_map_deadzone_and_modifier_slider[analog_id]->value(); 333 analogs_param[analog_id].Set("range", spinbox_value / 100.0f);
372 if (analogs_param[analog_id].Get("engine", "") == "sdl" ||
373 analogs_param[analog_id].Get("engine", "") == "gcpad") {
374 analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
375 tr("Deadzone: %1%").arg(slider_value));
376 analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
377 } else {
378 analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
379 tr("Modifier Scale: %1%").arg(slider_value));
380 analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
381 }
382 }); 334 });
335
336 connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] {
337 const auto slider_value = analog_map_deadzone_slider[analog_id]->value();
338 analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value));
339 analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
340 });
341
342 connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] {
343 const auto slider_value = analog_map_modifier_slider[analog_id]->value();
344 analog_map_modifier_label[analog_id]->setText(
345 tr("Modifier Range: %1%").arg(slider_value));
346 analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
347 });
383 } 348 }
384 349
385 connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); 350 // Player Connected checkbox
386 connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); }); 351 connect(ui->groupConnectedController, &QGroupBox::toggled,
352 [this](bool checked) { emit Connected(checked); });
353
354 // Set up controller type. Only Player 1 can choose Handheld.
355 ui->comboControllerType->clear();
356
357 QStringList controller_types = {
358 tr("Pro Controller"),
359 tr("Dual Joycons"),
360 tr("Left Joycon"),
361 tr("Right Joycon"),
362 };
363
364 if (player_index == 0) {
365 controller_types.append(tr("Handheld"));
366 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
367 [this](int index) {
368 emit HandheldStateChanged(GetControllerTypeFromIndex(index) ==
369 Settings::ControllerType::Handheld);
370 });
371 }
372
373 // The Debug Controller can only choose the Pro Controller.
374 if (debug) {
375 ui->buttonScreenshot->setEnabled(false);
376 ui->buttonHome->setEnabled(false);
377 ui->groupConnectedController->setCheckable(false);
378 QStringList debug_controller_types = {
379 tr("Pro Controller"),
380 };
381 ui->comboControllerType->addItems(debug_controller_types);
382 } else {
383 ui->comboControllerType->addItems(controller_types);
384 }
385
386 UpdateControllerIcon();
387 UpdateControllerAvailableButtons();
388 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
389 UpdateControllerIcon();
390 UpdateControllerAvailableButtons();
391 });
392
393 connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this,
394 &ConfigureInputPlayer::UpdateMappingWithDefaults);
395
396 ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
397 UpdateInputDevices();
398 connect(ui->buttonRefreshDevices, &QPushButton::clicked,
399 [this] { emit RefreshInputDevices(); });
387 400
388 timeout_timer->setSingleShot(true); 401 timeout_timer->setSingleShot(true);
389 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); 402 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
390 403
391 connect(poll_timer.get(), &QTimer::timeout, [this] { 404 connect(poll_timer.get(), &QTimer::timeout, [this] {
392 Common::ParamPackage params; 405 Common::ParamPackage params;
393 if (InputCommon::GetGCButtons()->IsPolling()) { 406 if (input_subsystem->GetGCButtons()->IsPolling()) {
394 params = InputCommon::GetGCButtons()->GetNextInput(); 407 params = input_subsystem->GetGCButtons()->GetNextInput();
395 if (params.Has("engine")) { 408 if (params.Has("engine")) {
396 SetPollingResult(params, false); 409 SetPollingResult(params, false);
397 return; 410 return;
398 } 411 }
399 } 412 }
400 if (InputCommon::GetGCAnalogs()->IsPolling()) { 413 if (input_subsystem->GetGCAnalogs()->IsPolling()) {
401 params = InputCommon::GetGCAnalogs()->GetNextInput(); 414 params = input_subsystem->GetGCAnalogs()->GetNextInput();
402 if (params.Has("engine")) { 415 if (params.Has("engine")) {
403 SetPollingResult(params, false); 416 SetPollingResult(params, false);
404 return; 417 return;
@@ -413,20 +426,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
413 } 426 }
414 }); 427 });
415 428
416 controller_color_buttons = {
417 ui->left_body_button,
418 ui->left_buttons_button,
419 ui->right_body_button,
420 ui->right_buttons_button,
421 };
422
423 for (std::size_t i = 0; i < controller_color_buttons.size(); ++i) {
424 connect(controller_color_buttons[i], &QPushButton::clicked, this,
425 [this, i] { OnControllerButtonClick(static_cast<int>(i)); });
426 }
427
428 LoadConfiguration(); 429 LoadConfiguration();
429 resize(0, 0);
430 430
431 // TODO(wwylele): enable this when we actually emulate it 431 // TODO(wwylele): enable this when we actually emulate it
432 ui->buttonHome->setEnabled(false); 432 ui->buttonHome->setEnabled(false);
@@ -435,27 +435,43 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
435ConfigureInputPlayer::~ConfigureInputPlayer() = default; 435ConfigureInputPlayer::~ConfigureInputPlayer() = default;
436 436
437void ConfigureInputPlayer::ApplyConfiguration() { 437void ConfigureInputPlayer::ApplyConfiguration() {
438 auto& buttons = 438 auto& player = Settings::values.players[player_index];
439 debug ? Settings::values.debug_pad_buttons : Settings::values.players[player_index].buttons; 439 auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons;
440 auto& analogs = 440 auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs;
441 debug ? Settings::values.debug_pad_analogs : Settings::values.players[player_index].analogs;
442 441
443 std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(), 442 std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(),
444 [](const Common::ParamPackage& param) { return param.Serialize(); }); 443 [](const Common::ParamPackage& param) { return param.Serialize(); });
445 std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(), 444 std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
446 [](const Common::ParamPackage& param) { return param.Serialize(); }); 445 [](const Common::ParamPackage& param) { return param.Serialize(); });
447 446
448 if (debug) 447 if (debug) {
448 return;
449 }
450
451 player.controller_type =
452 static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
453 player.connected = ui->groupConnectedController->isChecked();
454
455 // Player 2-8
456 if (player_index != 0) {
457 UpdateController(player.controller_type, player_index, player.connected);
449 return; 458 return;
459 }
450 460
451 std::array<u32, 4> colors{}; 461 // Player 1 and Handheld
452 std::transform(controller_colors.begin(), controller_colors.end(), colors.begin(), 462 auto& handheld = Settings::values.players[HANDHELD_INDEX];
453 [](QColor color) { return color.rgb(); }); 463 // If Handheld is selected, copy all the settings from Player 1 to Handheld.
464 if (player.controller_type == Settings::ControllerType::Handheld) {
465 handheld = player;
466 handheld.connected = ui->groupConnectedController->isChecked();
467 player.connected = false; // Disconnect Player 1
468 } else {
469 player.connected = ui->groupConnectedController->isChecked();
470 handheld.connected = false; // Disconnect Handheld
471 }
454 472
455 Settings::values.players[player_index].body_color_left = colors[0]; 473 UpdateController(player.controller_type, player_index, player.connected);
456 Settings::values.players[player_index].button_color_left = colors[1]; 474 UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected);
457 Settings::values.players[player_index].body_color_right = colors[2];
458 Settings::values.players[player_index].button_color_right = colors[3];
459} 475}
460 476
461void ConfigureInputPlayer::changeEvent(QEvent* event) { 477void ConfigureInputPlayer::changeEvent(QEvent* event) {
@@ -463,24 +479,16 @@ void ConfigureInputPlayer::changeEvent(QEvent* event) {
463 RetranslateUI(); 479 RetranslateUI();
464 } 480 }
465 481
466 QDialog::changeEvent(event); 482 QWidget::changeEvent(event);
467} 483}
468 484
469void ConfigureInputPlayer::RetranslateUI() { 485void ConfigureInputPlayer::RetranslateUI() {
470 ui->retranslateUi(this); 486 ui->retranslateUi(this);
471 UpdateButtonLabels(); 487 UpdateUI();
472}
473
474void ConfigureInputPlayer::OnControllerButtonClick(int i) {
475 const QColor new_bg_color = QColorDialog::getColor(controller_colors[i]);
476 if (!new_bg_color.isValid())
477 return;
478 controller_colors[i] = new_bg_color;
479 controller_color_buttons[i]->setStyleSheet(
480 QStringLiteral("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
481} 488}
482 489
483void ConfigureInputPlayer::LoadConfiguration() { 490void ConfigureInputPlayer::LoadConfiguration() {
491 auto& player = Settings::values.players[player_index];
484 if (debug) { 492 if (debug) {
485 std::transform(Settings::values.debug_pad_buttons.begin(), 493 std::transform(Settings::values.debug_pad_buttons.begin(),
486 Settings::values.debug_pad_buttons.end(), buttons_param.begin(), 494 Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
@@ -489,56 +497,60 @@ void ConfigureInputPlayer::LoadConfiguration() {
489 Settings::values.debug_pad_analogs.end(), analogs_param.begin(), 497 Settings::values.debug_pad_analogs.end(), analogs_param.begin(),
490 [](const std::string& str) { return Common::ParamPackage(str); }); 498 [](const std::string& str) { return Common::ParamPackage(str); });
491 } else { 499 } else {
492 std::transform(Settings::values.players[player_index].buttons.begin(), 500 std::transform(player.buttons.begin(), player.buttons.end(), buttons_param.begin(),
493 Settings::values.players[player_index].buttons.end(), buttons_param.begin(),
494 [](const std::string& str) { return Common::ParamPackage(str); }); 501 [](const std::string& str) { return Common::ParamPackage(str); });
495 std::transform(Settings::values.players[player_index].analogs.begin(), 502 std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(),
496 Settings::values.players[player_index].analogs.end(), analogs_param.begin(),
497 [](const std::string& str) { return Common::ParamPackage(str); }); 503 [](const std::string& str) { return Common::ParamPackage(str); });
498 } 504 }
499 505
500 UpdateButtonLabels(); 506 UpdateUI();
501 507
502 if (debug) 508 if (debug) {
503 return; 509 return;
510 }
504 511
505 std::array<u32, 4> colors = { 512 ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type));
506 Settings::values.players[player_index].body_color_left, 513 ui->groupConnectedController->setChecked(
507 Settings::values.players[player_index].button_color_left, 514 player.connected ||
508 Settings::values.players[player_index].body_color_right, 515 (player_index == 0 && Settings::values.players[HANDHELD_INDEX].connected));
509 Settings::values.players[player_index].button_color_right, 516}
510 };
511
512 std::transform(colors.begin(), colors.end(), controller_colors.begin(),
513 [](u32 rgb) { return QColor::fromRgb(rgb); });
514 517
515 for (std::size_t i = 0; i < colors.size(); ++i) { 518void ConfigureInputPlayer::UpdateInputDevices() {
516 controller_color_buttons[i]->setStyleSheet( 519 input_devices = input_subsystem->GetInputDevices();
517 QStringLiteral("QPushButton { background-color: %1 }") 520 ui->comboDevices->clear();
518 .arg(controller_colors[i].name())); 521 for (auto device : input_devices) {
522 ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
519 } 523 }
520} 524}
521 525
522void ConfigureInputPlayer::RestoreDefaults() { 526void ConfigureInputPlayer::RestoreDefaults() {
523 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 527 // Reset Buttons
528 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
524 buttons_param[button_id] = Common::ParamPackage{ 529 buttons_param[button_id] = Common::ParamPackage{
525 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; 530 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
526 } 531 }
527 532
528 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 533 // Reset Modifier Buttons
529 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 534 lstick_mod =
535 Common::ParamPackage(InputCommon::GenerateKeyboardParam(Config::default_stick_mod[0]));
536 rstick_mod =
537 Common::ParamPackage(InputCommon::GenerateKeyboardParam(Config::default_stick_mod[1]));
538
539 // Reset Analogs
540 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
541 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
530 Common::ParamPackage params{InputCommon::GenerateKeyboardParam( 542 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
531 Config::default_analogs[analog_id][sub_button_id])}; 543 Config::default_analogs[analog_id][sub_button_id])};
532 SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); 544 SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
533 } 545 }
534 } 546 }
535 547 UpdateUI();
536 UpdateButtonLabels(); 548 UpdateInputDevices();
537 ApplyConfiguration(); 549 ui->comboControllerType->setCurrentIndex(0);
538} 550}
539 551
540void ConfigureInputPlayer::ClearAll() { 552void ConfigureInputPlayer::ClearAll() {
541 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 553 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
542 const auto* const button = button_map[button_id]; 554 const auto* const button = button_map[button_id];
543 if (button == nullptr || !button->isEnabled()) { 555 if (button == nullptr || !button->isEnabled()) {
544 continue; 556 continue;
@@ -547,8 +559,11 @@ void ConfigureInputPlayer::ClearAll() {
547 buttons_param[button_id].Clear(); 559 buttons_param[button_id].Clear();
548 } 560 }
549 561
550 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 562 lstick_mod.Clear();
551 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 563 rstick_mod.Clear();
564
565 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
566 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
552 const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; 567 const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
553 if (analog_button == nullptr || !analog_button->isEnabled()) { 568 if (analog_button == nullptr || !analog_button->isEnabled()) {
554 continue; 569 continue;
@@ -558,17 +573,20 @@ void ConfigureInputPlayer::ClearAll() {
558 } 573 }
559 } 574 }
560 575
561 UpdateButtonLabels(); 576 UpdateUI();
562 ApplyConfiguration(); 577 UpdateInputDevices();
563} 578}
564 579
565void ConfigureInputPlayer::UpdateButtonLabels() { 580void ConfigureInputPlayer::UpdateUI() {
566 for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { 581 for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) {
567 button_map[button]->setText(ButtonToText(buttons_param[button])); 582 button_map[button]->setText(ButtonToText(buttons_param[button]));
568 } 583 }
569 584
570 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 585 ui->buttonLStickMod->setText(ButtonToText(lstick_mod));
571 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 586 ui->buttonRStickMod->setText(ButtonToText(rstick_mod));
587
588 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
589 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
572 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; 590 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
573 591
574 if (analog_button == nullptr) { 592 if (analog_button == nullptr) {
@@ -578,99 +596,141 @@ void ConfigureInputPlayer::UpdateButtonLabels() {
578 analog_button->setText( 596 analog_button->setText(
579 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); 597 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
580 } 598 }
581 analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
582 599
600 const auto deadzone_label = analog_map_deadzone_label[analog_id];
601 const auto deadzone_slider = analog_map_deadzone_slider[analog_id];
602 const auto modifier_groupbox = analog_map_modifier_groupbox[analog_id];
603 const auto modifier_label = analog_map_modifier_label[analog_id];
604 const auto modifier_slider = analog_map_modifier_slider[analog_id];
605 const auto range_groupbox = analog_map_range_groupbox[analog_id];
606 const auto range_spinbox = analog_map_range_spinbox[analog_id];
607
608 int slider_value;
583 auto& param = analogs_param[analog_id]; 609 auto& param = analogs_param[analog_id];
584 auto* const analog_stick_slider = analog_map_deadzone_and_modifier_slider[analog_id]; 610 const bool is_controller =
585 auto* const analog_stick_slider_label = 611 param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad";
586 analog_map_deadzone_and_modifier_slider_label[analog_id]; 612
587 613 if (is_controller) {
588 if (param.Has("engine")) { 614 if (!param.Has("deadzone")) {
589 if (param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad") { 615 param.Set("deadzone", 0.1f);
590 if (!param.Has("deadzone")) { 616 }
591 param.Set("deadzone", 0.1f); 617 slider_value = static_cast<int>(param.Get("deadzone", 0.1f) * 100);
592 } 618 deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value));
593 619 deadzone_slider->setValue(slider_value);
594 analog_stick_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100)); 620 if (!param.Has("range")) {
595 if (analog_stick_slider->value() == 0) { 621 param.Set("range", 1.0f);
596 analog_stick_slider_label->setText(tr("Deadzone: 0%")); 622 }
597 } 623 range_spinbox->setValue(static_cast<int>(param.Get("range", 1.0f) * 100));
598 } else { 624 } else {
599 if (!param.Has("modifier_scale")) { 625 if (!param.Has("modifier_scale")) {
600 param.Set("modifier_scale", 0.5f); 626 param.Set("modifier_scale", 0.5f);
601 }
602
603 analog_stick_slider->setValue(
604 static_cast<int>(param.Get("modifier_scale", 0.5f) * 100));
605 if (analog_stick_slider->value() == 0) {
606 analog_stick_slider_label->setText(tr("Modifier Scale: 0%"));
607 }
608 } 627 }
628 slider_value = static_cast<int>(param.Get("modifier_scale", 0.5f) * 100);
629 modifier_label->setText(tr("Modifier Range: %1%").arg(slider_value));
630 modifier_slider->setValue(slider_value);
609 } 631 }
632
633 deadzone_label->setVisible(is_controller);
634 deadzone_slider->setVisible(is_controller);
635 modifier_groupbox->setVisible(!is_controller);
636 modifier_label->setVisible(!is_controller);
637 modifier_slider->setVisible(!is_controller);
638 range_groupbox->setVisible(is_controller);
610 } 639 }
611} 640}
612 641
642void ConfigureInputPlayer::UpdateMappingWithDefaults() {
643 if (ui->comboDevices->currentIndex() < 2) {
644 return;
645 }
646 const auto& device = input_devices[ui->comboDevices->currentIndex()];
647 auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
648 auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
649 for (int i = 0; i < buttons_param.size(); ++i) {
650 buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)];
651 }
652 for (int i = 0; i < analogs_param.size(); ++i) {
653 analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)];
654 }
655
656 UpdateUI();
657}
658
613void ConfigureInputPlayer::HandleClick( 659void ConfigureInputPlayer::HandleClick(
614 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, 660 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
615 InputCommon::Polling::DeviceType type) { 661 InputCommon::Polling::DeviceType type) {
616 button->setText(tr("[press key]")); 662 button->setText(tr("[waiting]"));
617 button->setFocus(); 663 button->setFocus();
618 664
619 // Keyboard keys can only be used as button devices 665 // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
620 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; 666 // controller, then they don't want keyboard/mouse input
621 if (want_keyboard_keys) { 667 want_keyboard_mouse = ui->comboDevices->currentIndex() < 2;
622 const auto iter = std::find(button_map.begin(), button_map.end(), button);
623 ASSERT(iter != button_map.end());
624 const auto index = std::distance(button_map.begin(), iter);
625 ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
626 }
627 668
628 input_setter = new_input_setter; 669 input_setter = new_input_setter;
629 670
630 device_pollers = InputCommon::Polling::GetPollers(type); 671 device_pollers = input_subsystem->GetPollers(type);
631 672
632 for (auto& poller : device_pollers) { 673 for (auto& poller : device_pollers) {
633 poller->Start(); 674 poller->Start();
634 } 675 }
635 676
636 grabKeyboard(); 677 QWidget::grabMouse();
637 grabMouse(); 678 QWidget::grabKeyboard();
679
638 if (type == InputCommon::Polling::DeviceType::Button) { 680 if (type == InputCommon::Polling::DeviceType::Button) {
639 InputCommon::GetGCButtons()->BeginConfiguration(); 681 input_subsystem->GetGCButtons()->BeginConfiguration();
640 } else { 682 } else {
641 InputCommon::GetGCAnalogs()->BeginConfiguration(); 683 input_subsystem->GetGCAnalogs()->BeginConfiguration();
642 } 684 }
643 timeout_timer->start(5000); // Cancel after 5 seconds 685
644 poll_timer->start(200); // Check for new inputs every 200ms 686 timeout_timer->start(2500); // Cancel after 2.5 seconds
687 poll_timer->start(50); // Check for new inputs every 50ms
645} 688}
646 689
647void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) { 690void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) {
648 releaseKeyboard();
649 releaseMouse();
650 timeout_timer->stop(); 691 timeout_timer->stop();
651 poll_timer->stop(); 692 poll_timer->stop();
652 for (auto& poller : device_pollers) { 693 for (auto& poller : device_pollers) {
653 poller->Stop(); 694 poller->Stop();
654 } 695 }
655 696
656 InputCommon::GetGCButtons()->EndConfiguration(); 697 QWidget::releaseMouse();
657 InputCommon::GetGCAnalogs()->EndConfiguration(); 698 QWidget::releaseKeyboard();
699
700 input_subsystem->GetGCButtons()->EndConfiguration();
701 input_subsystem->GetGCAnalogs()->EndConfiguration();
658 702
659 if (!abort) { 703 if (!abort) {
660 (*input_setter)(params); 704 (*input_setter)(params);
661 } 705 }
662 706
663 UpdateButtonLabels(); 707 UpdateUI();
664 input_setter = std::nullopt; 708 input_setter = std::nullopt;
665} 709}
666 710
711void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
712 if (!input_setter || !event) {
713 return;
714 }
715
716 if (want_keyboard_mouse) {
717 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
718 false);
719 } else {
720 // We don't want any mouse buttons, so don't stop polling
721 return;
722 }
723
724 SetPollingResult({}, true);
725}
726
667void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { 727void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
668 if (!input_setter || !event) { 728 if (!input_setter || !event) {
669 return; 729 return;
670 } 730 }
671 731
672 if (event->key() != Qt::Key_Escape) { 732 if (event->key() != Qt::Key_Escape) {
673 if (want_keyboard_keys) { 733 if (want_keyboard_mouse) {
674 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, 734 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
675 false); 735 false);
676 } else { 736 } else {
@@ -678,5 +738,108 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
678 return; 738 return;
679 } 739 }
680 } 740 }
741
681 SetPollingResult({}, true); 742 SetPollingResult({}, true);
682} 743}
744
745void ConfigureInputPlayer::UpdateControllerIcon() {
746 // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its
747 // "nonstandard" to use an image through the icon support)
748 const QString stylesheet = [this] {
749 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
750 case Settings::ControllerType::ProController:
751 return QStringLiteral("image: url(:/controller/pro_controller%0)");
752 case Settings::ControllerType::DualJoyconDetached:
753 return QStringLiteral("image: url(:/controller/dual_joycon%0)");
754 case Settings::ControllerType::LeftJoycon:
755 return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)");
756 case Settings::ControllerType::RightJoycon:
757 return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)");
758 case Settings::ControllerType::Handheld:
759 return QStringLiteral("image: url(:/controller/handheld%0)");
760 default:
761 return QString{};
762 }
763 }();
764
765 const QString theme = [this] {
766 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
767 return QStringLiteral("_dark");
768 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
769 return QStringLiteral("_midnight");
770 } else {
771 return QString{};
772 }
773 }();
774
775 ui->controllerFrame->setStyleSheet(stylesheet.arg(theme));
776}
777
778void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
779 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
780 if (debug) {
781 layout = Settings::ControllerType::ProController;
782 }
783
784 // List of all the widgets that will be hidden by any of the following layouts that need
785 // "unhidden" after the controller type changes
786 const std::array<QWidget*, 9> layout_show = {
787 ui->buttonShoulderButtonsSLSR,
788 ui->horizontalSpacerShoulderButtonsWidget,
789 ui->horizontalSpacerShoulderButtonsWidget2,
790 ui->buttonShoulderButtonsLeft,
791 ui->buttonMiscButtonsMinusScreenshot,
792 ui->bottomLeft,
793 ui->buttonShoulderButtonsRight,
794 ui->buttonMiscButtonsPlusHome,
795 ui->bottomRight,
796 };
797
798 for (auto* widget : layout_show) {
799 widget->show();
800 }
801
802 std::vector<QWidget*> layout_hidden;
803 switch (layout) {
804 case Settings::ControllerType::ProController:
805 case Settings::ControllerType::DualJoyconDetached:
806 case Settings::ControllerType::Handheld:
807 layout_hidden = {
808 ui->buttonShoulderButtonsSLSR,
809 ui->horizontalSpacerShoulderButtonsWidget2,
810 };
811 break;
812 case Settings::ControllerType::LeftJoycon:
813 layout_hidden = {
814 ui->horizontalSpacerShoulderButtonsWidget2,
815 ui->buttonShoulderButtonsRight,
816 ui->buttonMiscButtonsPlusHome,
817 ui->bottomRight,
818 };
819 break;
820 case Settings::ControllerType::RightJoycon:
821 layout_hidden = {
822 ui->horizontalSpacerShoulderButtonsWidget,
823 ui->buttonShoulderButtonsLeft,
824 ui->buttonMiscButtonsMinusScreenshot,
825 ui->bottomLeft,
826 };
827 break;
828 }
829
830 for (auto* widget : layout_hidden) {
831 widget->hide();
832 }
833}
834
835void ConfigureInputPlayer::showEvent(QShowEvent* event) {
836 if (bottom_row == nullptr) {
837 return;
838 }
839 QWidget::showEvent(event);
840 ui->main->addWidget(bottom_row);
841}
842
843void ConfigureInputPlayer::ConnectPlayer(bool connected) {
844 ui->groupConnectedController->setChecked(connected);
845}
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 @@
10#include <optional> 10#include <optional>
11#include <string> 11#include <string>
12 12
13#include <QDialog> 13#include <QWidget>
14 14
15#include "common/param_package.h" 15#include "common/param_package.h"
16#include "core/settings.h" 16#include "core/settings.h"
17#include "ui_configure_input.h" 17#include "ui_configure_input.h"
18 18
19class QCheckBox;
19class QKeyEvent; 20class QKeyEvent;
21class QLabel;
20class QPushButton; 22class QPushButton;
23class QSlider;
24class QSpinBox;
21class QString; 25class QString;
22class QTimer; 26class QTimer;
27class QWidget;
28
29namespace InputCommon {
30class InputSubsystem;
31}
23 32
24namespace InputCommon::Polling { 33namespace InputCommon::Polling {
25class DevicePoller; 34class DevicePoller;
@@ -30,77 +39,116 @@ namespace Ui {
30class ConfigureInputPlayer; 39class ConfigureInputPlayer;
31} 40}
32 41
33class ConfigureInputPlayer : public QDialog { 42class ConfigureInputPlayer : public QWidget {
34 Q_OBJECT 43 Q_OBJECT
35 44
36public: 45public:
37 explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug = false); 46 explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row,
47 InputCommon::InputSubsystem* input_subsystem_,
48 bool debug = false);
38 ~ConfigureInputPlayer() override; 49 ~ConfigureInputPlayer() override;
39 50
40 /// Save all button configurations to settings file 51 /// Save all button configurations to settings file.
41 void ApplyConfiguration(); 52 void ApplyConfiguration();
42 53
54 /// Update the input devices combobox.
55 void UpdateInputDevices();
56
57 /// Restore all buttons to their default values.
58 void RestoreDefaults();
59
60 /// Clear all input configuration.
61 void ClearAll();
62
63 /// Set the connection state checkbox (used to sync state).
64 void ConnectPlayer(bool connected);
65
66signals:
67 /// Emitted when this controller is connected by the user.
68 void Connected(bool connected);
69 /// Emitted when the Handheld mode is selected (undocked with dual joycons attached).
70 void HandheldStateChanged(bool is_handheld);
71 /// Emitted when the input devices combobox is being refreshed.
72 void RefreshInputDevices();
73
74protected:
75 void showEvent(QShowEvent* event) override;
76
43private: 77private:
44 void changeEvent(QEvent* event) override; 78 void changeEvent(QEvent* event) override;
45 void RetranslateUI(); 79 void RetranslateUI();
46 80
47 void OnControllerButtonClick(int i);
48
49 /// Load configuration settings. 81 /// Load configuration settings.
50 void LoadConfiguration(); 82 void LoadConfiguration();
51 /// Restore all buttons to their default values.
52 void RestoreDefaults();
53 /// Clear all input configuration
54 void ClearAll();
55
56 /// Update UI to reflect current configuration.
57 void UpdateButtonLabels();
58 83
59 /// Called when the button was pressed. 84 /// Called when the button was pressed.
60 void HandleClick(QPushButton* button, 85 void HandleClick(QPushButton* button,
61 std::function<void(const Common::ParamPackage&)> new_input_setter, 86 std::function<void(const Common::ParamPackage&)> new_input_setter,
62 InputCommon::Polling::DeviceType type); 87 InputCommon::Polling::DeviceType type);
63 88
64 /// Finish polling and configure input using the input_setter 89 /// Finish polling and configure input using the input_setter.
65 void SetPollingResult(const Common::ParamPackage& params, bool abort); 90 void SetPollingResult(const Common::ParamPackage& params, bool abort);
66 91
92 /// Handle mouse button press events.
93 void mousePressEvent(QMouseEvent* event) override;
94
67 /// Handle key press events. 95 /// Handle key press events.
68 void keyPressEvent(QKeyEvent* event) override; 96 void keyPressEvent(QKeyEvent* event) override;
69 97
98 /// Update UI to reflect current configuration.
99 void UpdateUI();
100
101 /// Update the controller selection combobox
102 void UpdateControllerCombobox();
103
104 /// Update the current controller icon.
105 void UpdateControllerIcon();
106
107 /// Hides and disables controller settings based on the current controller type.
108 void UpdateControllerAvailableButtons();
109
110 /// Gets the default controller mapping for this device and auto configures the input to match.
111 void UpdateMappingWithDefaults();
112
70 std::unique_ptr<Ui::ConfigureInputPlayer> ui; 113 std::unique_ptr<Ui::ConfigureInputPlayer> ui;
71 114
72 std::size_t player_index; 115 std::size_t player_index;
73 bool debug; 116 bool debug;
74 117
118 InputCommon::InputSubsystem* input_subsystem;
119
75 std::unique_ptr<QTimer> timeout_timer; 120 std::unique_ptr<QTimer> timeout_timer;
76 std::unique_ptr<QTimer> poll_timer; 121 std::unique_ptr<QTimer> poll_timer;
77 122
123 static constexpr int PLAYER_COUNT = 8;
124 std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox;
125
78 /// This will be the the setting function when an input is awaiting configuration. 126 /// This will be the the setting function when an input is awaiting configuration.
79 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; 127 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
80 128
81 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; 129 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
82 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; 130 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
83 131
84 static constexpr int ANALOG_SUB_BUTTONS_NUM = 5; 132 static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;
85 133
86 /// Each button input is represented by a QPushButton. 134 /// Each button input is represented by a QPushButton.
87 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map; 135 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
136 /// Extra buttons for the modifiers.
137 Common::ParamPackage lstick_mod;
138 Common::ParamPackage rstick_mod;
88 139
89 std::vector<QWidget*> debug_hidden; 140 /// A group of four QPushButtons represent one analog input. The buttons each represent up,
90 std::vector<QWidget*> layout_hidden; 141 /// down, left, right, respectively.
91
92 /// A group of five QPushButtons represent one analog input. The buttons each represent up,
93 /// down, left, right, and modifier, respectively.
94 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs> 142 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
95 analog_map_buttons; 143 analog_map_buttons;
96 144
97 /// Analog inputs are also represented each with a single button, used to configure with an 145 std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label;
98 /// actual analog stick 146 std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_slider;
99 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; 147 std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_groupbox;
100 std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> 148 std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_label;
101 analog_map_deadzone_and_modifier_slider; 149 std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_slider;
102 std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> 150 std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_groupbox;
103 analog_map_deadzone_and_modifier_slider_label; 151 std::array<QSpinBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_spinbox;
104 152
105 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; 153 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
106 154
@@ -108,8 +156,14 @@ private:
108 156
109 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, 157 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
110 /// keyboard events are ignored. 158 /// keyboard events are ignored.
111 bool want_keyboard_keys = false; 159 bool want_keyboard_mouse = false;
160
161 /// List of physical devices users can map with. If a SDL backed device is selected, then you
162 /// can usue this device to get a default mapping.
163 std::vector<Common::ParamPackage> input_devices;
112 164
113 std::array<QPushButton*, 4> controller_color_buttons; 165 /// Bottom row is where console wide settings are held, and its "owned" by the parent
114 std::array<QColor, 4> controller_colors; 166 /// ConfigureInput widget. On show, add this widget to the main layout. This will change the
167 /// parent of the widget to this widget (but thats fine).
168 QWidget* bottom_row;
115}; 169};
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 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureInputPlayer</class> 3 <class>ConfigureInputPlayer</class>
4 <widget class="QDialog" name="ConfigureInputPlayer"> 4 <widget class="QWidget" name="ConfigureInputPlayer">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>408</width> 9 <width>780</width>
10 <height>731</height> 10 <height>487</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
14 <string>Configure Input</string> 14 <string>Configure Input</string>
15 </property> 15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5"> 16 <layout class="QHBoxLayout" name="horizontalLayout_2">
17 <property name="spacing">
18 <number>0</number>
19 </property>
20 <property name="leftMargin">
21 <number>0</number>
22 </property>
23 <property name="topMargin">
24 <number>0</number>
25 </property>
26 <property name="rightMargin">
27 <number>0</number>
28 </property>
29 <property name="bottomMargin">
30 <number>0</number>
31 </property>
17 <item> 32 <item>
18 <layout class="QGridLayout" name="buttons"> 33 <layout class="QVBoxLayout" name="main">
19 <item row="1" column="1"> 34 <property name="spacing">
20 <widget class="QGroupBox" name="RStick"> 35 <number>0</number>
21 <property name="title"> 36 </property>
22 <string>Right Stick</string> 37 <property name="leftMargin">
23 </property> 38 <number>0</number>
24 <property name="alignment"> 39 </property>
25 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> 40 <property name="topMargin">
26 </property> 41 <number>0</number>
27 <property name="flat"> 42 </property>
28 <bool>false</bool> 43 <property name="rightMargin">
44 <number>0</number>
45 </property>
46 <property name="bottomMargin">
47 <number>0</number>
48 </property>
49 <item>
50 <layout class="QHBoxLayout" name="top" stretch="0,1,2">
51 <property name="spacing">
52 <number>3</number>
29 </property> 53 </property>
30 <property name="checkable"> 54 <property name="topMargin">
31 <bool>false</bool> 55 <number>0</number>
32 </property> 56 </property>
33 <layout class="QGridLayout" name="gridLayout_5"> 57 <item>
34 <item row="1" column="1"> 58 <widget class="QGroupBox" name="groupConnectedController">
35 <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout"> 59 <property name="layoutDirection">
60 <enum>Qt::LeftToRight</enum>
61 </property>
62 <property name="title">
63 <string>Connect Controller</string>
64 </property>
65 <property name="flat">
66 <bool>false</bool>
67 </property>
68 <property name="checkable">
69 <bool>true</bool>
70 </property>
71 <layout class="QHBoxLayout" name="horizontalLayout_3">
72 <property name="leftMargin">
73 <number>5</number>
74 </property>
75 <property name="topMargin">
76 <number>5</number>
77 </property>
78 <property name="rightMargin">
79 <number>5</number>
80 </property>
81 <property name="bottomMargin">
82 <number>5</number>
83 </property>
36 <item> 84 <item>
37 <layout class="QHBoxLayout" name="buttonRStickDownHorizontalLayout"> 85 <widget class="QComboBox" name="comboControllerType">
38 <item> 86 <item>
39 <widget class="QLabel" name="labelRStickDown"> 87 <property name="text">
40 <property name="text"> 88 <string>Pro Controller</string>
41 <string>Down:</string> 89 </property>
42 </property>
43 </widget>
44 </item> 90 </item>
45 </layout>
46 </item>
47 <item>
48 <widget class="QPushButton" name="buttonRStickDown">
49 <property name="text">
50 <string/>
51 </property>
52 </widget>
53 </item>
54 </layout>
55 </item>
56 <item row="0" column="1">
57 <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
58 <item>
59 <layout class="QHBoxLayout" name="buttonRStickRightHorizontalLayout">
60 <item> 91 <item>
61 <widget class="QLabel" name="labelRStickRight"> 92 <property name="text">
62 <property name="text"> 93 <string>Dual Joycons</string>
63 <string>Right:</string> 94 </property>
64 </property>
65 </widget>
66 </item> 95 </item>
67 </layout>
68 </item>
69 <item>
70 <widget class="QPushButton" name="buttonRStickRight">
71 <property name="text">
72 <string/>
73 </property>
74 </widget>
75 </item>
76 </layout>
77 </item>
78 <item row="3" column="0" colspan="2">
79 <widget class="QPushButton" name="buttonRStickAnalog">
80 <property name="text">
81 <string>Set Analog Stick</string>
82 </property>
83 </widget>
84 </item>
85 <item row="0" column="0">
86 <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
87 <item>
88 <layout class="QHBoxLayout" name="buttonRStickLeftHorizontalLayout">
89 <item> 96 <item>
90 <widget class="QLabel" name="labelRStickLeft"> 97 <property name="text">
91 <property name="text"> 98 <string>Left Joycon</string>
92 <string>Left:</string> 99 </property>
93 </property>
94 </widget>
95 </item> 100 </item>
96 </layout>
97 </item>
98 <item>
99 <widget class="QPushButton" name="buttonRStickLeft">
100 <property name="text">
101 <string/>
102 </property>
103 </widget>
104 </item>
105 </layout>
106 </item>
107 <item row="1" column="0">
108 <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
109 <item>
110 <layout class="QHBoxLayout" name="buttonRStickUpHorizontalLayout">
111 <item> 101 <item>
112 <widget class="QLabel" name="labelRStickUp"> 102 <property name="text">
113 <property name="text"> 103 <string>Right Joycon</string>
114 <string>Up:</string> 104 </property>
115 </property>
116 </widget>
117 </item> 105 </item>
118 </layout>
119 </item>
120 <item>
121 <widget class="QPushButton" name="buttonRStickUp">
122 <property name="text">
123 <string/>
124 </property>
125 </widget>
126 </item>
127 </layout>
128 </item>
129 <item row="2" column="0">
130 <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
131 <item>
132 <layout class="QHBoxLayout" name="buttonRStickPressedHorizontalLayout">
133 <item> 106 <item>
134 <widget class="QLabel" name="labelRStickPressed"> 107 <property name="text">
135 <property name="text"> 108 <string>Handheld</string>
136 <string>Pressed:</string> 109 </property>
137 </property>
138 </widget>
139 </item> 110 </item>
140 </layout>
141 </item>
142 <item>
143 <widget class="QPushButton" name="buttonRStick">
144 <property name="text">
145 <string/>
146 </property>
147 </widget> 111 </widget>
148 </item> 112 </item>
149 </layout> 113 </layout>
150 </item> 114 </widget>
151 <item row="2" column="1"> 115 </item>
152 <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout"> 116 <item>
117 <widget class="QGroupBox" name="devicesGroup">
118 <property name="title">
119 <string>Input Device</string>
120 </property>
121 <layout class="QHBoxLayout" name="horizontalLayout_5">
122 <property name="spacing">
123 <number>3</number>
124 </property>
125 <property name="leftMargin">
126 <number>5</number>
127 </property>
128 <property name="topMargin">
129 <number>5</number>
130 </property>
131 <property name="rightMargin">
132 <number>5</number>
133 </property>
134 <property name="bottomMargin">
135 <number>5</number>
136 </property>
153 <item> 137 <item>
154 <layout class="QHBoxLayout" name="buttonRStickModHorizontalLayout"> 138 <widget class="QComboBox" name="comboDevices">
155 <item> 139 <item>
156 <widget class="QLabel" name="labelRStickMod"> 140 <property name="text">
157 <property name="text"> 141 <string>Any</string>
158 <string>Modifier:</string> 142 </property>
159 </property>
160 </widget>
161 </item> 143 </item>
162 </layout>
163 </item>
164 <item>
165 <widget class="QPushButton" name="buttonRStickMod">
166 <property name="text">
167 <string/>
168 </property>
169 </widget>
170 </item>
171 </layout>
172 </item>
173 <item row="4" column="0" colspan="2">
174 <layout class="QVBoxLayout" name="sliderRStickDeadzoneAndModifierVerticalLayout">
175 <item>
176 <layout class="QHBoxLayout" name="sliderRStickDeadzoneAndModifierHorizontalLayout">
177 <item> 144 <item>
178 <widget class="QLabel" name="labelRStickDeadzoneAndModifier"> 145 <property name="text">
179 <property name="text"> 146 <string>Keyboard/Mouse</string>
180 <string>Deadzone: 0</string> 147 </property>
181 </property>
182 <property name="alignment">
183 <enum>Qt::AlignHCenter</enum>
184 </property>
185 </widget>
186 </item> 148 </item>
187 </layout> 149 </widget>
188 </item> 150 </item>
189 <item> 151 <item>
190 <widget class="QSlider" name="sliderRStickDeadzoneAndModifier"> 152 <widget class="QPushButton" name="buttonRefreshDevices">
191 <property name="orientation"> 153 <property name="minimumSize">
192 <enum>Qt::Horizontal</enum> 154 <size>
155 <width>24</width>
156 <height>22</height>
157 </size>
158 </property>
159 <property name="maximumSize">
160 <size>
161 <width>24</width>
162 <height>22</height>
163 </size>
164 </property>
165 <property name="styleSheet">
166 <string notr="true"/>
193 </property> 167 </property>
194 </widget> 168 </widget>
195 </item> 169 </item>
196 </layout> 170 </layout>
197 </item> 171 </widget>
198 <item row="5" column="0"> 172 </item>
199 <spacer name="RStick_verticalSpacer"> 173 <item>
200 <property name="orientation"> 174 <widget class="QGroupBox" name="profilesGroup">
201 <enum>Qt::Vertical</enum> 175 <property name="minimumSize">
176 <size>
177 <width>0</width>
178 <height>0</height>
179 </size>
180 </property>
181 <property name="title">
182 <string>Profile</string>
183 </property>
184 <layout class="QHBoxLayout" name="horizontalLayout_4" stretch="2,0,0,0">
185 <property name="spacing">
186 <number>3</number>
202 </property> 187 </property>
203 <property name="sizeHint" stdset="0"> 188 <property name="leftMargin">
204 <size> 189 <number>5</number>
205 <width>0</width> 190 </property>
206 <height>0</height> 191 <property name="topMargin">
207 </size> 192 <number>5</number>
193 </property>
194 <property name="rightMargin">
195 <number>5</number>
196 </property>
197 <property name="bottomMargin">
198 <number>5</number>
208 </property> 199 </property>
209 </spacer>
210 </item>
211 </layout>
212 </widget>
213 </item>
214 <item row="0" column="1">
215 <widget class="QGroupBox" name="Dpad">
216 <property name="title">
217 <string>Directional Pad</string>
218 </property>
219 <property name="flat">
220 <bool>false</bool>
221 </property>
222 <property name="checkable">
223 <bool>false</bool>
224 </property>
225 <layout class="QGridLayout" name="gridLayout_2">
226 <item row="1" column="0">
227 <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
228 <item> 200 <item>
229 <layout class="QHBoxLayout" name="buttonDpadUpHorizontalLayout"> 201 <widget class="QComboBox" name="comboProfiles"/>
230 <item>
231 <widget class="QLabel" name="labelDpadUp">
232 <property name="text">
233 <string>Up:</string>
234 </property>
235 </widget>
236 </item>
237 </layout>
238 </item> 202 </item>
239 <item> 203 <item>
240 <widget class="QPushButton" name="buttonDpadUp"> 204 <widget class="QPushButton" name="buttonProfilesSave">
241 <property name="text"> 205 <property name="maximumSize">
242 <string/> 206 <size>
207 <width>55</width>
208 <height>16777215</height>
209 </size>
210 </property>
211 <property name="styleSheet">
212 <string notr="true">min-width: 55px;</string>
243 </property> 213 </property>
244 </widget>
245 </item>
246 </layout>
247 </item>
248 <item row="1" column="1">
249 <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
250 <item>
251 <layout class="QHBoxLayout" name="buttonDpadDownHorizontalLayout">
252 <item>
253 <widget class="QLabel" name="labelDpadDown">
254 <property name="text">
255 <string>Down:</string>
256 </property>
257 </widget>
258 </item>
259 </layout>
260 </item>
261 <item>
262 <widget class="QPushButton" name="buttonDpadDown">
263 <property name="text"> 214 <property name="text">
264 <string/> 215 <string>Save</string>
265 </property> 216 </property>
266 </widget> 217 </widget>
267 </item> 218 </item>
268 </layout>
269 </item>
270 <item row="0" column="0">
271 <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
272 <item>
273 <layout class="QHBoxLayout" name="buttonDpadLeftHorizontalLayout">
274 <item>
275 <widget class="QLabel" name="labelDpadLeft">
276 <property name="minimumSize">
277 <size>
278 <width>80</width>
279 <height>0</height>
280 </size>
281 </property>
282 <property name="text">
283 <string>Left:</string>
284 </property>
285 </widget>
286 </item>
287 </layout>
288 </item>
289 <item> 219 <item>
290 <widget class="QPushButton" name="buttonDpadLeft"> 220 <widget class="QPushButton" name="buttonProfilesNew">
221 <property name="maximumSize">
222 <size>
223 <width>55</width>
224 <height>16777215</height>
225 </size>
226 </property>
227 <property name="styleSheet">
228 <string notr="true">min-width: 55px;</string>
229 </property>
291 <property name="text"> 230 <property name="text">
292 <string/> 231 <string>New</string>
293 </property> 232 </property>
294 </widget> 233 </widget>
295 </item> 234 </item>
296 </layout>
297 </item>
298 <item row="0" column="1">
299 <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
300 <item>
301 <layout class="QHBoxLayout" name="buttonDpadRightHorizontalLayout">
302 <item>
303 <widget class="QLabel" name="labelDpadRight">
304 <property name="minimumSize">
305 <size>
306 <width>80</width>
307 <height>0</height>
308 </size>
309 </property>
310 <property name="text">
311 <string>Right:</string>
312 </property>
313 </widget>
314 </item>
315 </layout>
316 </item>
317 <item> 235 <item>
318 <widget class="QPushButton" name="buttonDpadRight"> 236 <widget class="QPushButton" name="buttonProfilesDelete">
237 <property name="maximumSize">
238 <size>
239 <width>55</width>
240 <height>16777215</height>
241 </size>
242 </property>
243 <property name="styleSheet">
244 <string notr="true">min-width: 55px;</string>
245 </property>
319 <property name="text"> 246 <property name="text">
320 <string/> 247 <string>Delete</string>
321 </property> 248 </property>
322 </widget> 249 </widget>
323 </item> 250 </item>
324 </layout> 251 </layout>
325 </item> 252 </widget>
326 </layout> 253 </item>
327 </widget> 254 </layout>
328 </item> 255 </item>
329 <item row="0" column="0"> 256 <item>
330 <widget class="QGroupBox" name="faceButtons"> 257 <widget class="QFrame" name="bottom">
331 <property name="title"> 258 <property name="sizePolicy">
332 <string>Face Buttons</string> 259 <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
333 </property> 260 <horstretch>0</horstretch>
334 <property name="flat"> 261 <verstretch>0</verstretch>
335 <bool>false</bool> 262 </sizepolicy>
336 </property>
337 <property name="checkable">
338 <bool>false</bool>
339 </property> 263 </property>
340 <layout class="QGridLayout" name="gridLayout"> 264 <layout class="QHBoxLayout" name="_2">
341 <item row="0" column="0"> 265 <property name="sizeConstraint">
342 <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout"> 266 <enum>QLayout::SetMinimumSize</enum>
343 <item> 267 </property>
344 <layout class="QHBoxLayout" name="buttonFaceButtonsAHorizontalLayout"> 268 <property name="leftMargin">
345 <item> 269 <number>0</number>
346 <widget class="QLabel" name="labelA"> 270 </property>
347 <property name="minimumSize"> 271 <property name="topMargin">
348 <size> 272 <number>0</number>
349 <width>80</width> 273 </property>
350 <height>0</height> 274 <property name="rightMargin">
351 </size> 275 <number>0</number>
276 </property>
277 <property name="bottomMargin">
278 <number>0</number>
279 </property>
280 <item>
281 <widget class="QWidget" name="bottomLeft" native="true">
282 <layout class="QVBoxLayout" name="bottomLeftLayout" stretch="0,0,0,0">
283 <property name="spacing">
284 <number>0</number>
285 </property>
286 <property name="sizeConstraint">
287 <enum>QLayout::SetDefaultConstraint</enum>
288 </property>
289 <property name="leftMargin">
290 <number>0</number>
291 </property>
292 <property name="topMargin">
293 <number>0</number>
294 </property>
295 <property name="rightMargin">
296 <number>0</number>
297 </property>
298 <property name="bottomMargin">
299 <number>0</number>
300 </property>
301 <item>
302 <widget class="QGroupBox" name="LStick">
303 <property name="sizePolicy">
304 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
305 <horstretch>0</horstretch>
306 <verstretch>0</verstretch>
307 </sizepolicy>
308 </property>
309 <property name="title">
310 <string>Left Stick</string>
311 </property>
312 <property name="alignment">
313 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
314 </property>
315 <layout class="QVBoxLayout" name="verticalLayout_3">
316 <property name="spacing">
317 <number>0</number>
352 </property> 318 </property>
353 <property name="text"> 319 <property name="sizeConstraint">
354 <string>A:</string> 320 <enum>QLayout::SetDefaultConstraint</enum>
355 </property> 321 </property>
356 </widget> 322 <property name="leftMargin">
357 </item> 323 <number>3</number>
358 </layout>
359 </item>
360 <item>
361 <widget class="QPushButton" name="buttonA">
362 <property name="text">
363 <string/>
364 </property>
365 </widget>
366 </item>
367 </layout>
368 </item>
369 <item row="0" column="1">
370 <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
371 <item>
372 <layout class="QHBoxLayout" name="buttonFaceButtonsBHorizontalLayout">
373 <item>
374 <widget class="QLabel" name="labelB">
375 <property name="minimumSize">
376 <size>
377 <width>80</width>
378 <height>0</height>
379 </size>
380 </property> 324 </property>
381 <property name="text"> 325 <property name="topMargin">
382 <string>B:</string> 326 <number>0</number>
383 </property> 327 </property>
384 </widget> 328 <property name="rightMargin">
385 </item> 329 <number>3</number>
386 </layout>
387 </item>
388 <item>
389 <widget class="QPushButton" name="buttonB">
390 <property name="text">
391 <string/>
392 </property>
393 </widget>
394 </item>
395 </layout>
396 </item>
397 <item row="1" column="0">
398 <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
399 <item>
400 <layout class="QHBoxLayout" name="buttonFaceButtonsXHorizontalLayout">
401 <item>
402 <widget class="QLabel" name="labelX">
403 <property name="text">
404 <string>X:</string>
405 </property> 330 </property>
406 </widget> 331 <property name="bottomMargin">
407 </item> 332 <number>0</number>
408 </layout>
409 </item>
410 <item>
411 <widget class="QPushButton" name="buttonX">
412 <property name="text">
413 <string/>
414 </property>
415 </widget>
416 </item>
417 </layout>
418 </item>
419 <item row="1" column="1">
420 <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
421 <item>
422 <layout class="QHBoxLayout" name="buttonFaceButtonsYHorizontalLayout">
423 <item>
424 <widget class="QLabel" name="labelY">
425 <property name="text">
426 <string>Y:</string>
427 </property> 333 </property>
428 </widget> 334 <item>
429 </item> 335 <widget class="QWidget" name="buttonLStickUpWidget" native="true">
430 </layout> 336 <layout class="QHBoxLayout" name="horizontalLayout_20">
431 </item> 337 <property name="spacing">
432 <item> 338 <number>0</number>
433 <widget class="QPushButton" name="buttonY"> 339 </property>
434 <property name="text"> 340 <property name="leftMargin">
435 <string/> 341 <number>0</number>
436 </property> 342 </property>
437 </widget> 343 <property name="topMargin">
438 </item> 344 <number>0</number>
439 </layout> 345 </property>
440 </item> 346 <property name="rightMargin">
441 </layout> 347 <number>0</number>
442 </widget> 348 </property>
443 </item> 349 <property name="bottomMargin">
444 <item row="5" column="0" colspan="2"> 350 <number>0</number>
445 <widget class="QGroupBox" name="controller_color"> 351 </property>
446 <property name="title"> 352 <item>
447 <string>Controller Color</string> 353 <spacer name="horizontalSpacerLStickUpLeft">
448 </property> 354 <property name="orientation">
449 <layout class="QGridLayout" name="gridLayout_10" columnstretch="0,0,0,0,0,0,0"> 355 <enum>Qt::Horizontal</enum>
450 <item row="0" column="0"> 356 </property>
451 <spacer name="horizontalSpacer_2"> 357 <property name="sizeHint" stdset="0">
452 <property name="orientation"> 358 <size>
453 <enum>Qt::Horizontal</enum> 359 <width>20</width>
454 </property> 360 <height>20</height>
455 <property name="sizeHint" stdset="0"> 361 </size>
456 <size> 362 </property>
457 <width>40</width> 363 </spacer>
458 <height>20</height> 364 </item>
459 </size> 365 <item alignment="Qt::AlignHCenter">
460 </property> 366 <widget class="QGroupBox" name="buttonLStickUpGroup">
461 </spacer> 367 <property name="title">
462 </item> 368 <string>Up</string>
463 <item row="0" column="1"> 369 </property>
464 <widget class="QLabel" name="left_body_label"> 370 <property name="alignment">
465 <property name="text"> 371 <set>Qt::AlignCenter</set>
466 <string>Left Body</string> 372 </property>
467 </property> 373 <property name="flat">
468 </widget> 374 <bool>false</bool>
469 </item> 375 </property>
470 <item row="0" column="6"> 376 <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout">
471 <spacer name="horizontalSpacer_3"> 377 <property name="spacing">
472 <property name="orientation"> 378 <number>3</number>
473 <enum>Qt::Horizontal</enum> 379 </property>
474 </property> 380 <property name="leftMargin">
475 <property name="sizeHint" stdset="0"> 381 <number>3</number>
476 <size> 382 </property>
477 <width>40</width> 383 <property name="topMargin">
478 <height>20</height> 384 <number>3</number>
479 </size> 385 </property>
480 </property> 386 <property name="rightMargin">
481 </spacer> 387 <number>3</number>
482 </item> 388 </property>
483 <item row="1" column="1"> 389 <property name="bottomMargin">
484 <widget class="QLabel" name="left_buttons_label"> 390 <number>3</number>
485 <property name="minimumSize"> 391 </property>
486 <size> 392 <item>
487 <width>90</width> 393 <widget class="QPushButton" name="buttonLStickUp">
488 <height>0</height> 394 <property name="minimumSize">
489 </size> 395 <size>
490 </property> 396 <width>57</width>
491 <property name="text"> 397 <height>0</height>
492 <string>Left Buttons</string> 398 </size>
493 </property> 399 </property>
494 </widget> 400 <property name="maximumSize">
495 </item> 401 <size>
496 <item row="1" column="5"> 402 <width>55</width>
497 <widget class="QPushButton" name="right_buttons_button"> 403 <height>16777215</height>
498 <property name="sizePolicy"> 404 </size>
499 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 405 </property>
500 <horstretch>0</horstretch> 406 <property name="styleSheet">
501 <verstretch>0</verstretch> 407 <string notr="true">min-width: 55px;</string>
502 </sizepolicy> 408 </property>
503 </property> 409 <property name="text">
504 <property name="minimumSize"> 410 <string>Up</string>
505 <size> 411 </property>
506 <width>32</width> 412 </widget>
507 <height>0</height> 413 </item>
508 </size> 414 </layout>
509 </property> 415 </widget>
510 <property name="maximumSize"> 416 </item>
511 <size> 417 <item>
512 <width>40</width> 418 <spacer name="horizontalSpacerLStickUpRight">
513 <height>16777215</height> 419 <property name="orientation">
514 </size> 420 <enum>Qt::Horizontal</enum>
515 </property> 421 </property>
516 <property name="text"> 422 <property name="sizeHint" stdset="0">
517 <string/> 423 <size>
518 </property> 424 <width>20</width>
519 </widget> 425 <height>20</height>
520 </item> 426 </size>
521 <item row="0" column="4"> 427 </property>
522 <widget class="QLabel" name="right_body_label"> 428 </spacer>
523 <property name="text"> 429 </item>
524 <string>Right Body</string> 430 </layout>
525 </property> 431 </widget>
526 </widget> 432 </item>
527 </item> 433 <item>
528 <item row="1" column="4"> 434 <layout class="QHBoxLayout" name="buttonLStickLeftRightHorizontaLayout">
529 <widget class="QLabel" name="right_buttons_label"> 435 <property name="spacing">
530 <property name="minimumSize"> 436 <number>3</number>
531 <size> 437 </property>
532 <width>90</width> 438 <item alignment="Qt::AlignHCenter">
533 <height>0</height> 439 <widget class="QGroupBox" name="buttonLStickLeftGroup">
534 </size> 440 <property name="title">
535 </property> 441 <string>Left</string>
536 <property name="text"> 442 </property>
537 <string>Right Buttons</string> 443 <property name="alignment">
538 </property> 444 <set>Qt::AlignCenter</set>
539 </widget> 445 </property>
540 </item> 446 <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
541 <item row="1" column="2"> 447 <property name="spacing">
542 <widget class="QPushButton" name="left_buttons_button"> 448 <number>3</number>
543 <property name="sizePolicy"> 449 </property>
544 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 450 <property name="leftMargin">
545 <horstretch>0</horstretch> 451 <number>3</number>
546 <verstretch>0</verstretch> 452 </property>
547 </sizepolicy> 453 <property name="topMargin">
548 </property> 454 <number>3</number>
549 <property name="minimumSize"> 455 </property>
550 <size> 456 <property name="rightMargin">
551 <width>32</width> 457 <number>3</number>
552 <height>0</height> 458 </property>
553 </size> 459 <property name="bottomMargin">
554 </property> 460 <number>3</number>
555 <property name="maximumSize"> 461 </property>
556 <size> 462 <item>
557 <width>40</width> 463 <widget class="QPushButton" name="buttonLStickLeft">
558 <height>16777215</height> 464 <property name="minimumSize">
559 </size> 465 <size>
560 </property> 466 <width>57</width>
561 <property name="text"> 467 <height>0</height>
562 <string/> 468 </size>
563 </property> 469 </property>
564 </widget> 470 <property name="maximumSize">
565 </item> 471 <size>
566 <item row="0" column="2"> 472 <width>55</width>
567 <widget class="QPushButton" name="left_body_button"> 473 <height>16777215</height>
568 <property name="sizePolicy"> 474 </size>
569 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 475 </property>
570 <horstretch>0</horstretch> 476 <property name="styleSheet">
571 <verstretch>0</verstretch> 477 <string notr="true">min-width: 55px;</string>
572 </sizepolicy> 478 </property>
573 </property> 479 <property name="text">
574 <property name="minimumSize"> 480 <string>Left</string>
575 <size> 481 </property>
576 <width>32</width> 482 </widget>
577 <height>0</height> 483 </item>
578 </size> 484 </layout>
579 </property> 485 </widget>
580 <property name="maximumSize"> 486 </item>
581 <size> 487 <item alignment="Qt::AlignHCenter">
582 <width>40</width> 488 <widget class="QGroupBox" name="buttonLStickRightGroup">
583 <height>16777215</height> 489 <property name="title">
584 </size> 490 <string>Right</string>
585 </property> 491 </property>
586 <property name="text"> 492 <property name="alignment">
587 <string/> 493 <set>Qt::AlignCenter</set>
588 </property> 494 </property>
589 </widget> 495 <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
590 </item> 496 <property name="spacing">
591 <item row="0" column="5"> 497 <number>3</number>
592 <widget class="QPushButton" name="right_body_button"> 498 </property>
593 <property name="sizePolicy"> 499 <property name="leftMargin">
594 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 500 <number>3</number>
595 <horstretch>0</horstretch> 501 </property>
596 <verstretch>0</verstretch> 502 <property name="topMargin">
597 </sizepolicy> 503 <number>3</number>
598 </property> 504 </property>
599 <property name="minimumSize"> 505 <property name="rightMargin">
600 <size> 506 <number>3</number>
601 <width>32</width> 507 </property>
602 <height>0</height> 508 <property name="bottomMargin">
603 </size> 509 <number>3</number>
604 </property> 510 </property>
605 <property name="maximumSize"> 511 <item>
606 <size> 512 <widget class="QPushButton" name="buttonLStickRight">
607 <width>40</width> 513 <property name="minimumSize">
608 <height>16777215</height> 514 <size>
609 </size> 515 <width>57</width>
610 </property> 516 <height>0</height>
611 <property name="text"> 517 </size>
612 <string/> 518 </property>
613 </property> 519 <property name="maximumSize">
614 </widget> 520 <size>
615 </item> 521 <width>55</width>
616 <item row="0" column="3"> 522 <height>16777215</height>
617 <spacer name="horizontalSpacer_4"> 523 </size>
618 <property name="orientation"> 524 </property>
619 <enum>Qt::Horizontal</enum> 525 <property name="styleSheet">
620 </property> 526 <string notr="true">min-width: 55px;</string>
621 <property name="sizeType"> 527 </property>
622 <enum>QSizePolicy::Fixed</enum> 528 <property name="text">
623 </property> 529 <string>Right</string>
624 <property name="sizeHint" stdset="0"> 530 </property>
625 <size> 531 </widget>
626 <width>20</width> 532 </item>
627 <height>20</height> 533 </layout>
628 </size> 534 </widget>
629 </property> 535 </item>
630 </spacer> 536 </layout>
631 </item> 537 </item>
632 </layout> 538 <item>
633 </widget> 539 <widget class="QWidget" name="buttonLStickDownWidget" native="true">
634 </item> 540 <layout class="QHBoxLayout" name="horizontalLayout_22">
635 <item row="1" column="0"> 541 <property name="spacing">
636 <widget class="QGroupBox" name="LStick"> 542 <number>0</number>
637 <property name="title"> 543 </property>
638 <string>Left Stick</string> 544 <property name="leftMargin">
639 </property> 545 <number>0</number>
640 <property name="flat"> 546 </property>
641 <bool>false</bool> 547 <property name="topMargin">
642 </property> 548 <number>0</number>
643 <property name="checkable"> 549 </property>
644 <bool>false</bool> 550 <property name="rightMargin">
645 </property> 551 <number>0</number>
646 <layout class="QGridLayout" name="gridLayout_4"> 552 </property>
647 <item row="1" column="1"> 553 <property name="bottomMargin">
648 <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout"> 554 <number>0</number>
649 <item> 555 </property>
650 <layout class="QHBoxLayout" name="buttonLStickUpHorizontalLayout"> 556 <item>
651 <item> 557 <spacer name="horizontalSpacerLStickDownLeft">
652 <widget class="QLabel" name="labelLStickUp"> 558 <property name="orientation">
653 <property name="text"> 559 <enum>Qt::Horizontal</enum>
654 <string>Up:</string> 560 </property>
561 <property name="sizeHint" stdset="0">
562 <size>
563 <width>20</width>
564 <height>20</height>
565 </size>
566 </property>
567 </spacer>
568 </item>
569 <item alignment="Qt::AlignHCenter">
570 <widget class="QGroupBox" name="buttonLStickDownGroup">
571 <property name="title">
572 <string>Down</string>
573 </property>
574 <property name="alignment">
575 <set>Qt::AlignCenter</set>
576 </property>
577 <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
578 <property name="spacing">
579 <number>3</number>
580 </property>
581 <property name="leftMargin">
582 <number>3</number>
583 </property>
584 <property name="topMargin">
585 <number>3</number>
586 </property>
587 <property name="rightMargin">
588 <number>3</number>
589 </property>
590 <property name="bottomMargin">
591 <number>3</number>
592 </property>
593 <item>
594 <widget class="QPushButton" name="buttonLStickDown">
595 <property name="minimumSize">
596 <size>
597 <width>57</width>
598 <height>0</height>
599 </size>
600 </property>
601 <property name="maximumSize">
602 <size>
603 <width>55</width>
604 <height>16777215</height>
605 </size>
606 </property>
607 <property name="styleSheet">
608 <string notr="true">min-width: 55px;</string>
609 </property>
610 <property name="text">
611 <string>Down</string>
612 </property>
613 </widget>
614 </item>
615 </layout>
616 </widget>
617 </item>
618 <item>
619 <spacer name="horizontalSpacerLStickDownRight">
620 <property name="orientation">
621 <enum>Qt::Horizontal</enum>
622 </property>
623 <property name="sizeHint" stdset="0">
624 <size>
625 <width>20</width>
626 <height>20</height>
627 </size>
628 </property>
629 </spacer>
630 </item>
631 </layout>
632 </widget>
633 </item>
634 <item>
635 <layout class="QHBoxLayout" name="buttonLStickPressedModifierHorizontalLayout">
636 <property name="spacing">
637 <number>3</number>
638 </property>
639 <item alignment="Qt::AlignHCenter">
640 <widget class="QGroupBox" name="buttonLStickPressedGroup">
641 <property name="title">
642 <string>Pressed</string>
643 </property>
644 <property name="alignment">
645 <set>Qt::AlignCenter</set>
646 </property>
647 <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0">
648 <property name="spacing">
649 <number>3</number>
650 </property>
651 <property name="leftMargin">
652 <number>3</number>
653 </property>
654 <property name="topMargin">
655 <number>3</number>
656 </property>
657 <property name="rightMargin">
658 <number>3</number>
659 </property>
660 <property name="bottomMargin">
661 <number>3</number>
662 </property>
663 <item>
664 <widget class="QPushButton" name="buttonLStick">
665 <property name="minimumSize">
666 <size>
667 <width>57</width>
668 <height>0</height>
669 </size>
670 </property>
671 <property name="maximumSize">
672 <size>
673 <width>55</width>
674 <height>16777215</height>
675 </size>
676 </property>
677 <property name="styleSheet">
678 <string notr="true">min-width: 55px;</string>
679 </property>
680 <property name="text">
681 <string>Pressed</string>
682 </property>
683 </widget>
684 </item>
685 </layout>
686 </widget>
687 </item>
688 <item alignment="Qt::AlignHCenter">
689 <widget class="QGroupBox" name="buttonLStickModGroup">
690 <property name="title">
691 <string>Modifier</string>
692 </property>
693 <property name="alignment">
694 <set>Qt::AlignCenter</set>
695 </property>
696 <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
697 <property name="spacing">
698 <number>3</number>
699 </property>
700 <property name="leftMargin">
701 <number>3</number>
702 </property>
703 <property name="topMargin">
704 <number>3</number>
705 </property>
706 <property name="rightMargin">
707 <number>3</number>
708 </property>
709 <property name="bottomMargin">
710 <number>3</number>
711 </property>
712 <item>
713 <widget class="QPushButton" name="buttonLStickMod">
714 <property name="minimumSize">
715 <size>
716 <width>57</width>
717 <height>0</height>
718 </size>
719 </property>
720 <property name="maximumSize">
721 <size>
722 <width>55</width>
723 <height>16777215</height>
724 </size>
725 </property>
726 <property name="styleSheet">
727 <string notr="true">min-width: 55px;</string>
728 </property>
729 <property name="text">
730 <string>Modifier</string>
731 </property>
732 </widget>
733 </item>
734 </layout>
735 </widget>
736 </item>
737 <item>
738 <widget class="QGroupBox" name="buttonLStickRangeGroup">
739 <property name="title">
740 <string>Range</string>
741 </property>
742 <layout class="QHBoxLayout" name="buttonLStickRangeGroupHorizontalLayout">
743 <property name="spacing">
744 <number>3</number>
745 </property>
746 <property name="leftMargin">
747 <number>3</number>
748 </property>
749 <property name="topMargin">
750 <number>3</number>
751 </property>
752 <property name="rightMargin">
753 <number>3</number>
754 </property>
755 <property name="bottomMargin">
756 <number>3</number>
757 </property>
758 <item>
759 <widget class="QSpinBox" name="spinboxLStickRange">
760 <property name="minimumSize">
761 <size>
762 <width>55</width>
763 <height>21</height>
764 </size>
765 </property>
766 <property name="maximumSize">
767 <size>
768 <width>55</width>
769 <height>16777215</height>
770 </size>
771 </property>
772 <property name="suffix">
773 <string>%</string>
774 </property>
775 <property name="minimum">
776 <number>50</number>
777 </property>
778 <property name="maximum">
779 <number>150</number>
780 </property>
781 <property name="value">
782 <number>100</number>
783 </property>
784 </widget>
785 </item>
786 </layout>
787 </widget>
788 </item>
789 </layout>
790 </item>
791 <item>
792 <layout class="QVBoxLayout" name="sliderLStickDeadzoneModifierRangeVerticalLayout">
793 <property name="spacing">
794 <number>3</number>
795 </property>
796 <property name="sizeConstraint">
797 <enum>QLayout::SetDefaultConstraint</enum>
798 </property>
799 <property name="leftMargin">
800 <number>0</number>
801 </property>
802 <property name="topMargin">
803 <number>2</number>
804 </property>
805 <property name="rightMargin">
806 <number>0</number>
807 </property>
808 <property name="bottomMargin">
809 <number>3</number>
810 </property>
811 <item>
812 <layout class="QHBoxLayout" name="sliderLStickDeadzoneHorizontalLayout">
813 <item>
814 <widget class="QLabel" name="labelLStickDeadzone">
815 <property name="text">
816 <string>Deadzone: 0%</string>
817 </property>
818 <property name="alignment">
819 <set>Qt::AlignHCenter</set>
820 </property>
821 </widget>
822 </item>
823 </layout>
824 </item>
825 <item>
826 <widget class="QSlider" name="sliderLStickDeadzone">
827 <property name="maximum">
828 <number>100</number>
829 </property>
830 <property name="orientation">
831 <enum>Qt::Horizontal</enum>
832 </property>
833 </widget>
834 </item>
835 <item>
836 <layout class="QHBoxLayout" name="sliderLStickModifierRangeHorizontalLayout">
837 <item>
838 <widget class="QLabel" name="labelLStickModifierRange">
839 <property name="text">
840 <string>Modifier Range: 0%</string>
841 </property>
842 <property name="alignment">
843 <set>Qt::AlignHCenter</set>
844 </property>
845 </widget>
846 </item>
847 </layout>
848 </item>
849 <item>
850 <widget class="QSlider" name="sliderLStickModifierRange">
851 <property name="maximum">
852 <number>100</number>
853 </property>
854 <property name="orientation">
855 <enum>Qt::Horizontal</enum>
856 </property>
857 </widget>
858 </item>
859 </layout>
860 </item>
861 </layout>
862 </widget>
863 </item>
864 <item>
865 <spacer name="verticalSpacerBottomLeft">
866 <property name="orientation">
867 <enum>Qt::Vertical</enum>
868 </property>
869 <property name="sizeHint" stdset="0">
870 <size>
871 <width>20</width>
872 <height>0</height>
873 </size>
874 </property>
875 </spacer>
876 </item>
877 <item>
878 <widget class="QGroupBox" name="Dpad">
879 <property name="sizePolicy">
880 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
881 <horstretch>0</horstretch>
882 <verstretch>0</verstretch>
883 </sizepolicy>
884 </property>
885 <property name="title">
886 <string>D-Pad</string>
887 </property>
888 <property name="flat">
889 <bool>false</bool>
890 </property>
891 <property name="checkable">
892 <bool>false</bool>
893 </property>
894 <layout class="QVBoxLayout" name="verticalLayout_5">
895 <property name="spacing">
896 <number>0</number>
655 </property> 897 </property>
656 </widget> 898 <property name="leftMargin">
657 </item> 899 <number>3</number>
658 </layout>
659 </item>
660 <item>
661 <widget class="QPushButton" name="buttonLStickUp">
662 <property name="text">
663 <string/>
664 </property>
665 </widget>
666 </item>
667 </layout>
668 </item>
669 <item row="0" column="2">
670 <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
671 <item>
672 <layout class="QHBoxLayout" name="buttonLStickRightHorizontalLayout">
673 <item>
674 <widget class="QLabel" name="labelLStickRight">
675 <property name="text">
676 <string>Right:</string>
677 </property> 900 </property>
678 </widget> 901 <property name="topMargin">
679 </item> 902 <number>0</number>
680 </layout>
681 </item>
682 <item>
683 <widget class="QPushButton" name="buttonLStickRight">
684 <property name="text">
685 <string/>
686 </property>
687 </widget>
688 </item>
689 </layout>
690 </item>
691 <item row="4" column="1" colspan="2">
692 <widget class="QPushButton" name="buttonLStickAnalog">
693 <property name="text">
694 <string>Set Analog Stick</string>
695 </property>
696 </widget>
697 </item>
698 <item row="0" column="1">
699 <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
700 <item>
701 <layout class="QHBoxLayout" name="buttonLStickLeftHorizontalLayout_2">
702 <item>
703 <widget class="QLabel" name="labelLStickLeft">
704 <property name="text">
705 <string>Left:</string>
706 </property> 903 </property>
707 </widget> 904 <property name="rightMargin">
708 </item> 905 <number>3</number>
709 </layout>
710 </item>
711 <item>
712 <widget class="QPushButton" name="buttonLStickLeft">
713 <property name="text">
714 <string/>
715 </property>
716 </widget>
717 </item>
718 </layout>
719 </item>
720 <item row="1" column="2">
721 <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
722 <item>
723 <layout class="QHBoxLayout" name="buttonLStickDownHorizontalLayout">
724 <item>
725 <widget class="QLabel" name="labelLStickDown">
726 <property name="text">
727 <string>Down:</string>
728 </property> 906 </property>
729 </widget> 907 <property name="bottomMargin">
730 </item> 908 <number>3</number>
731 </layout>
732 </item>
733 <item>
734 <widget class="QPushButton" name="buttonLStickDown">
735 <property name="text">
736 <string/>
737 </property>
738 </widget>
739 </item>
740 </layout>
741 </item>
742 <item row="3" column="2">
743 <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
744 <item>
745 <layout class="QHBoxLayout" name="buttonLStickModHorizontalLayout">
746 <item>
747 <widget class="QLabel" name="labelLStickMod">
748 <property name="text">
749 <string>Modifier:</string>
750 </property> 909 </property>
751 </widget> 910 <item>
752 </item> 911 <widget class="QWidget" name="buttonDpadUpWidget" native="true">
753 </layout> 912 <layout class="QHBoxLayout" name="horizontalLayout_23">
754 </item> 913 <property name="spacing">
755 <item> 914 <number>0</number>
756 <widget class="QPushButton" name="buttonLStickMod"> 915 </property>
757 <property name="text"> 916 <property name="leftMargin">
758 <string/> 917 <number>0</number>
759 </property> 918 </property>
760 </widget> 919 <property name="topMargin">
761 </item> 920 <number>0</number>
762 </layout> 921 </property>
922 <property name="rightMargin">
923 <number>0</number>
924 </property>
925 <property name="bottomMargin">
926 <number>0</number>
927 </property>
928 <item>
929 <spacer name="horizontalSpacerDpadUpLeft">
930 <property name="orientation">
931 <enum>Qt::Horizontal</enum>
932 </property>
933 <property name="sizeHint" stdset="0">
934 <size>
935 <width>20</width>
936 <height>20</height>
937 </size>
938 </property>
939 </spacer>
940 </item>
941 <item alignment="Qt::AlignHCenter">
942 <widget class="QGroupBox" name="buttonDpadUpGroup">
943 <property name="title">
944 <string>Up</string>
945 </property>
946 <property name="alignment">
947 <set>Qt::AlignCenter</set>
948 </property>
949 <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
950 <property name="spacing">
951 <number>3</number>
952 </property>
953 <property name="leftMargin">
954 <number>3</number>
955 </property>
956 <property name="topMargin">
957 <number>3</number>
958 </property>
959 <property name="rightMargin">
960 <number>3</number>
961 </property>
962 <property name="bottomMargin">
963 <number>3</number>
964 </property>
965 <item>
966 <widget class="QPushButton" name="buttonDpadUp">
967 <property name="minimumSize">
968 <size>
969 <width>57</width>
970 <height>0</height>
971 </size>
972 </property>
973 <property name="maximumSize">
974 <size>
975 <width>55</width>
976 <height>16777215</height>
977 </size>
978 </property>
979 <property name="styleSheet">
980 <string notr="true">min-width: 55px;</string>
981 </property>
982 <property name="text">
983 <string>Up</string>
984 </property>
985 </widget>
986 </item>
987 </layout>
988 </widget>
989 </item>
990 <item>
991 <spacer name="horizontalSpacerDpadUpRight">
992 <property name="orientation">
993 <enum>Qt::Horizontal</enum>
994 </property>
995 <property name="sizeHint" stdset="0">
996 <size>
997 <width>20</width>
998 <height>20</height>
999 </size>
1000 </property>
1001 </spacer>
1002 </item>
1003 </layout>
1004 </widget>
1005 </item>
1006 <item>
1007 <layout class="QHBoxLayout" name="buttonDpadLeftRightHorizontalLayout">
1008 <property name="spacing">
1009 <number>3</number>
1010 </property>
1011 <item alignment="Qt::AlignHCenter">
1012 <widget class="QGroupBox" name="buttonDpadLeftGroup">
1013 <property name="title">
1014 <string>Left</string>
1015 </property>
1016 <property name="alignment">
1017 <set>Qt::AlignCenter</set>
1018 </property>
1019 <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
1020 <property name="spacing">
1021 <number>3</number>
1022 </property>
1023 <property name="leftMargin">
1024 <number>3</number>
1025 </property>
1026 <property name="topMargin">
1027 <number>3</number>
1028 </property>
1029 <property name="rightMargin">
1030 <number>3</number>
1031 </property>
1032 <property name="bottomMargin">
1033 <number>3</number>
1034 </property>
1035 <item>
1036 <widget class="QPushButton" name="buttonDpadLeft">
1037 <property name="minimumSize">
1038 <size>
1039 <width>57</width>
1040 <height>0</height>
1041 </size>
1042 </property>
1043 <property name="maximumSize">
1044 <size>
1045 <width>55</width>
1046 <height>16777215</height>
1047 </size>
1048 </property>
1049 <property name="styleSheet">
1050 <string notr="true">min-width: 55px;</string>
1051 </property>
1052 <property name="text">
1053 <string>Left</string>
1054 </property>
1055 </widget>
1056 </item>
1057 </layout>
1058 </widget>
1059 </item>
1060 <item alignment="Qt::AlignHCenter">
1061 <widget class="QGroupBox" name="buttonDpadRightGroup">
1062 <property name="title">
1063 <string>Right</string>
1064 </property>
1065 <property name="alignment">
1066 <set>Qt::AlignCenter</set>
1067 </property>
1068 <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
1069 <property name="spacing">
1070 <number>3</number>
1071 </property>
1072 <property name="leftMargin">
1073 <number>3</number>
1074 </property>
1075 <property name="topMargin">
1076 <number>3</number>
1077 </property>
1078 <property name="rightMargin">
1079 <number>3</number>
1080 </property>
1081 <property name="bottomMargin">
1082 <number>3</number>
1083 </property>
1084 <item>
1085 <widget class="QPushButton" name="buttonDpadRight">
1086 <property name="minimumSize">
1087 <size>
1088 <width>57</width>
1089 <height>0</height>
1090 </size>
1091 </property>
1092 <property name="maximumSize">
1093 <size>
1094 <width>55</width>
1095 <height>16777215</height>
1096 </size>
1097 </property>
1098 <property name="styleSheet">
1099 <string notr="true">min-width: 55px;</string>
1100 </property>
1101 <property name="text">
1102 <string>Right</string>
1103 </property>
1104 </widget>
1105 </item>
1106 </layout>
1107 </widget>
1108 </item>
1109 </layout>
1110 </item>
1111 <item>
1112 <widget class="QWidget" name="buttonDpadDownWidget" native="true">
1113 <layout class="QHBoxLayout" name="horizontalLayout_24">
1114 <property name="spacing">
1115 <number>0</number>
1116 </property>
1117 <property name="leftMargin">
1118 <number>0</number>
1119 </property>
1120 <property name="topMargin">
1121 <number>0</number>
1122 </property>
1123 <property name="rightMargin">
1124 <number>0</number>
1125 </property>
1126 <property name="bottomMargin">
1127 <number>0</number>
1128 </property>
1129 <item>
1130 <spacer name="horizontalSpacerDpadDownLeft">
1131 <property name="orientation">
1132 <enum>Qt::Horizontal</enum>
1133 </property>
1134 <property name="sizeHint" stdset="0">
1135 <size>
1136 <width>20</width>
1137 <height>20</height>
1138 </size>
1139 </property>
1140 </spacer>
1141 </item>
1142 <item alignment="Qt::AlignHCenter">
1143 <widget class="QGroupBox" name="buttonDpadDownGroup">
1144 <property name="title">
1145 <string>Down</string>
1146 </property>
1147 <property name="alignment">
1148 <set>Qt::AlignCenter</set>
1149 </property>
1150 <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
1151 <property name="spacing">
1152 <number>3</number>
1153 </property>
1154 <property name="leftMargin">
1155 <number>3</number>
1156 </property>
1157 <property name="topMargin">
1158 <number>3</number>
1159 </property>
1160 <property name="rightMargin">
1161 <number>3</number>
1162 </property>
1163 <property name="bottomMargin">
1164 <number>3</number>
1165 </property>
1166 <item>
1167 <widget class="QPushButton" name="buttonDpadDown">
1168 <property name="minimumSize">
1169 <size>
1170 <width>57</width>
1171 <height>0</height>
1172 </size>
1173 </property>
1174 <property name="maximumSize">
1175 <size>
1176 <width>55</width>
1177 <height>16777215</height>
1178 </size>
1179 </property>
1180 <property name="styleSheet">
1181 <string notr="true">min-width: 55px;</string>
1182 </property>
1183 <property name="text">
1184 <string>Down</string>
1185 </property>
1186 </widget>
1187 </item>
1188 </layout>
1189 </widget>
1190 </item>
1191 <item>
1192 <spacer name="horizontalSpacerDpadDownRight">
1193 <property name="orientation">
1194 <enum>Qt::Horizontal</enum>
1195 </property>
1196 <property name="sizeHint" stdset="0">
1197 <size>
1198 <width>20</width>
1199 <height>20</height>
1200 </size>
1201 </property>
1202 </spacer>
1203 </item>
1204 </layout>
1205 </widget>
1206 </item>
1207 </layout>
1208 </widget>
1209 </item>
1210 <item>
1211 <spacer name="verticalSpacerBottomLeft_2">
1212 <property name="orientation">
1213 <enum>Qt::Vertical</enum>
1214 </property>
1215 <property name="sizeHint" stdset="0">
1216 <size>
1217 <width>20</width>
1218 <height>0</height>
1219 </size>
1220 </property>
1221 </spacer>
1222 </item>
1223 </layout>
1224 </widget>
763 </item> 1225 </item>
764 <item row="3" column="1"> 1226 <item>
765 <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0"> 1227 <widget class="QWidget" name="bottomMiddle" native="true">
766 <item> 1228 <layout class="QVBoxLayout" stretch="0,0,0">
767 <layout class="QHBoxLayout" name="buttonLStickPressedHorizontalLayout"> 1229 <property name="spacing">
768 <item> 1230 <number>6</number>
769 <widget class="QLabel" name="labelLStickPressed"> 1231 </property>
770 <property name="text"> 1232 <property name="leftMargin">
771 <string>Pressed:</string> 1233 <number>0</number>
1234 </property>
1235 <property name="topMargin">
1236 <number>0</number>
1237 </property>
1238 <property name="rightMargin">
1239 <number>0</number>
1240 </property>
1241 <property name="bottomMargin">
1242 <number>0</number>
1243 </property>
1244 <item>
1245 <layout class="QHBoxLayout" name="shoulderButtons">
1246 <property name="spacing">
1247 <number>3</number>
1248 </property>
1249 <item>
1250 <widget class="QWidget" name="buttonShoulderButtonsLeft" native="true">
1251 <layout class="QVBoxLayout" name="buttonShoulderButtonsLeftVerticalLayout">
1252 <property name="spacing">
1253 <number>0</number>
1254 </property>
1255 <property name="leftMargin">
1256 <number>0</number>
1257 </property>
1258 <property name="topMargin">
1259 <number>0</number>
1260 </property>
1261 <property name="rightMargin">
1262 <number>0</number>
1263 </property>
1264 <property name="bottomMargin">
1265 <number>0</number>
1266 </property>
1267 <item>
1268 <widget class="QGroupBox" name="buttonShoulderButtonsButtonLGroup">
1269 <property name="title">
1270 <string>L</string>
1271 </property>
1272 <property name="alignment">
1273 <set>Qt::AlignCenter</set>
1274 </property>
1275 <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
1276 <property name="spacing">
1277 <number>3</number>
1278 </property>
1279 <property name="leftMargin">
1280 <number>3</number>
1281 </property>
1282 <property name="topMargin">
1283 <number>3</number>
1284 </property>
1285 <property name="rightMargin">
1286 <number>3</number>
1287 </property>
1288 <property name="bottomMargin">
1289 <number>3</number>
1290 </property>
1291 <item>
1292 <widget class="QPushButton" name="buttonL">
1293 <property name="minimumSize">
1294 <size>
1295 <width>57</width>
1296 <height>0</height>
1297 </size>
1298 </property>
1299 <property name="maximumSize">
1300 <size>
1301 <width>55</width>
1302 <height>16777215</height>
1303 </size>
1304 </property>
1305 <property name="styleSheet">
1306 <string notr="true">min-width: 55px;</string>
1307 </property>
1308 <property name="text">
1309 <string>L</string>
1310 </property>
1311 </widget>
1312 </item>
1313 </layout>
1314 </widget>
1315 </item>
1316 <item>
1317 <widget class="QGroupBox" name="buttonShoulderButtonsButtonZLGroup">
1318 <property name="title">
1319 <string>ZL</string>
1320 </property>
1321 <property name="alignment">
1322 <set>Qt::AlignCenter</set>
1323 </property>
1324 <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
1325 <property name="spacing">
1326 <number>3</number>
1327 </property>
1328 <property name="leftMargin">
1329 <number>3</number>
1330 </property>
1331 <property name="topMargin">
1332 <number>3</number>
1333 </property>
1334 <property name="rightMargin">
1335 <number>3</number>
1336 </property>
1337 <property name="bottomMargin">
1338 <number>3</number>
1339 </property>
1340 <item>
1341 <widget class="QPushButton" name="buttonZL">
1342 <property name="minimumSize">
1343 <size>
1344 <width>57</width>
1345 <height>0</height>
1346 </size>
1347 </property>
1348 <property name="maximumSize">
1349 <size>
1350 <width>55</width>
1351 <height>16777215</height>
1352 </size>
1353 </property>
1354 <property name="styleSheet">
1355 <string notr="true">min-width: 55px;</string>
1356 </property>
1357 <property name="text">
1358 <string>ZL</string>
1359 </property>
1360 </widget>
1361 </item>
1362 </layout>
1363 </widget>
1364 </item>
1365 </layout>
1366 </widget>
1367 </item>
1368 <item>
1369 <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget" native="true">
1370 <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidgetLayout">
1371 <property name="spacing">
1372 <number>0</number>
1373 </property>
1374 <property name="leftMargin">
1375 <number>0</number>
1376 </property>
1377 <property name="topMargin">
1378 <number>0</number>
1379 </property>
1380 <property name="rightMargin">
1381 <number>0</number>
1382 </property>
1383 <property name="bottomMargin">
1384 <number>0</number>
1385 </property>
1386 <item>
1387 <spacer name="horizontalSpacerShoulderButtons1">
1388 <property name="orientation">
1389 <enum>Qt::Horizontal</enum>
1390 </property>
1391 <property name="sizeHint" stdset="0">
1392 <size>
1393 <width>0</width>
1394 <height>20</height>
1395 </size>
1396 </property>
1397 </spacer>
1398 </item>
1399 </layout>
1400 </widget>
1401 </item>
1402 <item>
1403 <widget class="QWidget" name="buttonMiscButtonsMinusScreenshot" native="true">
1404 <layout class="QVBoxLayout" name="buttonMiscButtonsMinusScreenshotVerticalLayout">
1405 <property name="spacing">
1406 <number>0</number>
1407 </property>
1408 <property name="leftMargin">
1409 <number>0</number>
1410 </property>
1411 <property name="topMargin">
1412 <number>0</number>
1413 </property>
1414 <property name="rightMargin">
1415 <number>0</number>
1416 </property>
1417 <property name="bottomMargin">
1418 <number>0</number>
1419 </property>
1420 <item alignment="Qt::AlignHCenter">
1421 <widget class="QGroupBox" name="buttonMiscButtonsMinusGroup">
1422 <property name="title">
1423 <string>Minus</string>
1424 </property>
1425 <property name="alignment">
1426 <set>Qt::AlignCenter</set>
1427 </property>
1428 <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout">
1429 <property name="spacing">
1430 <number>3</number>
1431 </property>
1432 <property name="leftMargin">
1433 <number>3</number>
1434 </property>
1435 <property name="topMargin">
1436 <number>3</number>
1437 </property>
1438 <property name="rightMargin">
1439 <number>3</number>
1440 </property>
1441 <property name="bottomMargin">
1442 <number>3</number>
1443 </property>
1444 <item>
1445 <widget class="QPushButton" name="buttonMinus">
1446 <property name="minimumSize">
1447 <size>
1448 <width>57</width>
1449 <height>0</height>
1450 </size>
1451 </property>
1452 <property name="maximumSize">
1453 <size>
1454 <width>55</width>
1455 <height>16777215</height>
1456 </size>
1457 </property>
1458 <property name="styleSheet">
1459 <string notr="true">min-width: 55px;</string>
1460 </property>
1461 <property name="text">
1462 <string>Minus</string>
1463 </property>
1464 </widget>
1465 </item>
1466 </layout>
1467 </widget>
1468 </item>
1469 <item alignment="Qt::AlignHCenter">
1470 <widget class="QGroupBox" name="buttonMiscButtonsScreenshotGroup">
1471 <property name="title">
1472 <string>Capture</string>
1473 </property>
1474 <property name="alignment">
1475 <set>Qt::AlignCenter</set>
1476 </property>
1477 <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
1478 <property name="spacing">
1479 <number>3</number>
1480 </property>
1481 <property name="leftMargin">
1482 <number>3</number>
1483 </property>
1484 <property name="topMargin">
1485 <number>3</number>
1486 </property>
1487 <property name="rightMargin">
1488 <number>3</number>
1489 </property>
1490 <property name="bottomMargin">
1491 <number>3</number>
1492 </property>
1493 <item>
1494 <widget class="QPushButton" name="buttonScreenshot">
1495 <property name="minimumSize">
1496 <size>
1497 <width>57</width>
1498 <height>0</height>
1499 </size>
1500 </property>
1501 <property name="maximumSize">
1502 <size>
1503 <width>55</width>
1504 <height>16777215</height>
1505 </size>
1506 </property>
1507 <property name="styleSheet">
1508 <string notr="true">min-width: 55px;</string>
1509 </property>
1510 <property name="text">
1511 <string>Capture</string>
1512 </property>
1513 </widget>
1514 </item>
1515 </layout>
1516 </widget>
1517 </item>
1518 </layout>
1519 </widget>
1520 </item>
1521 <item>
1522 <widget class="QWidget" name="buttonMiscButtonsPlusHome" native="true">
1523 <layout class="QVBoxLayout" name="buttonMiscButtonsPlusHomeVerticalLayout">
1524 <property name="spacing">
1525 <number>0</number>
1526 </property>
1527 <property name="leftMargin">
1528 <number>0</number>
1529 </property>
1530 <property name="topMargin">
1531 <number>0</number>
1532 </property>
1533 <property name="rightMargin">
1534 <number>0</number>
1535 </property>
1536 <property name="bottomMargin">
1537 <number>0</number>
1538 </property>
1539 <item alignment="Qt::AlignHCenter">
1540 <widget class="QGroupBox" name="buttonMiscButtonsPlusGroup">
1541 <property name="title">
1542 <string>Plus</string>
1543 </property>
1544 <property name="alignment">
1545 <set>Qt::AlignCenter</set>
1546 </property>
1547 <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
1548 <property name="spacing">
1549 <number>3</number>
1550 </property>
1551 <property name="leftMargin">
1552 <number>3</number>
1553 </property>
1554 <property name="topMargin">
1555 <number>3</number>
1556 </property>
1557 <property name="rightMargin">
1558 <number>3</number>
1559 </property>
1560 <property name="bottomMargin">
1561 <number>3</number>
1562 </property>
1563 <item>
1564 <widget class="QPushButton" name="buttonPlus">
1565 <property name="minimumSize">
1566 <size>
1567 <width>57</width>
1568 <height>0</height>
1569 </size>
1570 </property>
1571 <property name="maximumSize">
1572 <size>
1573 <width>55</width>
1574 <height>16777215</height>
1575 </size>
1576 </property>
1577 <property name="styleSheet">
1578 <string notr="true">min-width: 55px;</string>
1579 </property>
1580 <property name="text">
1581 <string>Plus</string>
1582 </property>
1583 </widget>
1584 </item>
1585 </layout>
1586 </widget>
1587 </item>
1588 <item alignment="Qt::AlignHCenter">
1589 <widget class="QGroupBox" name="buttonMiscButtonsHomeGroup">
1590 <property name="title">
1591 <string>Home</string>
1592 </property>
1593 <property name="alignment">
1594 <set>Qt::AlignCenter</set>
1595 </property>
1596 <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
1597 <property name="spacing">
1598 <number>3</number>
1599 </property>
1600 <property name="leftMargin">
1601 <number>3</number>
1602 </property>
1603 <property name="topMargin">
1604 <number>3</number>
1605 </property>
1606 <property name="rightMargin">
1607 <number>3</number>
1608 </property>
1609 <property name="bottomMargin">
1610 <number>3</number>
1611 </property>
1612 <item>
1613 <widget class="QPushButton" name="buttonHome">
1614 <property name="minimumSize">
1615 <size>
1616 <width>57</width>
1617 <height>0</height>
1618 </size>
1619 </property>
1620 <property name="maximumSize">
1621 <size>
1622 <width>55</width>
1623 <height>16777215</height>
1624 </size>
1625 </property>
1626 <property name="styleSheet">
1627 <string notr="true">min-width: 55px;</string>
1628 </property>
1629 <property name="text">
1630 <string>Home</string>
1631 </property>
1632 </widget>
1633 </item>
1634 </layout>
1635 </widget>
1636 </item>
1637 </layout>
1638 </widget>
1639 </item>
1640 <item>
1641 <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget3" native="true">
1642 <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget3Layout">
1643 <property name="spacing">
1644 <number>0</number>
1645 </property>
1646 <property name="leftMargin">
1647 <number>0</number>
1648 </property>
1649 <property name="topMargin">
1650 <number>0</number>
1651 </property>
1652 <property name="rightMargin">
1653 <number>0</number>
1654 </property>
1655 <property name="bottomMargin">
1656 <number>0</number>
1657 </property>
1658 <item>
1659 <spacer name="horizontalSpacerShoulderButtons2">
1660 <property name="orientation">
1661 <enum>Qt::Horizontal</enum>
1662 </property>
1663 <property name="sizeHint" stdset="0">
1664 <size>
1665 <width>0</width>
1666 <height>20</height>
1667 </size>
1668 </property>
1669 </spacer>
1670 </item>
1671 </layout>
1672 </widget>
1673 </item>
1674 <item>
1675 <widget class="QWidget" name="buttonShoulderButtonsRight" native="true">
1676 <layout class="QVBoxLayout" name="buttonShoulderButtonsRightVerticalLayout">
1677 <property name="spacing">
1678 <number>0</number>
1679 </property>
1680 <property name="leftMargin">
1681 <number>0</number>
1682 </property>
1683 <property name="topMargin">
1684 <number>0</number>
1685 </property>
1686 <property name="rightMargin">
1687 <number>0</number>
1688 </property>
1689 <property name="bottomMargin">
1690 <number>0</number>
1691 </property>
1692 <item>
1693 <widget class="QGroupBox" name="buttonShoulderButtonsRGroup">
1694 <property name="title">
1695 <string>R</string>
1696 </property>
1697 <property name="alignment">
1698 <set>Qt::AlignCenter</set>
1699 </property>
1700 <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
1701 <property name="spacing">
1702 <number>3</number>
1703 </property>
1704 <property name="leftMargin">
1705 <number>3</number>
1706 </property>
1707 <property name="topMargin">
1708 <number>3</number>
1709 </property>
1710 <property name="rightMargin">
1711 <number>3</number>
1712 </property>
1713 <property name="bottomMargin">
1714 <number>3</number>
1715 </property>
1716 <item>
1717 <widget class="QPushButton" name="buttonR">
1718 <property name="minimumSize">
1719 <size>
1720 <width>57</width>
1721 <height>0</height>
1722 </size>
1723 </property>
1724 <property name="maximumSize">
1725 <size>
1726 <width>55</width>
1727 <height>16777215</height>
1728 </size>
1729 </property>
1730 <property name="styleSheet">
1731 <string notr="true">min-width: 55px;</string>
1732 </property>
1733 <property name="text">
1734 <string>R</string>
1735 </property>
1736 </widget>
1737 </item>
1738 </layout>
1739 </widget>
1740 </item>
1741 <item>
1742 <widget class="QGroupBox" name="buttonShoulderButtonsZRGroup">
1743 <property name="title">
1744 <string>ZR</string>
1745 </property>
1746 <property name="alignment">
1747 <set>Qt::AlignCenter</set>
1748 </property>
1749 <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
1750 <property name="spacing">
1751 <number>3</number>
1752 </property>
1753 <property name="leftMargin">
1754 <number>3</number>
1755 </property>
1756 <property name="topMargin">
1757 <number>3</number>
1758 </property>
1759 <property name="rightMargin">
1760 <number>3</number>
1761 </property>
1762 <property name="bottomMargin">
1763 <number>3</number>
1764 </property>
1765 <item>
1766 <widget class="QPushButton" name="buttonZR">
1767 <property name="minimumSize">
1768 <size>
1769 <width>57</width>
1770 <height>0</height>
1771 </size>
1772 </property>
1773 <property name="maximumSize">
1774 <size>
1775 <width>55</width>
1776 <height>16777215</height>
1777 </size>
1778 </property>
1779 <property name="styleSheet">
1780 <string notr="true">min-width: 55px;</string>
1781 </property>
1782 <property name="text">
1783 <string>ZR</string>
1784 </property>
1785 </widget>
1786 </item>
1787 </layout>
1788 </widget>
1789 </item>
1790 </layout>
1791 </widget>
1792 </item>
1793 <item>
1794 <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget2" native="true">
1795 <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget2Layout">
1796 <property name="spacing">
1797 <number>0</number>
1798 </property>
1799 <property name="leftMargin">
1800 <number>0</number>
1801 </property>
1802 <property name="topMargin">
1803 <number>0</number>
1804 </property>
1805 <property name="rightMargin">
1806 <number>0</number>
1807 </property>
1808 <property name="bottomMargin">
1809 <number>0</number>
1810 </property>
1811 <item>
1812 <spacer name="horizontalSpacerShoulderButtons3">
1813 <property name="orientation">
1814 <enum>Qt::Horizontal</enum>
1815 </property>
1816 <property name="sizeHint" stdset="0">
1817 <size>
1818 <width>0</width>
1819 <height>20</height>
1820 </size>
1821 </property>
1822 </spacer>
1823 </item>
1824 </layout>
1825 </widget>
1826 </item>
1827 <item>
1828 <widget class="QWidget" name="buttonShoulderButtonsSLSR" native="true">
1829 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRVerticalLayout">
1830 <property name="spacing">
1831 <number>0</number>
1832 </property>
1833 <property name="leftMargin">
1834 <number>0</number>
1835 </property>
1836 <property name="topMargin">
1837 <number>0</number>
1838 </property>
1839 <property name="rightMargin">
1840 <number>0</number>
1841 </property>
1842 <property name="bottomMargin">
1843 <number>0</number>
1844 </property>
1845 <item alignment="Qt::AlignHCenter">
1846 <widget class="QGroupBox" name="buttonShoulderButtonsSLGroup">
1847 <property name="title">
1848 <string>SL</string>
1849 </property>
1850 <property name="alignment">
1851 <set>Qt::AlignCenter</set>
1852 </property>
1853 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
1854 <property name="spacing">
1855 <number>3</number>
1856 </property>
1857 <property name="leftMargin">
1858 <number>3</number>
1859 </property>
1860 <property name="topMargin">
1861 <number>3</number>
1862 </property>
1863 <property name="rightMargin">
1864 <number>3</number>
1865 </property>
1866 <property name="bottomMargin">
1867 <number>3</number>
1868 </property>
1869 <item>
1870 <widget class="QPushButton" name="buttonSL">
1871 <property name="minimumSize">
1872 <size>
1873 <width>57</width>
1874 <height>0</height>
1875 </size>
1876 </property>
1877 <property name="maximumSize">
1878 <size>
1879 <width>55</width>
1880 <height>16777215</height>
1881 </size>
1882 </property>
1883 <property name="styleSheet">
1884 <string notr="true">min-width: 55px;</string>
1885 </property>
1886 <property name="text">
1887 <string>SL</string>
1888 </property>
1889 </widget>
1890 </item>
1891 </layout>
1892 </widget>
1893 </item>
1894 <item alignment="Qt::AlignHCenter">
1895 <widget class="QGroupBox" name="buttonShoulderButtonsSRGroup">
1896 <property name="title">
1897 <string>SR</string>
1898 </property>
1899 <property name="alignment">
1900 <set>Qt::AlignCenter</set>
1901 </property>
1902 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
1903 <property name="spacing">
1904 <number>3</number>
1905 </property>
1906 <property name="leftMargin">
1907 <number>3</number>
1908 </property>
1909 <property name="topMargin">
1910 <number>3</number>
1911 </property>
1912 <property name="rightMargin">
1913 <number>3</number>
1914 </property>
1915 <property name="bottomMargin">
1916 <number>3</number>
1917 </property>
1918 <item>
1919 <widget class="QPushButton" name="buttonSR">
1920 <property name="minimumSize">
1921 <size>
1922 <width>57</width>
1923 <height>0</height>
1924 </size>
1925 </property>
1926 <property name="maximumSize">
1927 <size>
1928 <width>55</width>
1929 <height>16777215</height>
1930 </size>
1931 </property>
1932 <property name="styleSheet">
1933 <string notr="true">min-width: 55px;</string>
1934 </property>
1935 <property name="text">
1936 <string>SR</string>
1937 </property>
1938 </widget>
1939 </item>
1940 </layout>
1941 </widget>
1942 </item>
1943 </layout>
1944 </widget>
1945 </item>
1946 </layout>
1947 </item>
1948 <item>
1949 <widget class="QFrame" name="controllerFrame">
1950 <property name="sizePolicy">
1951 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1952 <horstretch>0</horstretch>
1953 <verstretch>0</verstretch>
1954 </sizepolicy>
1955 </property>
1956 <property name="font">
1957 <font>
1958 <weight>75</weight>
1959 <bold>true</bold>
1960 </font>
1961 </property>
1962 <property name="styleSheet">
1963 <string notr="true">image: url(:/controller/pro);</string>
1964 </property>
1965 <layout class="QVBoxLayout" name="verticalLayout_4">
1966 <property name="leftMargin">
1967 <number>0</number>
772 </property> 1968 </property>
773 </widget> 1969 <property name="topMargin">
774 </item> 1970 <number>0</number>
775 </layout>
776 </item>
777 <item>
778 <widget class="QPushButton" name="buttonLStick">
779 <property name="text">
780 <string/>
781 </property>
782 </widget>
783 </item>
784 </layout>
785 </item>
786 <item row="5" column="1" colspan="2">
787 <layout class="QVBoxLayout" name="sliderLStickDeadzoneAndModifierVerticalLayout">
788 <property name="sizeConstraint">
789 <enum>QLayout::SetDefaultConstraint</enum>
790 </property>
791 <item>
792 <layout class="QHBoxLayout" name="sliderLStickDeadzoneAndModifierHorizontalLayout">
793 <item>
794 <widget class="QLabel" name="labelLStickDeadzoneAndModifier">
795 <property name="text">
796 <string>Deadzone: 0</string>
797 </property> 1971 </property>
798 <property name="alignment"> 1972 <property name="rightMargin">
799 <enum>Qt::AlignHCenter</enum> 1973 <number>0</number>
800 </property> 1974 </property>
801 </widget> 1975 <property name="bottomMargin">
802 </item> 1976 <number>0</number>
803 </layout>
804 </item>
805 <item>
806 <widget class="QSlider" name="sliderLStickDeadzoneAndModifier">
807 <property name="orientation">
808 <enum>Qt::Horizontal</enum>
809 </property>
810 </widget>
811 </item>
812 </layout>
813 </item>
814 <item row="6" column="1">
815 <spacer name="LStick_verticalSpacer">
816 <property name="orientation">
817 <enum>Qt::Vertical</enum>
818 </property>
819 <property name="sizeHint" stdset="0">
820 <size>
821 <width>0</width>
822 <height>0</height>
823 </size>
824 </property>
825 </spacer>
826 </item>
827 </layout>
828 </widget>
829 </item>
830 <item row="3" column="0">
831 <widget class="QGroupBox" name="shoulderButtons">
832 <property name="title">
833 <string>Shoulder Buttons</string>
834 </property>
835 <property name="flat">
836 <bool>false</bool>
837 </property>
838 <property name="checkable">
839 <bool>false</bool>
840 </property>
841 <layout class="QGridLayout" name="gridLayout_3">
842 <item row="0" column="0">
843 <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
844 <item>
845 <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout">
846 <item>
847 <widget class="QLabel" name="labelL">
848 <property name="text">
849 <string>L:</string>
850 </property> 1977 </property>
851 </widget> 1978 </layout>
852 </item> 1979 </widget>
853 </layout> 1980 </item>
854 </item> 1981 <item>
855 <item> 1982 <layout class="QHBoxLayout" name="miscButtons">
856 <widget class="QPushButton" name="buttonL"> 1983 <property name="spacing">
857 <property name="text"> 1984 <number>3</number>
858 <string/> 1985 </property>
859 </property> 1986 <item>
860 </widget> 1987 <spacer name="horizontalSpacerMiscButtons1">
861 </item> 1988 <property name="orientation">
862 </layout> 1989 <enum>Qt::Horizontal</enum>
1990 </property>
1991 <property name="sizeHint" stdset="0">
1992 <size>
1993 <width>40</width>
1994 <height>0</height>
1995 </size>
1996 </property>
1997 </spacer>
1998 </item>
1999 <item>
2000 <spacer name="horizontalSpacerMiscButtons4">
2001 <property name="orientation">
2002 <enum>Qt::Horizontal</enum>
2003 </property>
2004 <property name="sizeHint" stdset="0">
2005 <size>
2006 <width>40</width>
2007 <height>0</height>
2008 </size>
2009 </property>
2010 </spacer>
2011 </item>
2012 </layout>
2013 </item>
2014 </layout>
2015 </widget>
863 </item> 2016 </item>
864 <item row="0" column="1"> 2017 <item>
865 <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> 2018 <widget class="QWidget" name="bottomRight" native="true">
866 <item> 2019 <layout class="QVBoxLayout" name="bottomRightLayout">
867 <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout"> 2020 <property name="spacing">
868 <item> 2021 <number>0</number>
869 <widget class="QLabel" name="labelR"> 2022 </property>
870 <property name="text"> 2023 <property name="leftMargin">
871 <string>R:</string> 2024 <number>0</number>
2025 </property>
2026 <property name="topMargin">
2027 <number>0</number>
2028 </property>
2029 <property name="rightMargin">
2030 <number>0</number>
2031 </property>
2032 <property name="bottomMargin">
2033 <number>0</number>
2034 </property>
2035 <item>
2036 <widget class="QGroupBox" name="faceButtons">
2037 <property name="sizePolicy">
2038 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2039 <horstretch>0</horstretch>
2040 <verstretch>0</verstretch>
2041 </sizepolicy>
2042 </property>
2043 <property name="title">
2044 <string>Face Buttons</string>
2045 </property>
2046 <property name="flat">
2047 <bool>false</bool>
2048 </property>
2049 <property name="checkable">
2050 <bool>false</bool>
2051 </property>
2052 <layout class="QVBoxLayout" name="verticalLayout">
2053 <property name="spacing">
2054 <number>0</number>
872 </property> 2055 </property>
873 </widget> 2056 <property name="leftMargin">
874 </item> 2057 <number>3</number>
875 </layout>
876 </item>
877 <item>
878 <widget class="QPushButton" name="buttonR">
879 <property name="text">
880 <string/>
881 </property>
882 </widget>
883 </item>
884 </layout>
885 </item>
886 <item row="1" column="0">
887 <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
888 <item>
889 <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout">
890 <item>
891 <widget class="QLabel" name="labelZL">
892 <property name="text">
893 <string>ZL:</string>
894 </property> 2058 </property>
895 </widget> 2059 <property name="topMargin">
896 </item> 2060 <number>0</number>
897 </layout>
898 </item>
899 <item>
900 <widget class="QPushButton" name="buttonZL">
901 <property name="text">
902 <string/>
903 </property>
904 </widget>
905 </item>
906 </layout>
907 </item>
908 <item row="1" column="1">
909 <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
910 <item>
911 <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout">
912 <item>
913 <widget class="QLabel" name="labelZR">
914 <property name="text">
915 <string>ZR:</string>
916 </property> 2061 </property>
917 </widget> 2062 <property name="rightMargin">
918 </item> 2063 <number>3</number>
919 </layout>
920 </item>
921 <item>
922 <widget class="QPushButton" name="buttonZR">
923 <property name="text">
924 <string/>
925 </property>
926 </widget>
927 </item>
928 </layout>
929 </item>
930 <item row="0" column="2">
931 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
932 <item>
933 <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout">
934 <item>
935 <widget class="QLabel" name="labelSL">
936 <property name="text">
937 <string>SL:</string>
938 </property> 2064 </property>
939 </widget> 2065 <property name="bottomMargin">
940 </item> 2066 <number>3</number>
941 </layout>
942 </item>
943 <item>
944 <widget class="QPushButton" name="buttonSL">
945 <property name="text">
946 <string/>
947 </property>
948 </widget>
949 </item>
950 </layout>
951 </item>
952 <item row="1" column="2">
953 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
954 <item>
955 <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout">
956 <item>
957 <widget class="QLabel" name="labelSR">
958 <property name="text">
959 <string>SR:</string>
960 </property> 2067 </property>
961 </widget> 2068 <item>
962 </item> 2069 <widget class="QWidget" name="buttonFaceButtonsBWidget" native="true">
963 </layout> 2070 <layout class="QHBoxLayout" name="horizontalLayout_6">
964 </item> 2071 <property name="spacing">
965 <item> 2072 <number>0</number>
966 <widget class="QPushButton" name="buttonSR"> 2073 </property>
967 <property name="text"> 2074 <property name="leftMargin">
968 <string/> 2075 <number>0</number>
969 </property> 2076 </property>
970 </widget> 2077 <property name="topMargin">
971 </item> 2078 <number>0</number>
972 </layout> 2079 </property>
973 </item> 2080 <property name="rightMargin">
974 </layout> 2081 <number>0</number>
975 </widget> 2082 </property>
976 </item> 2083 <property name="bottomMargin">
977 <item row="3" column="1"> 2084 <number>0</number>
978 <widget class="QGroupBox" name="misc"> 2085 </property>
979 <property name="title"> 2086 <item>
980 <string>Misc.</string> 2087 <spacer name="horizontalSpacerBLeft">
981 </property> 2088 <property name="orientation">
982 <property name="flat"> 2089 <enum>Qt::Horizontal</enum>
983 <bool>false</bool> 2090 </property>
984 </property> 2091 <property name="sizeHint" stdset="0">
985 <property name="checkable"> 2092 <size>
986 <bool>false</bool> 2093 <width>20</width>
987 </property> 2094 <height>20</height>
988 <layout class="QGridLayout" name="gridLayout_6"> 2095 </size>
989 <item row="1" column="0"> 2096 </property>
990 <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout"> 2097 </spacer>
991 <item> 2098 </item>
992 <layout class="QHBoxLayout" name="buttonMiscMinusHorizontalLayout"> 2099 <item alignment="Qt::AlignHCenter">
993 <item> 2100 <widget class="QGroupBox" name="buttonFaceButtonsXGroup">
994 <widget class="QLabel" name="labelMinus"> 2101 <property name="title">
995 <property name="text"> 2102 <string>X</string>
996 <string>Minus:</string> 2103 </property>
2104 <property name="alignment">
2105 <set>Qt::AlignCenter</set>
2106 </property>
2107 <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
2108 <property name="spacing">
2109 <number>3</number>
2110 </property>
2111 <property name="leftMargin">
2112 <number>3</number>
2113 </property>
2114 <property name="topMargin">
2115 <number>3</number>
2116 </property>
2117 <property name="rightMargin">
2118 <number>3</number>
2119 </property>
2120 <property name="bottomMargin">
2121 <number>3</number>
2122 </property>
2123 <item>
2124 <widget class="QPushButton" name="buttonX">
2125 <property name="minimumSize">
2126 <size>
2127 <width>57</width>
2128 <height>0</height>
2129 </size>
2130 </property>
2131 <property name="maximumSize">
2132 <size>
2133 <width>55</width>
2134 <height>16777215</height>
2135 </size>
2136 </property>
2137 <property name="styleSheet">
2138 <string notr="true">min-width: 55px;</string>
2139 </property>
2140 <property name="text">
2141 <string>X</string>
2142 </property>
2143 </widget>
2144 </item>
2145 </layout>
2146 </widget>
2147 </item>
2148 <item>
2149 <spacer name="horizontalSpacerBRight">
2150 <property name="orientation">
2151 <enum>Qt::Horizontal</enum>
2152 </property>
2153 <property name="sizeHint" stdset="0">
2154 <size>
2155 <width>20</width>
2156 <height>20</height>
2157 </size>
2158 </property>
2159 </spacer>
2160 </item>
2161 </layout>
2162 </widget>
2163 </item>
2164 <item>
2165 <layout class="QHBoxLayout" name="buttonFaceButtonsYAHorizontalLayout">
2166 <property name="spacing">
2167 <number>3</number>
2168 </property>
2169 <item alignment="Qt::AlignHCenter">
2170 <widget class="QGroupBox" name="buttonFaceButtonsYGroup">
2171 <property name="title">
2172 <string>Y</string>
2173 </property>
2174 <property name="alignment">
2175 <set>Qt::AlignCenter</set>
2176 </property>
2177 <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
2178 <property name="spacing">
2179 <number>3</number>
2180 </property>
2181 <property name="leftMargin">
2182 <number>3</number>
2183 </property>
2184 <property name="topMargin">
2185 <number>3</number>
2186 </property>
2187 <property name="rightMargin">
2188 <number>3</number>
2189 </property>
2190 <property name="bottomMargin">
2191 <number>3</number>
2192 </property>
2193 <item>
2194 <widget class="QPushButton" name="buttonY">
2195 <property name="minimumSize">
2196 <size>
2197 <width>57</width>
2198 <height>0</height>
2199 </size>
2200 </property>
2201 <property name="maximumSize">
2202 <size>
2203 <width>55</width>
2204 <height>16777215</height>
2205 </size>
2206 </property>
2207 <property name="styleSheet">
2208 <string notr="true">min-width: 55px;</string>
2209 </property>
2210 <property name="text">
2211 <string>Y</string>
2212 </property>
2213 </widget>
2214 </item>
2215 </layout>
2216 </widget>
2217 </item>
2218 <item alignment="Qt::AlignHCenter">
2219 <widget class="QGroupBox" name="buttonFaceButtonsAGroup">
2220 <property name="title">
2221 <string>A</string>
2222 </property>
2223 <property name="alignment">
2224 <set>Qt::AlignCenter</set>
2225 </property>
2226 <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
2227 <property name="spacing">
2228 <number>3</number>
2229 </property>
2230 <property name="leftMargin">
2231 <number>3</number>
2232 </property>
2233 <property name="topMargin">
2234 <number>3</number>
2235 </property>
2236 <property name="rightMargin">
2237 <number>3</number>
2238 </property>
2239 <property name="bottomMargin">
2240 <number>3</number>
2241 </property>
2242 <item>
2243 <widget class="QPushButton" name="buttonA">
2244 <property name="minimumSize">
2245 <size>
2246 <width>57</width>
2247 <height>0</height>
2248 </size>
2249 </property>
2250 <property name="maximumSize">
2251 <size>
2252 <width>55</width>
2253 <height>16777215</height>
2254 </size>
2255 </property>
2256 <property name="styleSheet">
2257 <string notr="true">min-width: 55px;</string>
2258 </property>
2259 <property name="text">
2260 <string>A</string>
2261 </property>
2262 </widget>
2263 </item>
2264 </layout>
2265 </widget>
2266 </item>
2267 </layout>
2268 </item>
2269 <item>
2270 <widget class="QWidget" name="buttonFaceButtonsXWidget" native="true">
2271 <layout class="QHBoxLayout" name="horizontalLayout_10">
2272 <property name="spacing">
2273 <number>0</number>
2274 </property>
2275 <property name="leftMargin">
2276 <number>0</number>
2277 </property>
2278 <property name="topMargin">
2279 <number>0</number>
2280 </property>
2281 <property name="rightMargin">
2282 <number>0</number>
2283 </property>
2284 <property name="bottomMargin">
2285 <number>0</number>
2286 </property>
2287 <item>
2288 <spacer name="horizontalSpacerXLeft">
2289 <property name="orientation">
2290 <enum>Qt::Horizontal</enum>
2291 </property>
2292 <property name="sizeHint" stdset="0">
2293 <size>
2294 <width>20</width>
2295 <height>20</height>
2296 </size>
2297 </property>
2298 </spacer>
2299 </item>
2300 <item alignment="Qt::AlignHCenter">
2301 <widget class="QGroupBox" name="buttonFaceButtonsBWidget_2">
2302 <property name="title">
2303 <string>B</string>
2304 </property>
2305 <property name="alignment">
2306 <set>Qt::AlignCenter</set>
2307 </property>
2308 <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
2309 <property name="spacing">
2310 <number>3</number>
2311 </property>
2312 <property name="leftMargin">
2313 <number>3</number>
2314 </property>
2315 <property name="topMargin">
2316 <number>3</number>
2317 </property>
2318 <property name="rightMargin">
2319 <number>3</number>
2320 </property>
2321 <property name="bottomMargin">
2322 <number>3</number>
2323 </property>
2324 <item>
2325 <widget class="QPushButton" name="buttonB">
2326 <property name="minimumSize">
2327 <size>
2328 <width>57</width>
2329 <height>0</height>
2330 </size>
2331 </property>
2332 <property name="maximumSize">
2333 <size>
2334 <width>55</width>
2335 <height>16777215</height>
2336 </size>
2337 </property>
2338 <property name="styleSheet">
2339 <string notr="true">min-width: 55px;</string>
2340 </property>
2341 <property name="text">
2342 <string>B</string>
2343 </property>
2344 </widget>
2345 </item>
2346 </layout>
2347 </widget>
2348 </item>
2349 <item>
2350 <spacer name="horizontalSpacerXRight">
2351 <property name="orientation">
2352 <enum>Qt::Horizontal</enum>
2353 </property>
2354 <property name="sizeHint" stdset="0">
2355 <size>
2356 <width>20</width>
2357 <height>20</height>
2358 </size>
2359 </property>
2360 </spacer>
2361 </item>
2362 </layout>
2363 </widget>
2364 </item>
2365 </layout>
2366 </widget>
2367 </item>
2368 <item>
2369 <spacer name="verticalSpacerBottomRight">
2370 <property name="orientation">
2371 <enum>Qt::Vertical</enum>
2372 </property>
2373 <property name="sizeHint" stdset="0">
2374 <size>
2375 <width>20</width>
2376 <height>0</height>
2377 </size>
2378 </property>
2379 </spacer>
2380 </item>
2381 <item>
2382 <widget class="QGroupBox" name="RStick">
2383 <property name="sizePolicy">
2384 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2385 <horstretch>0</horstretch>
2386 <verstretch>0</verstretch>
2387 </sizepolicy>
2388 </property>
2389 <property name="title">
2390 <string>Right Stick</string>
2391 </property>
2392 <property name="alignment">
2393 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
2394 </property>
2395 <property name="flat">
2396 <bool>false</bool>
2397 </property>
2398 <property name="checkable">
2399 <bool>false</bool>
2400 </property>
2401 <layout class="QVBoxLayout" name="verticalLayout_2">
2402 <property name="spacing">
2403 <number>0</number>
997 </property> 2404 </property>
998 </widget> 2405 <property name="leftMargin">
999 </item> 2406 <number>3</number>
1000 </layout>
1001 </item>
1002 <item>
1003 <widget class="QPushButton" name="buttonMinus">
1004 <property name="text">
1005 <string/>
1006 </property>
1007 </widget>
1008 </item>
1009 </layout>
1010 </item>
1011 <item row="3" column="1">
1012 <spacer name="verticalSpacer_2">
1013 <property name="orientation">
1014 <enum>Qt::Vertical</enum>
1015 </property>
1016 <property name="sizeHint" stdset="0">
1017 <size>
1018 <width>20</width>
1019 <height>40</height>
1020 </size>
1021 </property>
1022 </spacer>
1023 </item>
1024 <item row="0" column="0">
1025 <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
1026 <item>
1027 <layout class="QHBoxLayout" name="buttonMiscPlusHorizontalLayout">
1028 <item>
1029 <widget class="QLabel" name="labelPlus">
1030 <property name="text">
1031 <string>Plus:</string>
1032 </property> 2407 </property>
1033 </widget> 2408 <property name="topMargin">
1034 </item> 2409 <number>0</number>
1035 </layout>
1036 </item>
1037 <item>
1038 <widget class="QPushButton" name="buttonPlus">
1039 <property name="text">
1040 <string/>
1041 </property>
1042 </widget>
1043 </item>
1044 </layout>
1045 </item>
1046 <item row="0" column="1">
1047 <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
1048 <item>
1049 <layout class="QHBoxLayout" name="buttonMiscHomeHorizontalLayout">
1050 <item>
1051 <widget class="QLabel" name="labelHome">
1052 <property name="text">
1053 <string>Home:</string>
1054 </property> 2410 </property>
1055 </widget> 2411 <property name="rightMargin">
1056 </item> 2412 <number>3</number>
1057 </layout>
1058 </item>
1059 <item>
1060 <widget class="QPushButton" name="buttonHome">
1061 <property name="text">
1062 <string/>
1063 </property>
1064 </widget>
1065 </item>
1066 </layout>
1067 </item>
1068 <item row="1" column="1">
1069 <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
1070 <item>
1071 <layout class="QHBoxLayout" name="buttonMiscScrCapHorizontalLayout">
1072 <item>
1073 <widget class="QLabel" name="labelScreenshot">
1074 <property name="text">
1075 <string>Screen Capture:</string>
1076 </property> 2413 </property>
1077 <property name="wordWrap"> 2414 <property name="bottomMargin">
1078 <bool>false</bool> 2415 <number>0</number>
1079 </property> 2416 </property>
1080 </widget> 2417 <item>
1081 </item> 2418 <widget class="QWidget" name="buttonRStickUpWidget" native="true">
1082 </layout> 2419 <property name="minimumSize">
1083 </item> 2420 <size>
1084 <item> 2421 <width>0</width>
1085 <widget class="QPushButton" name="buttonScreenshot"> 2422 <height>0</height>
1086 <property name="sizePolicy"> 2423 </size>
1087 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 2424 </property>
1088 <horstretch>0</horstretch> 2425 <layout class="QHBoxLayout" name="horizontalLayout_9">
1089 <verstretch>0</verstretch> 2426 <property name="spacing">
1090 </sizepolicy> 2427 <number>0</number>
1091 </property> 2428 </property>
1092 <property name="maximumSize"> 2429 <property name="leftMargin">
1093 <size> 2430 <number>0</number>
1094 <width>80</width> 2431 </property>
1095 <height>16777215</height> 2432 <property name="topMargin">
1096 </size> 2433 <number>0</number>
1097 </property> 2434 </property>
1098 <property name="text"> 2435 <property name="rightMargin">
1099 <string/> 2436 <number>0</number>
1100 </property> 2437 </property>
1101 </widget> 2438 <property name="bottomMargin">
1102 </item> 2439 <number>0</number>
1103 </layout> 2440 </property>
2441 <item>
2442 <spacer name="horizontalSpacerRStickUpLeft">
2443 <property name="orientation">
2444 <enum>Qt::Horizontal</enum>
2445 </property>
2446 <property name="sizeHint" stdset="0">
2447 <size>
2448 <width>20</width>
2449 <height>20</height>
2450 </size>
2451 </property>
2452 </spacer>
2453 </item>
2454 <item alignment="Qt::AlignHCenter">
2455 <widget class="QGroupBox" name="buttonRStickUpGroup">
2456 <property name="title">
2457 <string>Up</string>
2458 </property>
2459 <property name="alignment">
2460 <set>Qt::AlignCenter</set>
2461 </property>
2462 <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
2463 <property name="spacing">
2464 <number>3</number>
2465 </property>
2466 <property name="leftMargin">
2467 <number>3</number>
2468 </property>
2469 <property name="topMargin">
2470 <number>3</number>
2471 </property>
2472 <property name="rightMargin">
2473 <number>3</number>
2474 </property>
2475 <property name="bottomMargin">
2476 <number>3</number>
2477 </property>
2478 <item>
2479 <widget class="QPushButton" name="buttonRStickUp">
2480 <property name="minimumSize">
2481 <size>
2482 <width>57</width>
2483 <height>0</height>
2484 </size>
2485 </property>
2486 <property name="maximumSize">
2487 <size>
2488 <width>55</width>
2489 <height>16777215</height>
2490 </size>
2491 </property>
2492 <property name="styleSheet">
2493 <string notr="true">min-width: 55px;</string>
2494 </property>
2495 <property name="text">
2496 <string>Up</string>
2497 </property>
2498 </widget>
2499 </item>
2500 </layout>
2501 </widget>
2502 </item>
2503 <item>
2504 <spacer name="horizontalSpacerRStickUpRight">
2505 <property name="orientation">
2506 <enum>Qt::Horizontal</enum>
2507 </property>
2508 <property name="sizeHint" stdset="0">
2509 <size>
2510 <width>20</width>
2511 <height>20</height>
2512 </size>
2513 </property>
2514 </spacer>
2515 </item>
2516 </layout>
2517 </widget>
2518 </item>
2519 <item>
2520 <layout class="QHBoxLayout" name="buttonRStickLeftRightHorizontalLayout">
2521 <property name="spacing">
2522 <number>3</number>
2523 </property>
2524 <item alignment="Qt::AlignHCenter">
2525 <widget class="QGroupBox" name="buttonRStickLeftGroup">
2526 <property name="title">
2527 <string>Left</string>
2528 </property>
2529 <property name="alignment">
2530 <set>Qt::AlignCenter</set>
2531 </property>
2532 <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
2533 <property name="spacing">
2534 <number>3</number>
2535 </property>
2536 <property name="leftMargin">
2537 <number>3</number>
2538 </property>
2539 <property name="topMargin">
2540 <number>3</number>
2541 </property>
2542 <property name="rightMargin">
2543 <number>3</number>
2544 </property>
2545 <property name="bottomMargin">
2546 <number>3</number>
2547 </property>
2548 <item>
2549 <widget class="QPushButton" name="buttonRStickLeft">
2550 <property name="minimumSize">
2551 <size>
2552 <width>57</width>
2553 <height>0</height>
2554 </size>
2555 </property>
2556 <property name="maximumSize">
2557 <size>
2558 <width>55</width>
2559 <height>16777215</height>
2560 </size>
2561 </property>
2562 <property name="styleSheet">
2563 <string notr="true">min-width: 55px;</string>
2564 </property>
2565 <property name="text">
2566 <string>Left</string>
2567 </property>
2568 </widget>
2569 </item>
2570 </layout>
2571 </widget>
2572 </item>
2573 <item alignment="Qt::AlignHCenter">
2574 <widget class="QGroupBox" name="buttonRStickRightGroup">
2575 <property name="title">
2576 <string>Right</string>
2577 </property>
2578 <property name="alignment">
2579 <set>Qt::AlignCenter</set>
2580 </property>
2581 <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
2582 <property name="spacing">
2583 <number>3</number>
2584 </property>
2585 <property name="leftMargin">
2586 <number>3</number>
2587 </property>
2588 <property name="topMargin">
2589 <number>3</number>
2590 </property>
2591 <property name="rightMargin">
2592 <number>3</number>
2593 </property>
2594 <property name="bottomMargin">
2595 <number>3</number>
2596 </property>
2597 <item>
2598 <widget class="QPushButton" name="buttonRStickRight">
2599 <property name="minimumSize">
2600 <size>
2601 <width>57</width>
2602 <height>0</height>
2603 </size>
2604 </property>
2605 <property name="maximumSize">
2606 <size>
2607 <width>55</width>
2608 <height>16777215</height>
2609 </size>
2610 </property>
2611 <property name="styleSheet">
2612 <string notr="true">min-width: 55px;</string>
2613 </property>
2614 <property name="text">
2615 <string>Right</string>
2616 </property>
2617 </widget>
2618 </item>
2619 </layout>
2620 </widget>
2621 </item>
2622 </layout>
2623 </item>
2624 <item>
2625 <widget class="QWidget" name="buttonRStickDownWidget" native="true">
2626 <layout class="QHBoxLayout" name="horizontalLayout_11">
2627 <property name="spacing">
2628 <number>0</number>
2629 </property>
2630 <property name="leftMargin">
2631 <number>0</number>
2632 </property>
2633 <property name="topMargin">
2634 <number>0</number>
2635 </property>
2636 <property name="rightMargin">
2637 <number>0</number>
2638 </property>
2639 <property name="bottomMargin">
2640 <number>0</number>
2641 </property>
2642 <item>
2643 <spacer name="horizontalSpacerRStickDownLeft">
2644 <property name="orientation">
2645 <enum>Qt::Horizontal</enum>
2646 </property>
2647 <property name="sizeHint" stdset="0">
2648 <size>
2649 <width>20</width>
2650 <height>20</height>
2651 </size>
2652 </property>
2653 </spacer>
2654 </item>
2655 <item>
2656 <widget class="QGroupBox" name="buttonRStickDownGroup">
2657 <property name="title">
2658 <string>Down</string>
2659 </property>
2660 <property name="alignment">
2661 <set>Qt::AlignCenter</set>
2662 </property>
2663 <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout">
2664 <property name="spacing">
2665 <number>3</number>
2666 </property>
2667 <property name="leftMargin">
2668 <number>3</number>
2669 </property>
2670 <property name="topMargin">
2671 <number>3</number>
2672 </property>
2673 <property name="rightMargin">
2674 <number>3</number>
2675 </property>
2676 <property name="bottomMargin">
2677 <number>3</number>
2678 </property>
2679 <item>
2680 <widget class="QPushButton" name="buttonRStickDown">
2681 <property name="minimumSize">
2682 <size>
2683 <width>57</width>
2684 <height>0</height>
2685 </size>
2686 </property>
2687 <property name="maximumSize">
2688 <size>
2689 <width>55</width>
2690 <height>16777215</height>
2691 </size>
2692 </property>
2693 <property name="styleSheet">
2694 <string notr="true">min-width: 55px;</string>
2695 </property>
2696 <property name="text">
2697 <string>Down</string>
2698 </property>
2699 </widget>
2700 </item>
2701 </layout>
2702 </widget>
2703 </item>
2704 <item>
2705 <spacer name="horizontalSpacerRStickDownRight">
2706 <property name="orientation">
2707 <enum>Qt::Horizontal</enum>
2708 </property>
2709 <property name="sizeHint" stdset="0">
2710 <size>
2711 <width>20</width>
2712 <height>20</height>
2713 </size>
2714 </property>
2715 </spacer>
2716 </item>
2717 </layout>
2718 </widget>
2719 </item>
2720 <item>
2721 <layout class="QHBoxLayout" name="buttonRStickPressedModifierHorizontalLayout">
2722 <property name="spacing">
2723 <number>3</number>
2724 </property>
2725 <item alignment="Qt::AlignHCenter">
2726 <widget class="QGroupBox" name="groupRStickPressed">
2727 <property name="title">
2728 <string>Pressed</string>
2729 </property>
2730 <property name="alignment">
2731 <set>Qt::AlignCenter</set>
2732 </property>
2733 <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
2734 <property name="spacing">
2735 <number>3</number>
2736 </property>
2737 <property name="leftMargin">
2738 <number>3</number>
2739 </property>
2740 <property name="topMargin">
2741 <number>3</number>
2742 </property>
2743 <property name="rightMargin">
2744 <number>3</number>
2745 </property>
2746 <property name="bottomMargin">
2747 <number>3</number>
2748 </property>
2749 <item>
2750 <widget class="QPushButton" name="buttonRStick">
2751 <property name="minimumSize">
2752 <size>
2753 <width>57</width>
2754 <height>0</height>
2755 </size>
2756 </property>
2757 <property name="maximumSize">
2758 <size>
2759 <width>55</width>
2760 <height>16777215</height>
2761 </size>
2762 </property>
2763 <property name="styleSheet">
2764 <string notr="true">min-width: 55px;</string>
2765 </property>
2766 <property name="text">
2767 <string>Pressed</string>
2768 </property>
2769 </widget>
2770 </item>
2771 </layout>
2772 </widget>
2773 </item>
2774 <item alignment="Qt::AlignHCenter">
2775 <widget class="QGroupBox" name="buttonRStickModGroup">
2776 <property name="title">
2777 <string>Modifier</string>
2778 </property>
2779 <property name="alignment">
2780 <set>Qt::AlignCenter</set>
2781 </property>
2782 <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout">
2783 <property name="spacing">
2784 <number>3</number>
2785 </property>
2786 <property name="leftMargin">
2787 <number>3</number>
2788 </property>
2789 <property name="topMargin">
2790 <number>3</number>
2791 </property>
2792 <property name="rightMargin">
2793 <number>3</number>
2794 </property>
2795 <property name="bottomMargin">
2796 <number>3</number>
2797 </property>
2798 <item>
2799 <widget class="QPushButton" name="buttonRStickMod">
2800 <property name="minimumSize">
2801 <size>
2802 <width>57</width>
2803 <height>0</height>
2804 </size>
2805 </property>
2806 <property name="maximumSize">
2807 <size>
2808 <width>55</width>
2809 <height>16777215</height>
2810 </size>
2811 </property>
2812 <property name="styleSheet">
2813 <string notr="true">min-width: 55px;</string>
2814 </property>
2815 <property name="text">
2816 <string>Modifier</string>
2817 </property>
2818 </widget>
2819 </item>
2820 </layout>
2821 </widget>
2822 </item>
2823 <item>
2824 <widget class="QGroupBox" name="buttonRStickRangeGroup">
2825 <property name="title">
2826 <string>Range</string>
2827 </property>
2828 <layout class="QHBoxLayout" name="buttonRStickRangeGroupHorizontalLayout">
2829 <property name="spacing">
2830 <number>3</number>
2831 </property>
2832 <property name="leftMargin">
2833 <number>3</number>
2834 </property>
2835 <property name="topMargin">
2836 <number>3</number>
2837 </property>
2838 <property name="rightMargin">
2839 <number>3</number>
2840 </property>
2841 <property name="bottomMargin">
2842 <number>3</number>
2843 </property>
2844 <item>
2845 <widget class="QSpinBox" name="spinboxRStickRange">
2846 <property name="minimumSize">
2847 <size>
2848 <width>55</width>
2849 <height>21</height>
2850 </size>
2851 </property>
2852 <property name="maximumSize">
2853 <size>
2854 <width>55</width>
2855 <height>16777215</height>
2856 </size>
2857 </property>
2858 <property name="suffix">
2859 <string>%</string>
2860 </property>
2861 <property name="minimum">
2862 <number>50</number>
2863 </property>
2864 <property name="maximum">
2865 <number>150</number>
2866 </property>
2867 <property name="value">
2868 <number>100</number>
2869 </property>
2870 </widget>
2871 </item>
2872 </layout>
2873 </widget>
2874 </item>
2875 </layout>
2876 </item>
2877 <item>
2878 <layout class="QVBoxLayout" name="sliderRStickDeadzoneModifierRangeVerticalLayout">
2879 <property name="spacing">
2880 <number>3</number>
2881 </property>
2882 <property name="leftMargin">
2883 <number>0</number>
2884 </property>
2885 <property name="topMargin">
2886 <number>2</number>
2887 </property>
2888 <property name="rightMargin">
2889 <number>0</number>
2890 </property>
2891 <property name="bottomMargin">
2892 <number>3</number>
2893 </property>
2894 <item>
2895 <layout class="QHBoxLayout" name="sliderRStickDeadzoneHorizontalLayout">
2896 <item>
2897 <widget class="QLabel" name="labelRStickDeadzone">
2898 <property name="text">
2899 <string>Deadzone: 0%</string>
2900 </property>
2901 <property name="alignment">
2902 <set>Qt::AlignHCenter</set>
2903 </property>
2904 </widget>
2905 </item>
2906 </layout>
2907 </item>
2908 <item>
2909 <widget class="QSlider" name="sliderRStickDeadzone">
2910 <property name="maximum">
2911 <number>100</number>
2912 </property>
2913 <property name="orientation">
2914 <enum>Qt::Horizontal</enum>
2915 </property>
2916 </widget>
2917 </item>
2918 <item>
2919 <layout class="QHBoxLayout" name="sliderRStickModifierRangeHorizontalLayout">
2920 <item>
2921 <widget class="QLabel" name="labelRStickModifierRange">
2922 <property name="text">
2923 <string>Modifier Range: 0%</string>
2924 </property>
2925 <property name="alignment">
2926 <set>Qt::AlignHCenter</set>
2927 </property>
2928 </widget>
2929 </item>
2930 </layout>
2931 </item>
2932 <item>
2933 <widget class="QSlider" name="sliderRStickModifierRange">
2934 <property name="maximum">
2935 <number>100</number>
2936 </property>
2937 <property name="orientation">
2938 <enum>Qt::Horizontal</enum>
2939 </property>
2940 </widget>
2941 </item>
2942 </layout>
2943 </item>
2944 </layout>
2945 </widget>
2946 </item>
2947 <item>
2948 <spacer name="verticalSpacerBottomRight_2">
2949 <property name="orientation">
2950 <enum>Qt::Vertical</enum>
2951 </property>
2952 <property name="sizeHint" stdset="0">
2953 <size>
2954 <width>20</width>
2955 <height>0</height>
2956 </size>
2957 </property>
2958 </spacer>
2959 </item>
2960 </layout>
2961 </widget>
1104 </item> 2962 </item>
1105 </layout> 2963 </layout>
1106 </widget> 2964 </widget>
1107 </item> 2965 </item>
1108 </layout> 2966 </layout>
1109 </item> 2967 </item>
1110 <item>
1111 <spacer name="verticalSpacer">
1112 <property name="orientation">
1113 <enum>Qt::Vertical</enum>
1114 </property>
1115 <property name="sizeHint" stdset="0">
1116 <size>
1117 <width>20</width>
1118 <height>40</height>
1119 </size>
1120 </property>
1121 </spacer>
1122 </item>
1123 <item>
1124 <layout class="QHBoxLayout" name="horizontalLayout"/>
1125 </item>
1126 <item>
1127 <layout class="QHBoxLayout" name="horizontalLayout_2">
1128 <item>
1129 <widget class="QPushButton" name="buttonClearAll">
1130 <property name="sizePolicy">
1131 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
1132 <horstretch>0</horstretch>
1133 <verstretch>0</verstretch>
1134 </sizepolicy>
1135 </property>
1136 <property name="sizeIncrement">
1137 <size>
1138 <width>0</width>
1139 <height>0</height>
1140 </size>
1141 </property>
1142 <property name="baseSize">
1143 <size>
1144 <width>0</width>
1145 <height>0</height>
1146 </size>
1147 </property>
1148 <property name="layoutDirection">
1149 <enum>Qt::LeftToRight</enum>
1150 </property>
1151 <property name="text">
1152 <string>Clear All</string>
1153 </property>
1154 </widget>
1155 </item>
1156 <item>
1157 <widget class="QPushButton" name="buttonRestoreDefaults">
1158 <property name="sizePolicy">
1159 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
1160 <horstretch>0</horstretch>
1161 <verstretch>0</verstretch>
1162 </sizepolicy>
1163 </property>
1164 <property name="sizeIncrement">
1165 <size>
1166 <width>0</width>
1167 <height>0</height>
1168 </size>
1169 </property>
1170 <property name="baseSize">
1171 <size>
1172 <width>0</width>
1173 <height>0</height>
1174 </size>
1175 </property>
1176 <property name="layoutDirection">
1177 <enum>Qt::LeftToRight</enum>
1178 </property>
1179 <property name="text">
1180 <string>Restore Defaults</string>
1181 </property>
1182 </widget>
1183 </item>
1184 <item>
1185 <spacer name="horizontalSpacer">
1186 <property name="orientation">
1187 <enum>Qt::Horizontal</enum>
1188 </property>
1189 <property name="sizeHint" stdset="0">
1190 <size>
1191 <width>40</width>
1192 <height>20</height>
1193 </size>
1194 </property>
1195 </spacer>
1196 </item>
1197 <item>
1198 <widget class="QDialogButtonBox" name="buttonBox">
1199 <property name="standardButtons">
1200 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
1201 </property>
1202 </widget>
1203 </item>
1204 </layout>
1205 </item>
1206 </layout> 2968 </layout>
1207 </widget> 2969 </widget>
1208 <resources/> 2970 <resources>
1209 <connections> 2971 <include location="../../../dist/icons/controller/controller.qrc"/>
1210 <connection> 2972 </resources>
1211 <sender>buttonBox</sender> 2973 <connections/>
1212 <signal>accepted()</signal>
1213 <receiver>ConfigureInputPlayer</receiver>
1214 <slot>accept()</slot>
1215 <hints>
1216 <hint type="sourcelabel">
1217 <x>371</x>
1218 <y>730</y>
1219 </hint>
1220 <hint type="destinationlabel">
1221 <x>229</x>
1222 <y>375</y>
1223 </hint>
1224 </hints>
1225 </connection>
1226 <connection>
1227 <sender>buttonBox</sender>
1228 <signal>rejected()</signal>
1229 <receiver>ConfigureInputPlayer</receiver>
1230 <slot>reject()</slot>
1231 <hints>
1232 <hint type="sourcelabel">
1233 <x>371</x>
1234 <y>730</y>
1235 </hint>
1236 <hint type="destinationlabel">
1237 <x>229</x>
1238 <y>375</y>
1239 </hint>
1240 </hints>
1241 </connection>
1242 </connections>
1243</ui> 2974</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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <tuple>
7
8#include "ui_configure_input_simple.h"
9#include "yuzu/configuration/configure_input.h"
10#include "yuzu/configuration/configure_input_player.h"
11#include "yuzu/configuration/configure_input_simple.h"
12#include "yuzu/uisettings.h"
13
14namespace {
15
16template <typename Dialog, typename... Args>
17void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) {
18 caller->ApplyConfiguration();
19 Dialog dialog(caller, std::forward<Args>(args)...);
20
21 const auto res = dialog.exec();
22 if (res == QDialog::Accepted) {
23 dialog.ApplyConfiguration();
24 }
25}
26
27// OnProfileSelect functions should (when applicable):
28// - Set controller types
29// - Set controller enabled
30// - Set docked mode
31// - Set advanced controller config/enabled (i.e. debug, kbd, mouse, touch)
32//
33// OnProfileSelect function should NOT however:
34// - Reset any button mappings
35// - Open any dialogs
36// - Block in any way
37
38constexpr std::size_t PLAYER_0_INDEX = 0;
39constexpr std::size_t HANDHELD_INDEX = 8;
40
41void HandheldOnProfileSelect() {
42 Settings::values.players[HANDHELD_INDEX].connected = true;
43 Settings::values.players[HANDHELD_INDEX].type = Settings::ControllerType::DualJoycon;
44
45 for (std::size_t player = 0; player < HANDHELD_INDEX; ++player) {
46 Settings::values.players[player].connected = false;
47 }
48
49 Settings::values.use_docked_mode = false;
50 Settings::values.keyboard_enabled = false;
51 Settings::values.mouse_enabled = false;
52 Settings::values.debug_pad_enabled = false;
53 Settings::values.touchscreen.enabled = true;
54}
55
56void DualJoyconsDockedOnProfileSelect() {
57 Settings::values.players[PLAYER_0_INDEX].connected = true;
58 Settings::values.players[PLAYER_0_INDEX].type = Settings::ControllerType::DualJoycon;
59
60 for (std::size_t player = 1; player <= HANDHELD_INDEX; ++player) {
61 Settings::values.players[player].connected = false;
62 }
63
64 Settings::values.use_docked_mode = true;
65 Settings::values.keyboard_enabled = false;
66 Settings::values.mouse_enabled = false;
67 Settings::values.debug_pad_enabled = false;
68 Settings::values.touchscreen.enabled = true;
69}
70
71// Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure
72// is clicked)
73using InputProfile = std::tuple<const char*, void (*)(), void (*)(ConfigureInputSimple*)>;
74
75constexpr std::array<InputProfile, 3> INPUT_PROFILES{{
76 {QT_TR_NOOP("Single Player - Handheld - Undocked"), HandheldOnProfileSelect,
77 [](ConfigureInputSimple* caller) {
78 CallConfigureDialog<ConfigureInputPlayer>(caller, HANDHELD_INDEX, false);
79 }},
80 {QT_TR_NOOP("Single Player - Dual Joycons - Docked"), DualJoyconsDockedOnProfileSelect,
81 [](ConfigureInputSimple* caller) {
82 CallConfigureDialog<ConfigureInputPlayer>(caller, PLAYER_0_INDEX, false);
83 }},
84 {QT_TR_NOOP("Custom"), [] {}, CallConfigureDialog<ConfigureInput>},
85}};
86
87} // namespace
88
89void ApplyInputProfileConfiguration(int profile_index) {
90 std::get<1>(
91 INPUT_PROFILES.at(std::min(profile_index, static_cast<int>(INPUT_PROFILES.size() - 1))))();
92}
93
94ConfigureInputSimple::ConfigureInputSimple(QWidget* parent)
95 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputSimple>()) {
96 ui->setupUi(this);
97
98 for (const auto& profile : INPUT_PROFILES) {
99 const QString label = tr(std::get<0>(profile));
100 ui->profile_combobox->addItem(label, label);
101 }
102
103 connect(ui->profile_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
104 &ConfigureInputSimple::OnSelectProfile);
105 connect(ui->profile_configure, &QPushButton::clicked, this, &ConfigureInputSimple::OnConfigure);
106
107 LoadConfiguration();
108}
109
110ConfigureInputSimple::~ConfigureInputSimple() = default;
111
112void ConfigureInputSimple::ApplyConfiguration() {
113 auto index = ui->profile_combobox->currentIndex();
114 // Make the stored index for "Custom" very large so that if new profiles are added it
115 // doesn't change.
116 if (index >= static_cast<int>(INPUT_PROFILES.size() - 1)) {
117 index = std::numeric_limits<int>::max();
118 }
119
120 UISettings::values.profile_index = index;
121}
122
123void ConfigureInputSimple::changeEvent(QEvent* event) {
124 if (event->type() == QEvent::LanguageChange) {
125 RetranslateUI();
126 }
127
128 QWidget::changeEvent(event);
129}
130
131void ConfigureInputSimple::RetranslateUI() {
132 ui->retranslateUi(this);
133}
134
135void ConfigureInputSimple::LoadConfiguration() {
136 const auto index = UISettings::values.profile_index;
137 if (index >= static_cast<int>(INPUT_PROFILES.size()) || index < 0) {
138 ui->profile_combobox->setCurrentIndex(static_cast<int>(INPUT_PROFILES.size() - 1));
139 } else {
140 ui->profile_combobox->setCurrentIndex(index);
141 }
142}
143
144void ConfigureInputSimple::OnSelectProfile(int index) {
145 const auto old_docked = Settings::values.use_docked_mode;
146 ApplyInputProfileConfiguration(index);
147 OnDockedModeChanged(old_docked, Settings::values.use_docked_mode);
148}
149
150void ConfigureInputSimple::OnConfigure() {
151 std::get<2>(INPUT_PROFILES.at(ui->profile_combobox->currentIndex()))(this);
152}
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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9#include <QWidget>
10
11class QPushButton;
12class QString;
13class QTimer;
14
15namespace Ui {
16class ConfigureInputSimple;
17}
18
19// Used by configuration loader to apply a profile if the input is invalid.
20void ApplyInputProfileConfiguration(int profile_index);
21
22class ConfigureInputSimple : public QWidget {
23 Q_OBJECT
24
25public:
26 explicit ConfigureInputSimple(QWidget* parent = nullptr);
27 ~ConfigureInputSimple() override;
28
29 /// Save all button configurations to settings file
30 void ApplyConfiguration();
31
32private:
33 void changeEvent(QEvent* event) override;
34 void RetranslateUI();
35
36 /// Load configuration settings.
37 void LoadConfiguration();
38
39 void OnSelectProfile(int index);
40 void OnConfigure();
41
42 std::unique_ptr<Ui::ConfigureInputSimple> ui;
43};
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 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureInputSimple</class>
4 <widget class="QWidget" name="ConfigureInputSimple">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>473</width>
10 <height>685</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>ConfigureInputSimple</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5">
17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout">
19 <item>
20 <widget class="QGroupBox" name="gridGroupBox">
21 <property name="title">
22 <string>Profile</string>
23 </property>
24 <layout class="QGridLayout" name="gridLayout">
25 <item row="1" column="2">
26 <widget class="QPushButton" name="profile_configure">
27 <property name="text">
28 <string>Configure</string>
29 </property>
30 </widget>
31 </item>
32 <item row="1" column="0">
33 <spacer name="horizontalSpacer">
34 <property name="orientation">
35 <enum>Qt::Horizontal</enum>
36 </property>
37 <property name="sizeHint" stdset="0">
38 <size>
39 <width>40</width>
40 <height>20</height>
41 </size>
42 </property>
43 </spacer>
44 </item>
45 <item row="1" column="3">
46 <spacer name="horizontalSpacer_2">
47 <property name="orientation">
48 <enum>Qt::Horizontal</enum>
49 </property>
50 <property name="sizeHint" stdset="0">
51 <size>
52 <width>40</width>
53 <height>20</height>
54 </size>
55 </property>
56 </spacer>
57 </item>
58 <item row="1" column="1">
59 <widget class="QComboBox" name="profile_combobox">
60 <property name="minimumSize">
61 <size>
62 <width>250</width>
63 <height>0</height>
64 </size>
65 </property>
66 </widget>
67 </item>
68 <item row="0" column="1" colspan="2">
69 <widget class="QLabel" name="label">
70 <property name="text">
71 <string>Choose a controller configuration:</string>
72 </property>
73 </widget>
74 </item>
75 </layout>
76 </widget>
77 </item>
78 </layout>
79 </item>
80 <item>
81 <spacer name="verticalSpacer">
82 <property name="orientation">
83 <enum>Qt::Vertical</enum>
84 </property>
85 <property name="sizeHint" stdset="0">
86 <size>
87 <width>20</width>
88 <height>40</height>
89 </size>
90 </property>
91 </spacer>
92 </item>
93 </layout>
94 </widget>
95 <resources/>
96 <connections/>
97</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 @@
18 18
19static QString GetKeyName(int key_code) { 19static QString GetKeyName(int key_code) {
20 switch (key_code) { 20 switch (key_code) {
21 case Qt::LeftButton:
22 return QObject::tr("Click 0");
23 case Qt::RightButton:
24 return QObject::tr("Click 1");
25 case Qt::MiddleButton:
26 return QObject::tr("Click 2");
27 case Qt::BackButton:
28 return QObject::tr("Click 3");
29 case Qt::ForwardButton:
30 return QObject::tr("Click 4");
21 case Qt::Key_Shift: 31 case Qt::Key_Shift:
22 return QObject::tr("Shift"); 32 return QObject::tr("Shift");
23 case Qt::Key_Control: 33 case Qt::Key_Control:
@@ -66,8 +76,10 @@ static QString ButtonToText(const Common::ParamPackage& param) {
66 return QObject::tr("[unknown]"); 76 return QObject::tr("[unknown]");
67} 77}
68 78
69ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) 79ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent,
70 : QDialog(parent), ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), 80 InputCommon::InputSubsystem* input_subsystem_)
81 : QDialog(parent),
82 ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), input_subsystem{input_subsystem_},
71 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) { 83 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
72 ui->setupUi(this); 84 ui->setupUi(this);
73 setFocusPolicy(Qt::ClickFocus); 85 setFocusPolicy(Qt::ClickFocus);
@@ -84,11 +96,12 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
84 96
85 button->setContextMenuPolicy(Qt::CustomContextMenu); 97 button->setContextMenuPolicy(Qt::CustomContextMenu);
86 connect(button, &QPushButton::clicked, [=, this] { 98 connect(button, &QPushButton::clicked, [=, this] {
87 HandleClick(button_map[button_id], 99 HandleClick(
88 [=, this](const Common::ParamPackage& params) { 100 button_map[button_id],
89 buttons_param[button_id] = params; 101 [=, this](const Common::ParamPackage& params) {
90 }, 102 buttons_param[button_id] = params;
91 InputCommon::Polling::DeviceType::Button); 103 },
104 InputCommon::Polling::DeviceType::Button);
92 }); 105 });
93 connect(button, &QPushButton::customContextMenuRequested, 106 connect(button, &QPushButton::customContextMenuRequested,
94 [=, this](const QPoint& menu_location) { 107 [=, this](const QPoint& menu_location) {
@@ -187,9 +200,9 @@ void ConfigureMouseAdvanced::HandleClick(
187 button->setText(tr("[press key]")); 200 button->setText(tr("[press key]"));
188 button->setFocus(); 201 button->setFocus();
189 202
190 // Keyboard keys can only be used as button devices 203 // Keyboard keys or mouse buttons can only be used as button devices
191 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; 204 want_keyboard_mouse = type == InputCommon::Polling::DeviceType::Button;
192 if (want_keyboard_keys) { 205 if (want_keyboard_mouse) {
193 const auto iter = std::find(button_map.begin(), button_map.end(), button); 206 const auto iter = std::find(button_map.begin(), button_map.end(), button);
194 ASSERT(iter != button_map.end()); 207 ASSERT(iter != button_map.end());
195 const auto index = std::distance(button_map.begin(), iter); 208 const auto index = std::distance(button_map.begin(), iter);
@@ -198,27 +211,29 @@ void ConfigureMouseAdvanced::HandleClick(
198 211
199 input_setter = new_input_setter; 212 input_setter = new_input_setter;
200 213
201 device_pollers = InputCommon::Polling::GetPollers(type); 214 device_pollers = input_subsystem->GetPollers(type);
202 215
203 for (auto& poller : device_pollers) { 216 for (auto& poller : device_pollers) {
204 poller->Start(); 217 poller->Start();
205 } 218 }
206 219
207 grabKeyboard(); 220 QWidget::grabMouse();
208 grabMouse(); 221 QWidget::grabKeyboard();
209 timeout_timer->start(5000); // Cancel after 5 seconds 222
210 poll_timer->start(200); // Check for new inputs every 200ms 223 timeout_timer->start(2500); // Cancel after 2.5 seconds
224 poll_timer->start(50); // Check for new inputs every 50ms
211} 225}
212 226
213void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) { 227void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) {
214 releaseKeyboard();
215 releaseMouse();
216 timeout_timer->stop(); 228 timeout_timer->stop();
217 poll_timer->stop(); 229 poll_timer->stop();
218 for (auto& poller : device_pollers) { 230 for (auto& poller : device_pollers) {
219 poller->Stop(); 231 poller->Stop();
220 } 232 }
221 233
234 QWidget::releaseMouse();
235 QWidget::releaseKeyboard();
236
222 if (!abort) { 237 if (!abort) {
223 (*input_setter)(params); 238 (*input_setter)(params);
224 } 239 }
@@ -227,13 +242,29 @@ void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params
227 input_setter = std::nullopt; 242 input_setter = std::nullopt;
228} 243}
229 244
245void ConfigureMouseAdvanced::mousePressEvent(QMouseEvent* event) {
246 if (!input_setter || !event) {
247 return;
248 }
249
250 if (want_keyboard_mouse) {
251 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
252 false);
253 } else {
254 // We don't want any mouse buttons, so don't stop polling
255 return;
256 }
257
258 SetPollingResult({}, true);
259}
260
230void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) { 261void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
231 if (!input_setter || !event) { 262 if (!input_setter || !event) {
232 return; 263 return;
233 } 264 }
234 265
235 if (event->key() != Qt::Key_Escape) { 266 if (event->key() != Qt::Key_Escape) {
236 if (want_keyboard_keys) { 267 if (want_keyboard_mouse) {
237 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, 268 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
238 false); 269 false);
239 } else { 270 } 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 @@
8#include <optional> 8#include <optional>
9#include <QDialog> 9#include <QDialog>
10 10
11#include "core/settings.h"
12
13class QCheckBox; 11class QCheckBox;
14class QPushButton; 12class QPushButton;
15class QTimer; 13class QTimer;
16 14
15namespace InputCommon {
16class InputSubsystem;
17}
18
17namespace Ui { 19namespace Ui {
18class ConfigureMouseAdvanced; 20class ConfigureMouseAdvanced;
19} 21}
@@ -22,7 +24,7 @@ class ConfigureMouseAdvanced : public QDialog {
22 Q_OBJECT 24 Q_OBJECT
23 25
24public: 26public:
25 explicit ConfigureMouseAdvanced(QWidget* parent); 27 explicit ConfigureMouseAdvanced(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_);
26 ~ConfigureMouseAdvanced() override; 28 ~ConfigureMouseAdvanced() override;
27 29
28 void ApplyConfiguration(); 30 void ApplyConfiguration();
@@ -49,11 +51,16 @@ private:
49 /// Finish polling and configure input using the input_setter 51 /// Finish polling and configure input using the input_setter
50 void SetPollingResult(const Common::ParamPackage& params, bool abort); 52 void SetPollingResult(const Common::ParamPackage& params, bool abort);
51 53
54 /// Handle mouse button press events.
55 void mousePressEvent(QMouseEvent* event) override;
56
52 /// Handle key press events. 57 /// Handle key press events.
53 void keyPressEvent(QKeyEvent* event) override; 58 void keyPressEvent(QKeyEvent* event) override;
54 59
55 std::unique_ptr<Ui::ConfigureMouseAdvanced> ui; 60 std::unique_ptr<Ui::ConfigureMouseAdvanced> ui;
56 61
62 InputCommon::InputSubsystem* input_subsystem;
63
57 /// This will be the the setting function when an input is awaiting configuration. 64 /// This will be the the setting function when an input is awaiting configuration.
58 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; 65 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
59 66
@@ -67,5 +74,5 @@ private:
67 74
68 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, 75 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
69 /// keyboard events are ignored. 76 /// keyboard events are ignored.
70 bool want_keyboard_keys = false; 77 bool want_keyboard_mouse = false;
71}; 78};
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 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>250</width> 9 <width>310</width>
10 <height>261</height> 10 <height>193</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
14 <string>Configure Mouse</string> 14 <string>Configure Mouse</string>
15 </property> 15 </property>
16 <property name="styleSheet">
17 <string notr="true">QPushButton {
18 min-width: 55px;
19}</string>
20 </property>
16 <layout class="QVBoxLayout" name="verticalLayout"> 21 <layout class="QVBoxLayout" name="verticalLayout">
17 <item> 22 <item>
18 <widget class="QGroupBox" name="gridGroupBox"> 23 <widget class="QGroupBox" name="gridGroupBox">
@@ -20,81 +25,33 @@
20 <string>Mouse Buttons</string> 25 <string>Mouse Buttons</string>
21 </property> 26 </property>
22 <layout class="QGridLayout" name="gridLayout"> 27 <layout class="QGridLayout" name="gridLayout">
23 <item row="0" column="4"> 28 <item row="3" column="5">
24 <spacer name="horizontalSpacer_2"> 29 <layout class="QVBoxLayout" name="verticalLayout_6">
25 <property name="orientation">
26 <enum>Qt::Horizontal</enum>
27 </property>
28 <property name="sizeType">
29 <enum>QSizePolicy::Fixed</enum>
30 </property>
31 <property name="sizeHint" stdset="0">
32 <size>
33 <width>20</width>
34 <height>20</height>
35 </size>
36 </property>
37 </spacer>
38 </item>
39 <item row="0" column="3">
40 <layout class="QVBoxLayout" name="verticalLayout_4">
41 <item> 30 <item>
42 <layout class="QHBoxLayout" name="horizontalLayout_3"> 31 <layout class="QHBoxLayout" name="horizontalLayout_5">
43 <item> 32 <item>
44 <widget class="QLabel" name="label_3"> 33 <widget class="QLabel" name="label_5">
45 <property name="text"> 34 <property name="text">
46 <string>Right:</string> 35 <string>Forward:</string>
47 </property> 36 </property>
48 </widget> 37 </widget>
49 </item> 38 </item>
50 </layout> 39 </layout>
51 </item> 40 </item>
52 <item> 41 <item>
53 <widget class="QPushButton" name="right_button"> 42 <widget class="QPushButton" name="forward_button">
54 <property name="minimumSize"> 43 <property name="minimumSize">
55 <size> 44 <size>
56 <width>75</width> 45 <width>57</width>
57 <height>0</height> 46 <height>0</height>
58 </size> 47 </size>
59 </property> 48 </property>
60 <property name="text"> 49 <property name="maximumSize">
61 <string/> 50 <size>
51 <width>16777215</width>
52 <height>16777215</height>
53 </size>
62 </property> 54 </property>
63 </widget>
64 </item>
65 </layout>
66 </item>
67 <item row="0" column="0">
68 <spacer name="horizontalSpacer">
69 <property name="orientation">
70 <enum>Qt::Horizontal</enum>
71 </property>
72 <property name="sizeType">
73 <enum>QSizePolicy::Fixed</enum>
74 </property>
75 <property name="sizeHint" stdset="0">
76 <size>
77 <width>20</width>
78 <height>20</height>
79 </size>
80 </property>
81 </spacer>
82 </item>
83 <item row="2" column="1">
84 <layout class="QVBoxLayout" name="verticalLayout_3">
85 <item>
86 <layout class="QHBoxLayout" name="horizontalLayout_2">
87 <item>
88 <widget class="QLabel" name="label_2">
89 <property name="text">
90 <string>Middle:</string>
91 </property>
92 </widget>
93 </item>
94 </layout>
95 </item>
96 <item>
97 <widget class="QPushButton" name="middle_button">
98 <property name="text"> 55 <property name="text">
99 <string/> 56 <string/>
100 </property> 57 </property>
@@ -123,6 +80,12 @@
123 </item> 80 </item>
124 <item> 81 <item>
125 <widget class="QPushButton" name="back_button"> 82 <widget class="QPushButton" name="back_button">
83 <property name="minimumSize">
84 <size>
85 <width>57</width>
86 <height>0</height>
87 </size>
88 </property>
126 <property name="text"> 89 <property name="text">
127 <string/> 90 <string/>
128 </property> 91 </property>
@@ -147,7 +110,7 @@
147 <widget class="QPushButton" name="left_button"> 110 <widget class="QPushButton" name="left_button">
148 <property name="minimumSize"> 111 <property name="minimumSize">
149 <size> 112 <size>
150 <width>75</width> 113 <width>57</width>
151 <height>0</height> 114 <height>0</height>
152 </size> 115 </size>
153 </property> 116 </property>
@@ -158,21 +121,99 @@
158 </item> 121 </item>
159 </layout> 122 </layout>
160 </item> 123 </item>
161 <item row="3" column="3"> 124 <item row="0" column="3">
162 <layout class="QVBoxLayout" name="verticalLayout_6"> 125 <layout class="QVBoxLayout" name="verticalLayout_3">
163 <item> 126 <item>
164 <layout class="QHBoxLayout" name="horizontalLayout_5"> 127 <layout class="QHBoxLayout" name="horizontalLayout_2">
165 <item> 128 <item>
166 <widget class="QLabel" name="label_5"> 129 <widget class="QLabel" name="label_2">
167 <property name="text"> 130 <property name="text">
168 <string>Forward:</string> 131 <string>Middle:</string>
169 </property> 132 </property>
170 </widget> 133 </widget>
171 </item> 134 </item>
172 </layout> 135 </layout>
173 </item> 136 </item>
174 <item> 137 <item>
175 <widget class="QPushButton" name="forward_button"> 138 <widget class="QPushButton" name="middle_button">
139 <property name="minimumSize">
140 <size>
141 <width>57</width>
142 <height>0</height>
143 </size>
144 </property>
145 <property name="maximumSize">
146 <size>
147 <width>16777215</width>
148 <height>16777215</height>
149 </size>
150 </property>
151 <property name="text">
152 <string/>
153 </property>
154 </widget>
155 </item>
156 </layout>
157 </item>
158 <item row="0" column="6">
159 <spacer name="horizontalSpacer_2">
160 <property name="orientation">
161 <enum>Qt::Horizontal</enum>
162 </property>
163 <property name="sizeType">
164 <enum>QSizePolicy::Fixed</enum>
165 </property>
166 <property name="sizeHint" stdset="0">
167 <size>
168 <width>0</width>
169 <height>20</height>
170 </size>
171 </property>
172 </spacer>
173 </item>
174 <item row="0" column="0">
175 <spacer name="horizontalSpacer">
176 <property name="orientation">
177 <enum>Qt::Horizontal</enum>
178 </property>
179 <property name="sizeType">
180 <enum>QSizePolicy::Fixed</enum>
181 </property>
182 <property name="sizeHint" stdset="0">
183 <size>
184 <width>0</width>
185 <height>20</height>
186 </size>
187 </property>
188 </spacer>
189 </item>
190 <item row="0" column="5">
191 <layout class="QVBoxLayout" name="verticalLayout_4">
192 <item>
193 <layout class="QHBoxLayout" name="horizontalLayout_3">
194 <item>
195 <widget class="QLabel" name="label_3">
196 <property name="text">
197 <string>Right:</string>
198 </property>
199 </widget>
200 </item>
201 </layout>
202 </item>
203 <item>
204 <widget class="QPushButton" name="right_button">
205 <property name="minimumSize">
206 <size>
207 <width>57</width>
208 <height>0</height>
209 </size>
210 </property>
211 <property name="maximumSize">
212 <size>
213 <width>16777215</width>
214 <height>16777215</height>
215 </size>
216 </property>
176 <property name="text"> 217 <property name="text">
177 <string/> 218 <string/>
178 </property> 219 </property>
@@ -180,6 +221,32 @@
180 </item> 221 </item>
181 </layout> 222 </layout>
182 </item> 223 </item>
224 <item row="0" column="2">
225 <spacer name="horizontalSpacer_4">
226 <property name="orientation">
227 <enum>Qt::Horizontal</enum>
228 </property>
229 <property name="sizeHint" stdset="0">
230 <size>
231 <width>0</width>
232 <height>20</height>
233 </size>
234 </property>
235 </spacer>
236 </item>
237 <item row="0" column="4">
238 <spacer name="horizontalSpacer_5">
239 <property name="orientation">
240 <enum>Qt::Horizontal</enum>
241 </property>
242 <property name="sizeHint" stdset="0">
243 <size>
244 <width>0</width>
245 <height>20</height>
246 </size>
247 </property>
248 </spacer>
249 </item>
183 </layout> 250 </layout>
184 </widget> 251 </widget>
185 </item> 252 </item>
@@ -187,15 +254,39 @@
187 <layout class="QHBoxLayout" name="horizontalLayout_6"> 254 <layout class="QHBoxLayout" name="horizontalLayout_6">
188 <item> 255 <item>
189 <widget class="QPushButton" name="buttonClearAll"> 256 <widget class="QPushButton" name="buttonClearAll">
257 <property name="minimumSize">
258 <size>
259 <width>57</width>
260 <height>0</height>
261 </size>
262 </property>
263 <property name="maximumSize">
264 <size>
265 <width>16777215</width>
266 <height>16777215</height>
267 </size>
268 </property>
190 <property name="text"> 269 <property name="text">
191 <string>Clear All</string> 270 <string>Clear</string>
192 </property> 271 </property>
193 </widget> 272 </widget>
194 </item> 273 </item>
195 <item> 274 <item>
196 <widget class="QPushButton" name="buttonRestoreDefaults"> 275 <widget class="QPushButton" name="buttonRestoreDefaults">
276 <property name="minimumSize">
277 <size>
278 <width>57</width>
279 <height>0</height>
280 </size>
281 </property>
282 <property name="maximumSize">
283 <size>
284 <width>16777215</width>
285 <height>16777215</height>
286 </size>
287 </property>
197 <property name="text"> 288 <property name="text">
198 <string>Restore Defaults</string> 289 <string>Defaults</string>
199 </property> 290 </property>
200 </widget> 291 </widget>
201 </item> 292 </item>
@@ -206,21 +297,24 @@
206 </property> 297 </property>
207 <property name="sizeHint" stdset="0"> 298 <property name="sizeHint" stdset="0">
208 <size> 299 <size>
209 <width>40</width> 300 <width>0</width>
210 <height>20</height> 301 <height>20</height>
211 </size> 302 </size>
212 </property> 303 </property>
213 </spacer> 304 </spacer>
214 </item> 305 </item>
306 <item>
307 <widget class="QDialogButtonBox" name="buttonBox">
308 <property name="styleSheet">
309 <string notr="true"/>
310 </property>
311 <property name="standardButtons">
312 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
313 </property>
314 </widget>
315 </item>
215 </layout> 316 </layout>
216 </item> 317 </item>
217 <item>
218 <widget class="QDialogButtonBox" name="buttonBox">
219 <property name="standardButtons">
220 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
221 </property>
222 </widget>
223 </item>
224 </layout> 318 </layout>
225 </widget> 319 </widget>
226 <resources/> 320 <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() {
79 std::sort(disabled_addons.begin(), disabled_addons.end()); 79 std::sort(disabled_addons.begin(), disabled_addons.end());
80 std::sort(current.begin(), current.end()); 80 std::sort(current.begin(), current.end());
81 if (disabled_addons != current) { 81 if (disabled_addons != current) {
82 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + 82 Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
83 "game_list" + DIR_SEP + fmt::format("{:016X}.pv.txt", title_id)); 83 "game_list" + DIR_SEP + fmt::format("{:016X}.pv.txt", title_id));
84 } 84 }
85 85
86 Settings::values.disabled_addons[title_id] = disabled_addons; 86 Settings::values.disabled_addons[title_id] = disabled_addons;
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp
index f53423440..6334c4c50 100644
--- a/src/yuzu/configuration/configure_profile_manager.cpp
+++ b/src/yuzu/configuration/configure_profile_manager.cpp
@@ -34,7 +34,7 @@ constexpr std::array<u8, 107> backup_jpeg{
34}; 34};
35 35
36QString GetImagePath(Common::UUID uuid) { 36QString GetImagePath(Common::UUID uuid) {
37 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 37 const auto path = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
38 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; 38 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
39 return QString::fromStdString(path); 39 return QString::fromStdString(path);
40} 40}
@@ -282,7 +282,7 @@ void ConfigureProfileManager::SetUserImage() {
282 } 282 }
283 283
284 const auto raw_path = QString::fromStdString( 284 const auto raw_path = QString::fromStdString(
285 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"); 285 Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + "/system/save/8000000000000010");
286 const QFileInfo raw_info{raw_path}; 286 const QFileInfo raw_info{raw_path};
287 if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) { 287 if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) {
288 QMessageBox::warning(this, tr("Error deleting file"), 288 QMessageBox::warning(this, tr("Error deleting file"),
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 0c4daf147..9ad43ed8f 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -90,13 +90,13 @@ void ConfigureSystem::SetConfiguration() {
90 &Settings::values.time_zone_index); 90 &Settings::values.time_zone_index);
91 ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index); 91 ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index);
92 92
93 ConfigurationShared::SetHighlight(ui->label_language, "label_language", 93 ConfigurationShared::SetHighlight(ui->label_language,
94 !Settings::values.language_index.UsingGlobal()); 94 !Settings::values.language_index.UsingGlobal());
95 ConfigurationShared::SetHighlight(ui->label_region, "label_region", 95 ConfigurationShared::SetHighlight(ui->label_region,
96 !Settings::values.region_index.UsingGlobal()); 96 !Settings::values.region_index.UsingGlobal());
97 ConfigurationShared::SetHighlight(ui->label_timezone, "label_timezone", 97 ConfigurationShared::SetHighlight(ui->label_timezone,
98 !Settings::values.time_zone_index.UsingGlobal()); 98 !Settings::values.time_zone_index.UsingGlobal());
99 ConfigurationShared::SetHighlight(ui->label_sound, "label_sound", 99 ConfigurationShared::SetHighlight(ui->label_sound,
100 !Settings::values.sound_index.UsingGlobal()); 100 !Settings::values.sound_index.UsingGlobal());
101 } 101 }
102} 102}
@@ -224,22 +224,20 @@ void ConfigureSystem::SetupPerGameUI() {
224 } 224 }
225 225
226 ConfigurationShared::SetColoredComboBox(ui->combo_language, ui->label_language, 226 ConfigurationShared::SetColoredComboBox(ui->combo_language, ui->label_language,
227 "label_language",
228 Settings::values.language_index.GetValue(true)); 227 Settings::values.language_index.GetValue(true));
229 ConfigurationShared::SetColoredComboBox(ui->combo_region, ui->label_region, "label_region", 228 ConfigurationShared::SetColoredComboBox(ui->combo_region, ui->label_region,
230 Settings::values.region_index.GetValue(true)); 229 Settings::values.region_index.GetValue(true));
231 ConfigurationShared::SetColoredComboBox(ui->combo_time_zone, ui->label_timezone, 230 ConfigurationShared::SetColoredComboBox(ui->combo_time_zone, ui->label_timezone,
232 "label_timezone",
233 Settings::values.time_zone_index.GetValue(true)); 231 Settings::values.time_zone_index.GetValue(true));
234 ConfigurationShared::SetColoredComboBox(ui->combo_sound, ui->label_sound, "label_sound", 232 ConfigurationShared::SetColoredComboBox(ui->combo_sound, ui->label_sound,
235 Settings::values.sound_index.GetValue(true)); 233 Settings::values.sound_index.GetValue(true));
236 234
237 ConfigurationShared::SetColoredTristate( 235 ConfigurationShared::SetColoredTristate(
238 ui->rng_seed_checkbox, "rng_seed_checkbox", Settings::values.rng_seed.UsingGlobal(), 236 ui->rng_seed_checkbox, Settings::values.rng_seed.UsingGlobal(),
239 Settings::values.rng_seed.GetValue().has_value(), 237 Settings::values.rng_seed.GetValue().has_value(),
240 Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed); 238 Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed);
241 ConfigurationShared::SetColoredTristate( 239 ConfigurationShared::SetColoredTristate(
242 ui->custom_rtc_checkbox, "custom_rtc_checkbox", Settings::values.custom_rtc.UsingGlobal(), 240 ui->custom_rtc_checkbox, Settings::values.custom_rtc.UsingGlobal(),
243 Settings::values.custom_rtc.GetValue().has_value(), 241 Settings::values.custom_rtc.GetValue().has_value(),
244 Settings::values.custom_rtc.GetValue(true).has_value(), use_custom_rtc); 242 Settings::values.custom_rtc.GetValue(true).has_value(), use_custom_rtc);
245} 243}
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index 2c20b68d0..dbe3f78c8 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -61,9 +61,9 @@ ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::Configur
61 // Set screenshot path to user specification. 61 // Set screenshot path to user specification.
62 connect(ui->screenshot_path_button, &QToolButton::pressed, this, [this] { 62 connect(ui->screenshot_path_button, &QToolButton::pressed, this, [this] {
63 const QString& filename = 63 const QString& filename =
64 QFileDialog::getExistingDirectory( 64 QFileDialog::getExistingDirectory(this, tr("Select Screenshots Path..."),
65 this, tr("Select Screenshots Path..."), 65 QString::fromStdString(Common::FS::GetUserPath(
66 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))) + 66 Common::FS::UserPath::ScreenshotsDir))) +
67 QDir::separator(); 67 QDir::separator();
68 if (!filename.isEmpty()) { 68 if (!filename.isEmpty()) {
69 ui->screenshot_path_edit->setText(filename); 69 ui->screenshot_path_edit->setText(filename);
@@ -82,8 +82,8 @@ void ConfigureUi::ApplyConfiguration() {
82 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); 82 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
83 83
84 UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked(); 84 UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked();
85 FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir, 85 Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir,
86 ui->screenshot_path_edit->text().toStdString()); 86 ui->screenshot_path_edit->text().toStdString());
87 Settings::Apply(); 87 Settings::Apply();
88} 88}
89 89
@@ -101,7 +101,7 @@ void ConfigureUi::SetConfiguration() {
101 101
102 ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as); 102 ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as);
103 ui->screenshot_path_edit->setText( 103 ui->screenshot_path_edit->setText(
104 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))); 104 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir)));
105} 105}
106 106
107void ConfigureUi::changeEvent(QEvent* event) { 107void ConfigureUi::changeEvent(QEvent* event) {
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp
index 53049ffd6..0e26f765b 100644
--- a/src/yuzu/debugger/profiler.cpp
+++ b/src/yuzu/debugger/profiler.cpp
@@ -109,8 +109,7 @@ MicroProfileWidget::MicroProfileWidget(QWidget* parent) : QWidget(parent) {
109 MicroProfileSetDisplayMode(1); // Timers screen 109 MicroProfileSetDisplayMode(1); // Timers screen
110 MicroProfileInitUI(); 110 MicroProfileInitUI();
111 111
112 connect(&update_timer, &QTimer::timeout, this, 112 connect(&update_timer, &QTimer::timeout, this, qOverload<>(&MicroProfileWidget::update));
113 static_cast<void (MicroProfileWidget::*)()>(&MicroProfileWidget::update));
114} 113}
115 114
116void MicroProfileWidget::paintEvent(QPaintEvent* ev) { 115void MicroProfileWidget::paintEvent(QPaintEvent* ev) {
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 62acc3720..6a71d9644 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -406,7 +406,7 @@ bool GameList::isEmpty() const {
406 type == GameListItemType::SysNandDir)) { 406 type == GameListItemType::SysNandDir)) {
407 item_model->invisibleRootItem()->removeRow(child->row()); 407 item_model->invisibleRootItem()->removeRow(child->row());
408 i--; 408 i--;
409 }; 409 }
410 } 410 }
411 return !item_model->invisibleRootItem()->hasChildren(); 411 return !item_model->invisibleRootItem()->hasChildren();
412} 412}
@@ -502,10 +502,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string pat
502 navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0); 502 navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
503 503
504 connect(open_save_location, &QAction::triggered, [this, program_id, path]() { 504 connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
505 emit OpenFolderRequested(GameListOpenTarget::SaveData, path); 505 emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path);
506 }); 506 });
507 connect(open_mod_location, &QAction::triggered, [this, program_id, path]() { 507 connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
508 emit OpenFolderRequested(GameListOpenTarget::ModData, path); 508 emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path);
509 }); 509 });
510 connect(open_transferable_shader_cache, &QAction::triggered, 510 connect(open_transferable_shader_cache, &QAction::triggered,
511 [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); }); 511 [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); });
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 483835cce..78e2ba169 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -84,7 +84,8 @@ public:
84signals: 84signals:
85 void GameChosen(QString game_path); 85 void GameChosen(QString game_path);
86 void ShouldCancelWorker(); 86 void ShouldCancelWorker();
87 void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path); 87 void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
88 const std::string& game_path);
88 void OpenTransferableShaderCacheRequested(u64 program_id); 89 void OpenTransferableShaderCacheRequested(u64 program_id);
89 void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type); 90 void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type);
90 void RemoveFileRequested(u64 program_id, GameListRemoveTarget target); 91 void RemoveFileRequested(u64 program_id, GameListRemoveTarget target);
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 239016b94..e0ce45fd9 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -39,12 +39,12 @@ QString GetGameListCachedObject(const std::string& filename, const std::string&
39 return generator(); 39 return generator();
40 } 40 }
41 41
42 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" + 42 const auto path = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
43 DIR_SEP + filename + '.' + ext; 43 "game_list" + DIR_SEP + filename + '.' + ext;
44 44
45 FileUtil::CreateFullPath(path); 45 Common::FS::CreateFullPath(path);
46 46
47 if (!FileUtil::Exists(path)) { 47 if (!Common::FS::Exists(path)) {
48 const auto str = generator(); 48 const auto str = generator();
49 49
50 QFile file{QString::fromStdString(path)}; 50 QFile file{QString::fromStdString(path)};
@@ -70,14 +70,14 @@ std::pair<std::vector<u8>, std::string> GetGameListCachedObject(
70 return generator(); 70 return generator();
71 } 71 }
72 72
73 const auto path1 = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" + 73 const auto path1 = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
74 DIR_SEP + filename + ".jpeg"; 74 "game_list" + DIR_SEP + filename + ".jpeg";
75 const auto path2 = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" + 75 const auto path2 = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP +
76 DIR_SEP + filename + ".appname.txt"; 76 "game_list" + DIR_SEP + filename + ".appname.txt";
77 77
78 FileUtil::CreateFullPath(path1); 78 Common::FS::CreateFullPath(path1);
79 79
80 if (!FileUtil::Exists(path1) || !FileUtil::Exists(path2)) { 80 if (!Common::FS::Exists(path1) || !Common::FS::Exists(path2)) {
81 const auto [icon, nacp] = generator(); 81 const auto [icon, nacp] = generator();
82 82
83 QFile file1{QString::fromStdString(path1)}; 83 QFile file1{QString::fromStdString(path1)};
@@ -208,7 +208,7 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
208 file_type_string, program_id), 208 file_type_string, program_id),
209 new GameListItemCompat(compatibility), 209 new GameListItemCompat(compatibility),
210 new GameListItem(file_type_string), 210 new GameListItem(file_type_string),
211 new GameListItemSize(FileUtil::GetSize(path)), 211 new GameListItemSize(Common::FS::GetSize(path)),
212 }; 212 };
213 213
214 if (UISettings::values.show_add_ons) { 214 if (UISettings::values.show_add_ons) {
@@ -289,7 +289,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
289 } 289 }
290 290
291 const std::string physical_name = directory + DIR_SEP + virtual_name; 291 const std::string physical_name = directory + DIR_SEP + virtual_name;
292 const bool is_dir = FileUtil::IsDirectory(physical_name); 292 const bool is_dir = Common::FS::IsDirectory(physical_name);
293 if (!is_dir && 293 if (!is_dir &&
294 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { 294 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
295 const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read); 295 const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read);
@@ -345,11 +345,12 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
345 return true; 345 return true;
346 }; 346 };
347 347
348 FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); 348 Common::FS::ForeachDirectoryEntry(nullptr, dir_path, callback);
349} 349}
350 350
351void GameListWorker::run() { 351void GameListWorker::run() {
352 stop_processing = false; 352 stop_processing = false;
353 provider->ClearAllEntries();
353 354
354 for (UISettings::GameDir& game_dir : game_dirs) { 355 for (UISettings::GameDir& game_dir : game_dirs) {
355 if (game_dir.path == QStringLiteral("SDMC")) { 356 if (game_dir.path == QStringLiteral("SDMC")) {
@@ -368,13 +369,12 @@ void GameListWorker::run() {
368 watch_list.append(game_dir.path); 369 watch_list.append(game_dir.path);
369 auto* const game_list_dir = new GameListDir(game_dir); 370 auto* const game_list_dir = new GameListDir(game_dir);
370 emit DirEntryReady(game_list_dir); 371 emit DirEntryReady(game_list_dir);
371 provider->ClearAllEntries();
372 ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), 372 ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(),
373 game_dir.deep_scan ? 256 : 0, game_list_dir); 373 game_dir.deep_scan ? 256 : 0, game_list_dir);
374 ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), 374 ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(),
375 game_dir.deep_scan ? 256 : 0, game_list_dir); 375 game_dir.deep_scan ? 256 : 0, game_list_dir);
376 } 376 }
377 }; 377 }
378 378
379 emit Finished(watch_list); 379 emit Finished(watch_list);
380} 380}
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 592993c36..a1b61d119 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -94,6 +94,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
94#include "core/perf_stats.h" 94#include "core/perf_stats.h"
95#include "core/settings.h" 95#include "core/settings.h"
96#include "core/telemetry_session.h" 96#include "core/telemetry_session.h"
97#include "input_common/main.h"
97#include "video_core/gpu.h" 98#include "video_core/gpu.h"
98#include "video_core/shader_notify.h" 99#include "video_core/shader_notify.h"
99#include "yuzu/about_dialog.h" 100#include "yuzu/about_dialog.h"
@@ -177,8 +178,8 @@ static void InitializeLogging() {
177 log_filter.ParseFilterString(Settings::values.log_filter); 178 log_filter.ParseFilterString(Settings::values.log_filter);
178 Log::SetGlobalFilter(log_filter); 179 Log::SetGlobalFilter(log_filter);
179 180
180 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 181 const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
181 FileUtil::CreateFullPath(log_dir); 182 Common::FS::CreateFullPath(log_dir);
182 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); 183 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
183#ifdef _WIN32 184#ifdef _WIN32
184 Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); 185 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
@@ -186,9 +187,9 @@ static void InitializeLogging() {
186} 187}
187 188
188GMainWindow::GMainWindow() 189GMainWindow::GMainWindow()
189 : config(new Config()), emu_thread(nullptr), 190 : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
190 vfs(std::make_shared<FileSys::RealVfsFilesystem>()), 191 config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
191 provider(std::make_unique<FileSys::ManualContentProvider>()) { 192 provider{std::make_unique<FileSys::ManualContentProvider>()} {
192 InitializeLogging(); 193 InitializeLogging();
193 194
194 LoadTranslation(); 195 LoadTranslation();
@@ -473,7 +474,7 @@ void GMainWindow::InitializeWidgets() {
473#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING 474#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
474 ui.action_Report_Compatibility->setVisible(true); 475 ui.action_Report_Compatibility->setVisible(true);
475#endif 476#endif
476 render_window = new GRenderWindow(this, emu_thread.get()); 477 render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem);
477 render_window->hide(); 478 render_window->hide();
478 479
479 game_list = new GameList(vfs, provider.get(), this); 480 game_list = new GameList(vfs, provider.get(), this);
@@ -894,6 +895,8 @@ void GMainWindow::ConnectMenuEvents() {
894 connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ); 895 connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ);
895 connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); 896 connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); });
896 connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); 897 connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
898 connect(ui.action_Configure_Current_Game, &QAction::triggered, this,
899 &GMainWindow::OnConfigurePerGame);
897 900
898 // View 901 // View
899 connect(ui.action_Single_Window_Mode, &QAction::triggered, this, 902 connect(ui.action_Single_Window_Mode, &QAction::triggered, this,
@@ -1039,7 +1042,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
1039 } 1042 }
1040 game_path = filename; 1043 game_path = filename;
1041 1044
1042 system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "Qt"); 1045 system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "Qt");
1043 return true; 1046 return true;
1044} 1047}
1045 1048
@@ -1121,7 +1124,7 @@ void GMainWindow::BootGame(const QString& filename) {
1121 title_name = metadata.first->GetApplicationName(); 1124 title_name = metadata.first->GetApplicationName();
1122 } 1125 }
1123 if (res != Loader::ResultStatus::Success || title_name.empty()) { 1126 if (res != Loader::ResultStatus::Success || title_name.empty()) {
1124 title_name = FileUtil::GetFilename(filename.toStdString()); 1127 title_name = Common::FS::GetFilename(filename.toStdString());
1125 } 1128 }
1126 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); 1129 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
1127 UpdateWindowTitle(title_name, title_version); 1130 UpdateWindowTitle(title_name, title_version);
@@ -1167,6 +1170,7 @@ void GMainWindow::ShutdownGame() {
1167 ui.action_Pause->setEnabled(false); 1170 ui.action_Pause->setEnabled(false);
1168 ui.action_Stop->setEnabled(false); 1171 ui.action_Stop->setEnabled(false);
1169 ui.action_Restart->setEnabled(false); 1172 ui.action_Restart->setEnabled(false);
1173 ui.action_Configure_Current_Game->setEnabled(false);
1170 ui.action_Report_Compatibility->setEnabled(false); 1174 ui.action_Report_Compatibility->setEnabled(false);
1171 ui.action_Load_Amiibo->setEnabled(false); 1175 ui.action_Load_Amiibo->setEnabled(false);
1172 ui.action_Capture_Screenshot->setEnabled(false); 1176 ui.action_Capture_Screenshot->setEnabled(false);
@@ -1239,27 +1243,36 @@ void GMainWindow::OnGameListLoadFile(QString game_path) {
1239 BootGame(game_path); 1243 BootGame(game_path);
1240} 1244}
1241 1245
1242void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path) { 1246void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
1247 const std::string& game_path) {
1243 std::string path; 1248 std::string path;
1244 QString open_target; 1249 QString open_target;
1245 1250
1246 const auto v_file = Core::GetGameFileFromPath(vfs, game_path); 1251 const auto [user_save_size, device_save_size] = [this, &program_id, &game_path] {
1247 const auto loader = Loader::GetLoader(v_file); 1252 FileSys::PatchManager pm{program_id};
1248 FileSys::NACP control{}; 1253 const auto control = pm.GetControlMetadata().first;
1249 u64 program_id{}; 1254 if (control != nullptr) {
1255 return std::make_pair(control->GetDefaultNormalSaveSize(),
1256 control->GetDeviceSaveDataSize());
1257 } else {
1258 const auto file = Core::GetGameFileFromPath(vfs, game_path);
1259 const auto loader = Loader::GetLoader(file);
1250 1260
1251 loader->ReadControlData(control); 1261 FileSys::NACP nacp{};
1252 loader->ReadProgramId(program_id); 1262 loader->ReadControlData(nacp);
1263 return std::make_pair(nacp.GetDefaultNormalSaveSize(), nacp.GetDeviceSaveDataSize());
1264 }
1265 }();
1253 1266
1254 const bool has_user_save{control.GetDefaultNormalSaveSize() > 0}; 1267 const bool has_user_save{user_save_size > 0};
1255 const bool has_device_save{control.GetDeviceSaveDataSize() > 0}; 1268 const bool has_device_save{device_save_size > 0};
1256 1269
1257 ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?"); 1270 ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?");
1258 1271
1259 switch (target) { 1272 switch (target) {
1260 case GameListOpenTarget::SaveData: { 1273 case GameListOpenTarget::SaveData: {
1261 open_target = tr("Save Data"); 1274 open_target = tr("Save Data");
1262 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); 1275 const std::string nand_dir = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
1263 1276
1264 if (has_user_save) { 1277 if (has_user_save) {
1265 // User save data 1278 // User save data
@@ -1294,16 +1307,16 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str
1294 FileSys::SaveDataType::SaveData, program_id, {}, 0); 1307 FileSys::SaveDataType::SaveData, program_id, {}, 0);
1295 } 1308 }
1296 1309
1297 if (!FileUtil::Exists(path)) { 1310 if (!Common::FS::Exists(path)) {
1298 FileUtil::CreateFullPath(path); 1311 Common::FS::CreateFullPath(path);
1299 FileUtil::CreateDir(path); 1312 Common::FS::CreateDir(path);
1300 } 1313 }
1301 1314
1302 break; 1315 break;
1303 } 1316 }
1304 case GameListOpenTarget::ModData: { 1317 case GameListOpenTarget::ModData: {
1305 open_target = tr("Mod Data"); 1318 open_target = tr("Mod Data");
1306 const auto load_dir = FileUtil::GetUserPath(FileUtil::UserPath::LoadDir); 1319 const auto load_dir = Common::FS::GetUserPath(Common::FS::UserPath::LoadDir);
1307 path = fmt::format("{}{:016X}", load_dir, program_id); 1320 path = fmt::format("{}{:016X}", load_dir, program_id);
1308 break; 1321 break;
1309 } 1322 }
@@ -1325,7 +1338,7 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str
1325 1338
1326void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { 1339void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
1327 const QString shader_dir = 1340 const QString shader_dir =
1328 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); 1341 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir));
1329 const QString transferable_shader_cache_folder_path = 1342 const QString transferable_shader_cache_folder_path =
1330 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); 1343 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
1331 const QString transferable_shader_cache_file_path = 1344 const QString transferable_shader_cache_file_path =
@@ -1428,8 +1441,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
1428 RemoveAddOnContent(program_id, entry_type); 1441 RemoveAddOnContent(program_id, entry_type);
1429 break; 1442 break;
1430 } 1443 }
1431 FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + 1444 Common::FS::DeleteDirRecursively(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
1432 "game_list"); 1445 DIR_SEP + "game_list");
1433 game_list->PopulateAsync(UISettings::values.game_dirs); 1446 game_list->PopulateAsync(UISettings::values.game_dirs);
1434} 1447}
1435 1448
@@ -1519,7 +1532,7 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ
1519 1532
1520void GMainWindow::RemoveTransferableShaderCache(u64 program_id) { 1533void GMainWindow::RemoveTransferableShaderCache(u64 program_id) {
1521 const QString shader_dir = 1534 const QString shader_dir =
1522 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); 1535 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir));
1523 const QString transferable_shader_cache_folder_path = 1536 const QString transferable_shader_cache_folder_path =
1524 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); 1537 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
1525 const QString transferable_shader_cache_file_path = 1538 const QString transferable_shader_cache_file_path =
@@ -1543,7 +1556,7 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id) {
1543 1556
1544void GMainWindow::RemoveCustomConfiguration(u64 program_id) { 1557void GMainWindow::RemoveCustomConfiguration(u64 program_id) {
1545 const QString config_dir = 1558 const QString config_dir =
1546 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir)); 1559 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir));
1547 const QString custom_config_file_path = 1560 const QString custom_config_file_path =
1548 config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id)); 1561 config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id));
1549 1562
@@ -1590,7 +1603,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1590 } 1603 }
1591 1604
1592 const auto path = fmt::format( 1605 const auto path = fmt::format(
1593 "{}{:016X}/romfs", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), *romfs_title_id); 1606 "{}{:016X}/romfs", Common::FS::GetUserPath(Common::FS::UserPath::DumpDir), *romfs_title_id);
1594 1607
1595 FileSys::VirtualFile romfs; 1608 FileSys::VirtualFile romfs;
1596 1609
@@ -1670,13 +1683,13 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
1670void GMainWindow::OnGameListOpenDirectory(const QString& directory) { 1683void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
1671 QString path; 1684 QString path;
1672 if (directory == QStringLiteral("SDMC")) { 1685 if (directory == QStringLiteral("SDMC")) {
1673 path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + 1686 path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
1674 "Nintendo/Contents/registered"); 1687 "Nintendo/Contents/registered");
1675 } else if (directory == QStringLiteral("UserNAND")) { 1688 } else if (directory == QStringLiteral("UserNAND")) {
1676 path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1689 path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1677 "user/Contents/registered"); 1690 "user/Contents/registered");
1678 } else if (directory == QStringLiteral("SysNAND")) { 1691 } else if (directory == QStringLiteral("SysNAND")) {
1679 path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 1692 path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
1680 "system/Contents/registered"); 1693 "system/Contents/registered");
1681 } else { 1694 } else {
1682 path = directory; 1695 path = directory;
@@ -1690,8 +1703,10 @@ void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
1690 1703
1691void GMainWindow::OnGameListAddDirectory() { 1704void GMainWindow::OnGameListAddDirectory() {
1692 const QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); 1705 const QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
1693 if (dir_path.isEmpty()) 1706 if (dir_path.isEmpty()) {
1694 return; 1707 return;
1708 }
1709
1695 UISettings::GameDir game_dir{dir_path, false, true}; 1710 UISettings::GameDir game_dir{dir_path, false, true};
1696 if (!UISettings::values.game_dirs.contains(game_dir)) { 1711 if (!UISettings::values.game_dirs.contains(game_dir)) {
1697 UISettings::values.game_dirs.append(game_dir); 1712 UISettings::values.game_dirs.append(game_dir);
@@ -1718,26 +1733,7 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
1718 return; 1733 return;
1719 } 1734 }
1720 1735
1721 ConfigurePerGame dialog(this, title_id); 1736 OpenPerGameConfiguration(title_id, file);
1722 dialog.LoadFromFile(v_file);
1723 auto result = dialog.exec();
1724 if (result == QDialog::Accepted) {
1725 dialog.ApplyConfiguration();
1726
1727 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
1728 if (reload) {
1729 game_list->PopulateAsync(UISettings::values.game_dirs);
1730 }
1731
1732 // Do not cause the global config to write local settings into the config file
1733 Settings::RestoreGlobalState();
1734
1735 if (!Core::System::GetInstance().IsPoweredOn()) {
1736 config->Save();
1737 }
1738 } else {
1739 Settings::RestoreGlobalState();
1740 }
1741} 1737}
1742 1738
1743void GMainWindow::OnMenuLoadFile() { 1739void GMainWindow::OnMenuLoadFile() {
@@ -1882,8 +1878,8 @@ void GMainWindow::OnMenuInstallToNAND() {
1882 : tr("%n file(s) failed to install\n", "", failed_files.size())); 1878 : tr("%n file(s) failed to install\n", "", failed_files.size()));
1883 1879
1884 QMessageBox::information(this, tr("Install Results"), install_results); 1880 QMessageBox::information(this, tr("Install Results"), install_results);
1885 FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + 1881 Common::FS::DeleteDirRecursively(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
1886 "game_list"); 1882 DIR_SEP + "game_list");
1887 game_list->PopulateAsync(UISettings::values.game_dirs); 1883 game_list->PopulateAsync(UISettings::values.game_dirs);
1888 ui.action_Install_File_NAND->setEnabled(true); 1884 ui.action_Install_File_NAND->setEnabled(true);
1889} 1885}
@@ -2066,6 +2062,7 @@ void GMainWindow::OnStartGame() {
2066 ui.action_Pause->setEnabled(true); 2062 ui.action_Pause->setEnabled(true);
2067 ui.action_Stop->setEnabled(true); 2063 ui.action_Stop->setEnabled(true);
2068 ui.action_Restart->setEnabled(true); 2064 ui.action_Restart->setEnabled(true);
2065 ui.action_Configure_Current_Game->setEnabled(true);
2069 ui.action_Report_Compatibility->setEnabled(true); 2066 ui.action_Report_Compatibility->setEnabled(true);
2070 2067
2071 discord_rpc->Update(); 2068 discord_rpc->Update();
@@ -2217,7 +2214,7 @@ void GMainWindow::OnConfigure() {
2217 const auto old_theme = UISettings::values.theme; 2214 const auto old_theme = UISettings::values.theme;
2218 const bool old_discord_presence = UISettings::values.enable_discord_presence; 2215 const bool old_discord_presence = UISettings::values.enable_discord_presence;
2219 2216
2220 ConfigureDialog configure_dialog(this, hotkey_registry); 2217 ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get());
2221 connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this, 2218 connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this,
2222 &GMainWindow::OnLanguageChanged); 2219 &GMainWindow::OnLanguageChanged);
2223 2220
@@ -2255,6 +2252,36 @@ void GMainWindow::OnConfigure() {
2255 UpdateStatusButtons(); 2252 UpdateStatusButtons();
2256} 2253}
2257 2254
2255void GMainWindow::OnConfigurePerGame() {
2256 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
2257 OpenPerGameConfiguration(title_id, game_path.toStdString());
2258}
2259
2260void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_name) {
2261 const auto v_file = Core::GetGameFileFromPath(vfs, file_name);
2262
2263 ConfigurePerGame dialog(this, title_id);
2264 dialog.LoadFromFile(v_file);
2265 auto result = dialog.exec();
2266 if (result == QDialog::Accepted) {
2267 dialog.ApplyConfiguration();
2268
2269 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
2270 if (reload) {
2271 game_list->PopulateAsync(UISettings::values.game_dirs);
2272 }
2273
2274 // Do not cause the global config to write local settings into the config file
2275 Settings::RestoreGlobalState();
2276
2277 if (!Core::System::GetInstance().IsPoweredOn()) {
2278 config->Save();
2279 }
2280 } else {
2281 Settings::RestoreGlobalState();
2282 }
2283}
2284
2258void GMainWindow::OnLoadAmiibo() { 2285void GMainWindow::OnLoadAmiibo() {
2259 const QString extensions{QStringLiteral("*.bin")}; 2286 const QString extensions{QStringLiteral("*.bin")};
2260 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); 2287 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
@@ -2302,7 +2329,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
2302 2329
2303void GMainWindow::OnOpenYuzuFolder() { 2330void GMainWindow::OnOpenYuzuFolder() {
2304 QDesktopServices::openUrl(QUrl::fromLocalFile( 2331 QDesktopServices::openUrl(QUrl::fromLocalFile(
2305 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir)))); 2332 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::UserDir))));
2306} 2333}
2307 2334
2308void GMainWindow::OnAbout() { 2335void GMainWindow::OnAbout() {
@@ -2324,7 +2351,7 @@ void GMainWindow::OnCaptureScreenshot() {
2324 2351
2325 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 2352 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
2326 const auto screenshot_path = 2353 const auto screenshot_path =
2327 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir)); 2354 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir));
2328 const auto date = 2355 const auto date =
2329 QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz")); 2356 QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz"));
2330 QString filename = QStringLiteral("%1%2_%3.png") 2357 QString filename = QStringLiteral("%1%2_%3.png")
@@ -2527,18 +2554,18 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
2527 if (res == QMessageBox::Cancel) 2554 if (res == QMessageBox::Cancel)
2528 return; 2555 return;
2529 2556
2530 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) + 2557 Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) +
2531 "prod.keys_autogenerated"); 2558 "prod.keys_autogenerated");
2532 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) + 2559 Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) +
2533 "console.keys_autogenerated"); 2560 "console.keys_autogenerated");
2534 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) + 2561 Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) +
2535 "title.keys_autogenerated"); 2562 "title.keys_autogenerated");
2536 } 2563 }
2537 2564
2538 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); 2565 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
2539 if (keys.BaseDeriveNecessary()) { 2566 if (keys.BaseDeriveNecessary()) {
2540 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory( 2567 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory(
2541 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)}; 2568 Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), FileSys::Mode::Read)};
2542 2569
2543 const auto function = [this, &keys, &pdm] { 2570 const auto function = [this, &keys, &pdm] {
2544 keys.PopulateFromPartitionData(pdm); 2571 keys.PopulateFromPartitionData(pdm);
@@ -2870,7 +2897,7 @@ int main(int argc, char* argv[]) {
2870 // If you start a bundle (binary) on OSX without the Terminal, the working directory is "/". 2897 // If you start a bundle (binary) on OSX without the Terminal, the working directory is "/".
2871 // But since we require the working directory to be the executable path for the location of 2898 // But since we require the working directory to be the executable path for the location of
2872 // the user folder in the Qt Frontend, we need to cd into that working directory 2899 // the user folder in the Qt Frontend, we need to cd into that working directory
2873 const std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + ".."; 2900 const std::string bin_path = Common::FS::GetBundleDirectory() + DIR_SEP + "..";
2874 chdir(bin_path.c_str()); 2901 chdir(bin_path.c_str());
2875#endif 2902#endif
2876 2903
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 {
40struct SoftwareKeyboardParameters; 40struct SoftwareKeyboardParameters;
41} // namespace Core::Frontend 41} // namespace Core::Frontend
42 42
43namespace DiscordRPC {
44class DiscordInterface;
45}
46
43namespace FileSys { 47namespace FileSys {
44class ContentProvider; 48class ContentProvider;
45class ManualContentProvider; 49class ManualContentProvider;
46class VfsFilesystem; 50class VfsFilesystem;
47} // namespace FileSys 51} // namespace FileSys
48 52
53namespace InputCommon {
54class InputSubsystem;
55}
56
49enum class EmulatedDirectoryTarget { 57enum class EmulatedDirectoryTarget {
50 NAND, 58 NAND,
51 SDMC, 59 SDMC,
@@ -62,10 +70,6 @@ enum class ReinitializeKeyBehavior {
62 Warning, 70 Warning,
63}; 71};
64 72
65namespace DiscordRPC {
66class DiscordInterface;
67}
68
69class GMainWindow : public QMainWindow { 73class GMainWindow : public QMainWindow {
70 Q_OBJECT 74 Q_OBJECT
71 75
@@ -86,8 +90,6 @@ public:
86 GMainWindow(); 90 GMainWindow();
87 ~GMainWindow() override; 91 ~GMainWindow() override;
88 92
89 std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
90
91 bool DropAction(QDropEvent* event); 93 bool DropAction(QDropEvent* event);
92 void AcceptDropEvent(QDropEvent* event); 94 void AcceptDropEvent(QDropEvent* event);
93 95
@@ -198,7 +200,8 @@ private slots:
198 void OnOpenFAQ(); 200 void OnOpenFAQ();
199 /// Called whenever a user selects a game in the game list widget. 201 /// Called whenever a user selects a game in the game list widget.
200 void OnGameListLoadFile(QString game_path); 202 void OnGameListLoadFile(QString game_path);
201 void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); 203 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
204 const std::string& game_path);
202 void OnTransferableShaderCacheOpenFile(u64 program_id); 205 void OnTransferableShaderCacheOpenFile(u64 program_id);
203 void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type); 206 void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type);
204 void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target); 207 void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target);
@@ -216,6 +219,7 @@ private slots:
216 void OnMenuInstallToNAND(); 219 void OnMenuInstallToNAND();
217 void OnMenuRecentFile(); 220 void OnMenuRecentFile();
218 void OnConfigure(); 221 void OnConfigure();
222 void OnConfigurePerGame();
219 void OnLoadAmiibo(); 223 void OnLoadAmiibo();
220 void OnOpenYuzuFolder(); 224 void OnOpenYuzuFolder();
221 void OnAbout(); 225 void OnAbout();
@@ -249,9 +253,13 @@ private:
249 void ShowMouseCursor(); 253 void ShowMouseCursor();
250 void OpenURL(const QUrl& url); 254 void OpenURL(const QUrl& url);
251 void LoadTranslation(); 255 void LoadTranslation();
256 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
252 257
253 Ui::MainWindow ui; 258 Ui::MainWindow ui;
254 259
260 std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
261 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
262
255 GRenderWindow* render_window; 263 GRenderWindow* render_window;
256 GameList* game_list; 264 GameList* game_list;
257 LoadingScreen* loading_screen; 265 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 @@
81 <addaction name="action_Restart"/> 81 <addaction name="action_Restart"/>
82 <addaction name="separator"/> 82 <addaction name="separator"/>
83 <addaction name="action_Configure"/> 83 <addaction name="action_Configure"/>
84 <addaction name="action_Configure_Current_Game"/>
84 </widget> 85 </widget>
85 <widget class="QMenu" name="menu_View"> 86 <widget class="QMenu" name="menu_View">
86 <property name="title"> 87 <property name="title">
@@ -287,6 +288,14 @@
287 <string>Capture Screenshot</string> 288 <string>Capture Screenshot</string>
288 </property> 289 </property>
289 </action> 290 </action>
291 <action name="action_Configure_Current_Game">
292 <property name="enabled">
293 <bool>false</bool>
294 </property>
295 <property name="text">
296 <string>Configure Current Game..</string>
297 </property>
298 </action>
290 </widget> 299 </widget>
291 <resources/> 300 <resources/>
292 <connections/> 301 <connections/>
diff --git a/src/yuzu/uisettings.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{{
16}}; 16}};
17 17
18Values values = {}; 18Values values = {};
19
19} // namespace UISettings 20} // 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;
29 29
30struct GameDir { 30struct GameDir {
31 QString path; 31 QString path;
32 bool deep_scan; 32 bool deep_scan = false;
33 bool expanded; 33 bool expanded = false;
34 bool operator==(const GameDir& rhs) const { 34 bool operator==(const GameDir& rhs) const {
35 return path == rhs.path; 35 return path == rhs.path;
36 }; 36 }
37 bool operator!=(const GameDir& rhs) const { 37 bool operator!=(const GameDir& rhs) const {
38 return !operator==(rhs); 38 return !operator==(rhs);
39 }; 39 }
40}; 40};
41 41
42struct Values { 42struct Values {
@@ -87,9 +87,6 @@ struct Values {
87 // logging 87 // logging
88 bool show_console; 88 bool show_console;
89 89
90 // Controllers
91 int profile_index;
92
93 // Game List 90 // Game List
94 bool show_add_ons; 91 bool show_add_ons;
95 uint32_t icon_size; 92 uint32_t icon_size;
@@ -100,6 +97,7 @@ struct Values {
100}; 97};
101 98
102extern Values values; 99extern Values values;
100
103} // namespace UISettings 101} // namespace UISettings
104 102
105Q_DECLARE_METATYPE(UISettings::GameDir*); 103Q_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 @@
16#include "yuzu_cmd/config.h" 16#include "yuzu_cmd/config.h"
17#include "yuzu_cmd/default_ini.h" 17#include "yuzu_cmd/default_ini.h"
18 18
19namespace FS = Common::FS;
20
19Config::Config() { 21Config::Config() {
20 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 22 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
21 sdl2_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "sdl2-config.ini"; 23 sdl2_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + "sdl2-config.ini";
22 sdl2_config = std::make_unique<INIReader>(sdl2_config_loc); 24 sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
23 25
24 Reload(); 26 Reload();
@@ -31,8 +33,8 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
31 if (sdl2_config->ParseError() < 0) { 33 if (sdl2_config->ParseError() < 0) {
32 if (retry) { 34 if (retry) {
33 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location); 35 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
34 FileUtil::CreateFullPath(location); 36 FS::CreateFullPath(location);
35 FileUtil::WriteStringToFile(true, location, default_contents); 37 FS::WriteStringToFile(true, location, default_contents);
36 sdl2_config = std::make_unique<INIReader>(location); // Reopen file 38 sdl2_config = std::make_unique<INIReader>(location); // Reopen file
37 39
38 return LoadINI(default_contents, false); 40 return LoadINI(default_contents, false);
@@ -286,6 +288,8 @@ void Config::ReadValues() {
286 Settings::values.debug_pad_analogs[i] = default_param; 288 Settings::values.debug_pad_analogs[i] = default_param;
287 } 289 }
288 290
291 Settings::values.vibration_enabled =
292 sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true);
289 Settings::values.touchscreen.enabled = 293 Settings::values.touchscreen.enabled =
290 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); 294 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
291 Settings::values.touchscreen.device = 295 Settings::values.touchscreen.device =
@@ -315,21 +319,21 @@ void Config::ReadValues() {
315 // Data Storage 319 // Data Storage
316 Settings::values.use_virtual_sd = 320 Settings::values.use_virtual_sd =
317 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); 321 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
318 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir, 322 FS::GetUserPath(
319 sdl2_config->Get("Data Storage", "nand_directory", 323 FS::UserPath::NANDDir,
320 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); 324 sdl2_config->Get("Data Storage", "nand_directory", FS::GetUserPath(FS::UserPath::NANDDir)));
321 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, 325 FS::GetUserPath(
322 sdl2_config->Get("Data Storage", "sdmc_directory", 326 FS::UserPath::SDMCDir,
323 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 327 sdl2_config->Get("Data Storage", "sdmc_directory", FS::GetUserPath(FS::UserPath::SDMCDir)));
324 FileUtil::GetUserPath(FileUtil::UserPath::LoadDir, 328 FS::GetUserPath(
325 sdl2_config->Get("Data Storage", "load_directory", 329 FS::UserPath::LoadDir,
326 FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))); 330 sdl2_config->Get("Data Storage", "load_directory", FS::GetUserPath(FS::UserPath::LoadDir)));
327 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir, 331 FS::GetUserPath(
328 sdl2_config->Get("Data Storage", "dump_directory", 332 FS::UserPath::DumpDir,
329 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))); 333 sdl2_config->Get("Data Storage", "dump_directory", FS::GetUserPath(FS::UserPath::DumpDir)));
330 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir, 334 FS::GetUserPath(FS::UserPath::CacheDir,
331 sdl2_config->Get("Data Storage", "cache_directory", 335 sdl2_config->Get("Data Storage", "cache_directory",
332 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))); 336 FS::GetUserPath(FS::UserPath::CacheDir)));
333 Settings::values.gamecard_inserted = 337 Settings::values.gamecard_inserted =
334 sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false); 338 sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false);
335 Settings::values.gamecard_current_game = 339 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 @@
13#include "input_common/sdl/sdl.h" 13#include "input_common/sdl/sdl.h"
14#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 14#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
15 15
16EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen) : system{system} { 16EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen,
17 InputCommon::InputSubsystem* input_subsystem_)
18 : system{system}, input_subsystem{input_subsystem_} {
17 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 19 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
18 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 20 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
19 exit(1); 21 exit(1);
20 } 22 }
21 InputCommon::Init(); 23 input_subsystem->Initialize();
22 SDL_SetMainReady(); 24 SDL_SetMainReady();
23} 25}
24 26
25EmuWindow_SDL2::~EmuWindow_SDL2() { 27EmuWindow_SDL2::~EmuWindow_SDL2() {
26 InputCommon::Shutdown(); 28 input_subsystem->Shutdown();
27 SDL_Quit(); 29 SDL_Quit();
28} 30}
29 31
30void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 32void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
31 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); 33 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
32 InputCommon::GetMotionEmu()->Tilt(x, y); 34 input_subsystem->GetMotionEmu()->Tilt(x, y);
33} 35}
34 36
35void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { 37void 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) {
41 } 43 }
42 } else if (button == SDL_BUTTON_RIGHT) { 44 } else if (button == SDL_BUTTON_RIGHT) {
43 if (state == SDL_PRESSED) { 45 if (state == SDL_PRESSED) {
44 InputCommon::GetMotionEmu()->BeginTilt(x, y); 46 input_subsystem->GetMotionEmu()->BeginTilt(x, y);
45 } else { 47 } else {
46 InputCommon::GetMotionEmu()->EndTilt(); 48 input_subsystem->GetMotionEmu()->EndTilt();
47 } 49 }
48 } 50 }
49} 51}
@@ -79,9 +81,9 @@ void EmuWindow_SDL2::OnFingerUp() {
79 81
80void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { 82void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
81 if (state == SDL_PRESSED) { 83 if (state == SDL_PRESSED) {
82 InputCommon::GetKeyboard()->PressKey(key); 84 input_subsystem->GetKeyboard()->PressKey(key);
83 } else if (state == SDL_RELEASED) { 85 } else if (state == SDL_RELEASED) {
84 InputCommon::GetKeyboard()->ReleaseKey(key); 86 input_subsystem->GetKeyboard()->ReleaseKey(key);
85 } 87 }
86} 88}
87 89
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 {
14class System; 14class System;
15} 15}
16 16
17namespace InputCommon {
18class InputSubsystem;
19}
20
17class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { 21class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
18public: 22public:
19 explicit EmuWindow_SDL2(Core::System& system, bool fullscreen); 23 explicit EmuWindow_SDL2(Core::System& system, bool fullscreen,
24 InputCommon::InputSubsystem* input_subsystem);
20 ~EmuWindow_SDL2(); 25 ~EmuWindow_SDL2();
21 26
22 /// Polls window events 27 /// Polls window events
@@ -76,4 +81,7 @@ protected:
76 81
77 /// Keeps track of how often to update the title bar during gameplay 82 /// Keeps track of how often to update the title bar during gameplay
78 u32 last_time = 0; 83 u32 last_time = 0;
84
85 /// Input subsystem to use with this window.
86 InputCommon::InputSubsystem* input_subsystem;
79}; 87};
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() {
87 return unsupported_ext.empty(); 87 return unsupported_ext.empty();
88} 88}
89 89
90EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen) 90EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen,
91 : EmuWindow_SDL2{system, fullscreen} { 91 InputCommon::InputSubsystem* input_subsystem)
92 : EmuWindow_SDL2{system, fullscreen, input_subsystem} {
92 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 93 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
93 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 94 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
94 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); 95 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 @@
8#include "core/frontend/emu_window.h" 8#include "core/frontend/emu_window.h"
9#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 9#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
10 10
11namespace InputCommon {
12class InputSubsystem;
13}
14
11class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { 15class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
12public: 16public:
13 explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen); 17 explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen,
18 InputCommon::InputSubsystem* input_subsystem);
14 ~EmuWindow_SDL2_GL(); 19 ~EmuWindow_SDL2_GL();
15 20
16 void Present() override; 21 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 @@
19#include <SDL.h> 19#include <SDL.h>
20#include <SDL_syswm.h> 20#include <SDL_syswm.h>
21 21
22EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen) 22EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen,
23 : EmuWindow_SDL2{system, fullscreen} { 23 InputCommon::InputSubsystem* input_subsystem)
24 : EmuWindow_SDL2{system, fullscreen, input_subsystem} {
24 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, 25 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
25 Common::g_scm_branch, Common::g_scm_desc); 26 Common::g_scm_branch, Common::g_scm_desc);
26 render_window = 27 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 {
13class System; 13class System;
14} 14}
15 15
16namespace InputCommon {
17class InputSubsystem;
18}
19
16class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { 20class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
17public: 21public:
18 explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen); 22 explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen,
23 InputCommon::InputSubsystem* input_subsystem);
19 ~EmuWindow_SDL2_VK(); 24 ~EmuWindow_SDL2_VK();
20 25
21 void Present() override; 26 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 @@
23#include "common/telemetry.h" 23#include "common/telemetry.h"
24#include "core/core.h" 24#include "core/core.h"
25#include "core/crypto/key_manager.h" 25#include "core/crypto/key_manager.h"
26#include "core/file_sys/registered_cache.h"
26#include "core/file_sys/vfs_real.h" 27#include "core/file_sys/vfs_real.h"
27#include "core/gdbstub/gdbstub.h" 28#include "core/gdbstub/gdbstub.h"
28#include "core/hle/service/filesystem/filesystem.h" 29#include "core/hle/service/filesystem/filesystem.h"
29#include "core/loader/loader.h" 30#include "core/loader/loader.h"
30#include "core/settings.h" 31#include "core/settings.h"
31#include "core/telemetry_session.h" 32#include "core/telemetry_session.h"
33#include "input_common/main.h"
32#include "video_core/renderer_base.h" 34#include "video_core/renderer_base.h"
33#include "yuzu_cmd/config.h" 35#include "yuzu_cmd/config.h"
34#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 36#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
@@ -37,8 +39,6 @@
37#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" 39#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
38#endif 40#endif
39 41
40#include "core/file_sys/registered_cache.h"
41
42#ifdef _WIN32 42#ifdef _WIN32
43// windows.h needs to be included before shellapi.h 43// windows.h needs to be included before shellapi.h
44#include <windows.h> 44#include <windows.h>
@@ -82,8 +82,8 @@ static void InitializeLogging() {
82 82
83 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); 83 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
84 84
85 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 85 const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
86 FileUtil::CreateFullPath(log_dir); 86 Common::FS::CreateFullPath(log_dir);
87 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); 87 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
88#ifdef _WIN32 88#ifdef _WIN32
89 Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); 89 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
@@ -179,15 +179,16 @@ int main(int argc, char** argv) {
179 Settings::Apply(); 179 Settings::Apply();
180 180
181 Core::System& system{Core::System::GetInstance()}; 181 Core::System& system{Core::System::GetInstance()};
182 InputCommon::InputSubsystem input_subsystem;
182 183
183 std::unique_ptr<EmuWindow_SDL2> emu_window; 184 std::unique_ptr<EmuWindow_SDL2> emu_window;
184 switch (Settings::values.renderer_backend.GetValue()) { 185 switch (Settings::values.renderer_backend.GetValue()) {
185 case Settings::RendererBackend::OpenGL: 186 case Settings::RendererBackend::OpenGL:
186 emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen); 187 emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen, &input_subsystem);
187 break; 188 break;
188 case Settings::RendererBackend::Vulkan: 189 case Settings::RendererBackend::Vulkan:
189#ifdef HAS_VULKAN 190#ifdef HAS_VULKAN
190 emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen); 191 emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen, &input_subsystem);
191 break; 192 break;
192#else 193#else
193 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); 194 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!");
@@ -229,7 +230,7 @@ int main(int argc, char** argv) {
229 } 230 }
230 } 231 }
231 232
232 system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); 233 system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL");
233 234
234 // Core is loaded, start the GPU (makes the GPU contexts current to this thread) 235 // Core is loaded, start the GPU (makes the GPU contexts current to this thread)
235 system.GPU().Start(); 236 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 @@
15#include "yuzu_tester/config.h" 15#include "yuzu_tester/config.h"
16#include "yuzu_tester/default_ini.h" 16#include "yuzu_tester/default_ini.h"
17 17
18namespace FS = Common::FS;
19
18Config::Config() { 20Config::Config() {
19 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 21 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
20 sdl2_config_loc = 22 sdl2_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + "sdl2-tester-config.ini";
21 FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "sdl2-tester-config.ini";
22 sdl2_config = std::make_unique<INIReader>(sdl2_config_loc); 23 sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
23 24
24 Reload(); 25 Reload();
@@ -31,8 +32,8 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
31 if (sdl2_config->ParseError() < 0) { 32 if (sdl2_config->ParseError() < 0) {
32 if (retry) { 33 if (retry) {
33 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location); 34 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
34 FileUtil::CreateFullPath(location); 35 FS::CreateFullPath(location);
35 FileUtil::WriteStringToFile(true, default_contents, location); 36 FS::WriteStringToFile(true, default_contents, location);
36 sdl2_config = std::make_unique<INIReader>(location); // Reopen file 37 sdl2_config = std::make_unique<INIReader>(location); // Reopen file
37 38
38 return LoadINI(default_contents, false); 39 return LoadINI(default_contents, false);
@@ -74,6 +75,7 @@ void Config::ReadValues() {
74 Settings::values.debug_pad_analogs[i] = ""; 75 Settings::values.debug_pad_analogs[i] = "";
75 } 76 }
76 77
78 Settings::values.vibration_enabled = true;
77 Settings::values.touchscreen.enabled = ""; 79 Settings::values.touchscreen.enabled = "";
78 Settings::values.touchscreen.device = ""; 80 Settings::values.touchscreen.device = "";
79 Settings::values.touchscreen.finger = 0; 81 Settings::values.touchscreen.finger = 0;
@@ -87,12 +89,12 @@ void Config::ReadValues() {
87 // Data Storage 89 // Data Storage
88 Settings::values.use_virtual_sd = 90 Settings::values.use_virtual_sd =
89 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); 91 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
90 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir, 92 FS::GetUserPath(Common::FS::UserPath::NANDDir,
91 sdl2_config->Get("Data Storage", "nand_directory", 93 sdl2_config->Get("Data Storage", "nand_directory",
92 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); 94 Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)));
93 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, 95 FS::GetUserPath(Common::FS::UserPath::SDMCDir,
94 sdl2_config->Get("Data Storage", "sdmc_directory", 96 sdl2_config->Get("Data Storage", "sdmc_directory",
95 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 97 Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)));
96 98
97 // System 99 // System
98 Settings::values.current_user = std::clamp<int>( 100 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 @@
13 13
14#include <glad/glad.h> 14#include <glad/glad.h>
15 15
16#include "common/assert.h"
17#include "common/logging/log.h" 16#include "common/logging/log.h"
18#include "common/scm_rev.h" 17#include "common/scm_rev.h"
19#include "core/settings.h" 18#include "core/settings.h"
@@ -53,7 +52,7 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
53 exit(1); 52 exit(1);
54 } 53 }
55 54
56 InputCommon::Init(); 55 input_subsystem->Initialize();
57 56
58 SDL_SetMainReady(); 57 SDL_SetMainReady();
59 58
@@ -105,7 +104,7 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
105} 104}
106 105
107EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { 106EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
108 InputCommon::Shutdown(); 107 input_subsystem->Shutdown();
109 SDL_GL_DeleteContext(gl_context); 108 SDL_GL_DeleteContext(gl_context);
110 SDL_Quit(); 109 SDL_Quit();
111} 110}
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 @@
8 8
9struct SDL_Window; 9struct SDL_Window;
10 10
11namespace InputCommon {
12class InputSubsystem;
13}
14
11class EmuWindow_SDL2_Hide : public Core::Frontend::EmuWindow { 15class EmuWindow_SDL2_Hide : public Core::Frontend::EmuWindow {
12public: 16public:
13 explicit EmuWindow_SDL2_Hide(); 17 explicit EmuWindow_SDL2_Hide();
@@ -25,6 +29,8 @@ private:
25 /// Whether the GPU and driver supports the OpenGL extension required 29 /// Whether the GPU and driver supports the OpenGL extension required
26 bool SupportsRequiredGLExtensions(); 30 bool SupportsRequiredGLExtensions();
27 31
32 std::unique_ptr<InputCommon::InputSubsystem> input_subsystem;
33
28 /// Internal SDL2 render window 34 /// Internal SDL2 render window
29 SDL_Window* render_window; 35 SDL_Window* render_window;
30 36
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index 083667baf..7acf0caad 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -79,8 +79,8 @@ static void InitializeLogging(bool console) {
79 if (console) 79 if (console)
80 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); 80 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
81 81
82 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 82 const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
83 FileUtil::CreateFullPath(log_dir); 83 Common::FS::CreateFullPath(log_dir);
84 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); 84 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
85#ifdef _WIN32 85#ifdef _WIN32
86 Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); 86 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
@@ -251,7 +251,8 @@ int main(int argc, char** argv) {
251 251
252 Service::Yuzu::InstallInterfaces(system.ServiceManager(), datastring, callback); 252 Service::Yuzu::InstallInterfaces(system.ServiceManager(), datastring, callback);
253 253
254 system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester"); 254 system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend",
255 "SDLHideTester");
255 256
256 system.GPU().Start(); 257 system.GPU().Start();
257 system.Renderer().Rasterizer().LoadDiskResources(); 258 system.Renderer().Rasterizer().LoadDiskResources();