summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/templates/build-msvc.yml4
-rw-r--r--CMakeLists.txt2
-rw-r--r--dist/qt_themes/default/style.qss14
-rw-r--r--dist/qt_themes/qdarkstyle/style.qss86
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/style.qss95
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/bit_cast.h22
-rw-r--r--src/common/logging/backend.cpp15
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/page_table.cpp2
-rw-r--r--src/common/page_table.h14
-rw-r--r--src/common/virtual_buffer.cpp4
-rw-r--r--src/common/virtual_buffer.h36
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/cpu_interrupt_handler.h4
-rw-r--r--src/core/core.cpp60
-rw-r--r--src/core/core.h177
-rw-r--r--src/core/file_sys/card_image.cpp5
-rw-r--r--src/core/file_sys/card_image.h2
-rw-r--r--src/core/file_sys/patch_manager.cpp81
-rw-r--r--src/core/file_sys/patch_manager.h13
-rw-r--r--src/core/file_sys/romfs_factory.cpp6
-rw-r--r--src/core/file_sys/submission_package.cpp6
-rw-r--r--src/core/file_sys/submission_package.h4
-rw-r--r--src/core/frontend/applets/controller.cpp8
-rw-r--r--src/core/frontend/framebuffer_layout.cpp2
-rw-r--r--src/core/frontend/input.h13
-rw-r--r--src/core/hle/ipc_helpers.h9
-rw-r--r--src/core/hle/kernel/hle_ipc.h6
-rw-r--r--src/core/hle/kernel/svc.cpp2
-rw-r--r--src/core/hle/service/acc/acc.cpp7
-rw-r--r--src/core/hle/service/am/am.cpp69
-rw-r--r--src/core/hle/service/am/am.h12
-rw-r--r--src/core/hle/service/am/applet_ae.cpp23
-rw-r--r--src/core/hle/service/am/applet_ae.h4
-rw-r--r--src/core/hle/service/am/applet_oe.cpp14
-rw-r--r--src/core/hle/service/am/applet_oe.h4
-rw-r--r--src/core/hle/service/am/applets/controller.cpp84
-rw-r--r--src/core/hle/service/am/applets/controller.h26
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp4
-rw-r--r--src/core/hle/service/apm/apm.cpp1
-rw-r--r--src/core/hle/service/apm/controller.cpp3
-rw-r--r--src/core/hle/service/bcat/module.cpp1
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp1
-rw-r--r--src/core/hle/service/btm/btm.cpp1
-rw-r--r--src/core/hle/service/caps/caps_u.cpp25
-rw-r--r--src/core/hle/service/caps/caps_u.h1
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp7
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp8
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h7
-rw-r--r--src/core/hle/service/friend/friend.cpp1
-rw-r--r--src/core/hle/service/glue/arp.cpp1
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp270
-rw-r--r--src/core/hle/service/hid/controllers/npad.h131
-rw-r--r--src/core/hle/service/hid/hid.cpp703
-rw-r--r--src/core/hle/service/hid/hid.h37
-rw-r--r--src/core/hle/service/ldr/ldr.cpp1
-rw-r--r--src/core/hle/service/lm/lm.cpp1
-rw-r--r--src/core/hle/service/ns/ns.cpp24
-rw-r--r--src/core/hle/service/ns/ns.h28
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h36
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp21
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp157
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h110
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp75
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h109
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp192
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h55
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp185
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h153
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp88
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h25
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp43
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h110
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp42
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h18
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp70
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h24
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp119
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h68
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp199
-rw-r--r--src/core/hle/service/nvdrv/interface.h6
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h86
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp98
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h23
-rw-r--r--src/core/hle/service/olsc/olsc.cpp69
-rw-r--r--src/core/hle/service/olsc/olsc.h16
-rw-r--r--src/core/hle/service/pm/pm.cpp1
-rw-r--r--src/core/hle/service/prepo/prepo.cpp1
-rw-r--r--src/core/hle/service/service.cpp22
-rw-r--r--src/core/hle/service/service.h21
-rw-r--r--src/core/hle/service/vi/vi.cpp59
-rw-r--r--src/core/hle/service/vi/vi.h7
-rw-r--r--src/core/hle/service/vi/vi_m.cpp3
-rw-r--r--src/core/hle/service/vi/vi_m.h4
-rw-r--r--src/core/hle/service/vi/vi_s.cpp3
-rw-r--r--src/core/hle/service/vi/vi_s.h4
-rw-r--r--src/core/hle/service/vi/vi_u.cpp3
-rw-r--r--src/core/hle/service/vi/vi_u.h4
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp6
-rw-r--r--src/core/loader/loader.cpp29
-rw-r--r--src/core/loader/loader.h11
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/loader/nsp.cpp22
-rw-r--r--src/core/loader/nsp.h14
-rw-r--r--src/core/loader/xci.cpp19
-rw-r--r--src/core/loader/xci.h14
-rw-r--r--src/core/settings.cpp9
-rw-r--r--src/core/settings.h59
-rw-r--r--src/core/telemetry_session.cpp11
-rw-r--r--src/core/telemetry_session.h18
-rw-r--r--src/input_common/CMakeLists.txt4
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp6
-rw-r--r--src/input_common/gcadapter/gc_adapter.h4
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp52
-rw-r--r--src/input_common/gcadapter/gc_poller.h11
-rw-r--r--src/input_common/main.cpp15
-rw-r--r--src/input_common/motion_input.h23
-rw-r--r--src/input_common/sdl/sdl.h2
-rw-r--r--src/input_common/sdl/sdl_impl.cpp142
-rw-r--r--src/input_common/sdl/sdl_impl.h2
-rw-r--r--src/input_common/settings.cpp21
-rw-r--r--src/input_common/settings.h35
-rw-r--r--src/input_common/touch_from_button.cpp3
-rw-r--r--src/input_common/udp/client.cpp12
-rw-r--r--src/input_common/udp/protocol.h11
-rw-r--r--src/video_core/dma_pusher.cpp26
-rw-r--r--src/video_core/dma_pusher.h3
-rw-r--r--src/video_core/engines/maxwell_3d.cpp265
-rw-r--r--src/video_core/engines/maxwell_3d.h8
-rw-r--r--src/video_core/engines/shader_bytecode.h184
-rw-r--r--src/video_core/gpu.h66
-rw-r--r--src/video_core/rasterizer_interface.h17
-rw-r--r--src/video_core/renderer_base.h22
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp6
-rw-r--r--src/video_core/shader/async_shaders.cpp32
-rw-r--r--src/video_core/shader/decode/image.cpp4
-rw-r--r--src/yuzu/CMakeLists.txt11
-rw-r--r--src/yuzu/aboutdialog.ui20
-rw-r--r--src/yuzu/applets/controller.cpp154
-rw-r--r--src/yuzu/applets/controller.h19
-rw-r--r--src/yuzu/applets/controller.ui49
-rw-r--r--src/yuzu/bootmanager.cpp58
-rw-r--r--src/yuzu/bootmanager.h7
-rw-r--r--src/yuzu/configuration/config.cpp334
-rw-r--r--src/yuzu/configuration/config.h22
-rw-r--r--src/yuzu/configuration/configure.ui20
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui46
-rw-r--r--src/yuzu/configuration/configure_debug_controller.cpp7
-rw-r--r--src/yuzu/configuration/configure_debug_controller.h9
-rw-r--r--src/yuzu/configuration/configure_debug_controller.ui20
-rw-r--r--src/yuzu/configuration/configure_input.cpp76
-rw-r--r--src/yuzu/configuration/configure_input.h14
-rw-r--r--src/yuzu/configuration/configure_input.ui46
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp14
-rw-r--r--src/yuzu/configuration/configure_input_advanced.h2
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui192
-rw-r--r--src/yuzu/configuration/configure_input_dialog.cpp37
-rw-r--r--src/yuzu/configuration/configure_input_dialog.h38
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp566
-rw-r--r--src/yuzu/configuration/configure_input_player.h49
-rw-r--r--src/yuzu/configuration/configure_input_player.ui241
-rw-r--r--src/yuzu/configuration/configure_input_profile_dialog.cpp37
-rw-r--r--src/yuzu/configuration/configure_input_profile_dialog.h40
-rw-r--r--src/yuzu/configuration/configure_input_profile_dialog.ui (renamed from src/yuzu/configuration/configure_input_dialog.ui)24
-rw-r--r--src/yuzu/configuration/configure_motion_touch.ui10
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.ui46
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp10
-rw-r--r--src/yuzu/configuration/configure_per_game.ui20
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp6
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.ui10
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.ui22
-rw-r--r--src/yuzu/configuration/configure_vibration.cpp146
-rw-r--r--src/yuzu/configuration/configure_vibration.h43
-rw-r--r--src/yuzu/configuration/configure_vibration.ui546
-rw-r--r--src/yuzu/configuration/input_profiles.cpp131
-rw-r--r--src/yuzu/configuration/input_profiles.h32
-rw-r--r--src/yuzu/game_list_worker.cpp36
-rw-r--r--src/yuzu/main.cpp108
-rw-r--r--src/yuzu/main.h9
-rw-r--r--src/yuzu_cmd/config.cpp27
-rw-r--r--src/yuzu_cmd/default_ini.h8
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
-rw-r--r--src/yuzu_tester/config.cpp15
-rw-r--r--src/yuzu_tester/yuzu.cpp4
187 files changed, 5848 insertions, 3261 deletions
diff --git a/.ci/templates/build-msvc.yml b/.ci/templates/build-msvc.yml
index d85a949aa..0b4bbd341 100644
--- a/.ci/templates/build-msvc.yml
+++ b/.ci/templates/build-msvc.yml
@@ -4,9 +4,11 @@ parameters:
4 version: '' 4 version: ''
5 5
6steps: 6steps:
7- script: choco install vulkan-sdk
8 displayName: 'Install vulkan-sdk'
7- script: python -m pip install --upgrade pip conan 9- script: python -m pip install --upgrade pip conan
8 displayName: 'Install conan' 10 displayName: 'Install conan'
9- script: mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd .. 11- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
10 displayName: 'Configure CMake' 12 displayName: 'Configure CMake'
11- task: MSBuild@1 13- task: MSBuild@1
12 displayName: 'Build' 14 displayName: 'Build'
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8fc2de042..273260378 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -159,7 +159,7 @@ macro(yuzu_find_packages)
159 # Cmake Pkg Prefix Version Conan Pkg 159 # Cmake Pkg Prefix Version Conan Pkg
160 "Boost 1.73 boost/1.73.0" 160 "Boost 1.73 boost/1.73.0"
161 "Catch2 2.13 catch2/2.13.0" 161 "Catch2 2.13 catch2/2.13.0"
162 "fmt 7.1 fmt/7.1.0" 162 "fmt 7.1 fmt/7.1.2"
163 # can't use until https://github.com/bincrafters/community/issues/1173 163 # can't use until https://github.com/bincrafters/community/issues/1173
164 #"libzip 1.5 libzip/1.5.2@bincrafters/stable" 164 #"libzip 1.5 libzip/1.5.2@bincrafters/stable"
165 "lz4 1.8 lz4/1.9.2" 165 "lz4 1.8 lz4/1.9.2"
diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss
index b6dd2063d..836dd25ca 100644
--- a/dist/qt_themes/default/style.qss
+++ b/dist/qt_themes/default/style.qss
@@ -1,3 +1,7 @@
1QAbstractSpinBox {
2 min-height: 19px;
3}
4
1QPushButton#TogglableStatusBarButton { 5QPushButton#TogglableStatusBarButton {
2 color: #959595; 6 color: #959595;
3 border: 1px solid transparent; 7 border: 1px solid transparent;
@@ -35,10 +39,10 @@ QPushButton#RendererStatusBarButton:!checked {
35} 39}
36 40
37QPushButton#buttonRefreshDevices { 41QPushButton#buttonRefreshDevices {
38 min-width: 20px; 42 min-width: 21px;
39 min-height: 20px; 43 min-height: 21px;
40 max-width: 20px; 44 max-width: 21px;
41 max-height: 20px; 45 max-height: 21px;
42} 46}
43 47
44QWidget#bottomPerGameInput, 48QWidget#bottomPerGameInput,
@@ -71,7 +75,7 @@ QWidget#middleControllerApplet {
71 75
72QWidget#topPerGameInput QComboBox, 76QWidget#topPerGameInput QComboBox,
73QWidget#middleControllerApplet QComboBox { 77QWidget#middleControllerApplet QComboBox {
74 width: 123px; 78 width: 120px;
75} 79}
76 80
77QWidget#connectedControllers { 81QWidget#connectedControllers {
diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss
index 66026e8be..2a1e8ddeb 100644
--- a/dist/qt_themes/qdarkstyle/style.qss
+++ b/dist/qt_themes/qdarkstyle/style.qss
@@ -99,12 +99,19 @@ QGroupBox::indicator:unchecked:disabled {
99} 99}
100 100
101QRadioButton { 101QRadioButton {
102 spacing: 5px;
103 outline: none;
104 color: #eff0f1; 102 color: #eff0f1;
103 spacing: 3px;
104 padding: 0px;
105 border: none;
106 outline: none;
105 margin-bottom: 2px; 107 margin-bottom: 2px;
106} 108}
107 109
110QGroupBox QRadioButton {
111 padding-left: 0px;
112 padding-right: 7px;
113}
114
108QRadioButton:disabled { 115QRadioButton:disabled {
109 color: #76797C; 116 color: #76797C;
110} 117}
@@ -522,13 +529,12 @@ QToolButton#qt_toolbar_ext_button {
522 529
523QPushButton { 530QPushButton {
524 color: #eff0f1; 531 color: #eff0f1;
525 border-width: 1px; 532 border: 1px solid #54575B;
526 border-color: #54575B;
527 border-style: solid;
528 padding: 6px 4px;
529 border-radius: 2px; 533 border-radius: 2px;
534 padding: 5px 0px 5px 0px;
530 outline: none; 535 outline: none;
531 min-width: 100px; 536 min-width: 100px;
537 min-height: 13px;
532 background-color: #232629; 538 background-color: #232629;
533} 539}
534 540
@@ -553,8 +559,9 @@ QComboBox {
553 selection-background-color: #3daee9; 559 selection-background-color: #3daee9;
554 border: 1px solid #54575B; 560 border: 1px solid #54575B;
555 border-radius: 2px; 561 border-radius: 2px;
556 padding: 4px 6px; 562 padding: 0px 4px 0px 4px;
557 min-width: 75px; 563 min-width: 60px;
564 min-height: 23px;
558 background-color: #232629; 565 background-color: #232629;
559} 566}
560 567
@@ -608,26 +615,26 @@ QComboBox::down-arrow:focus {
608} 615}
609 616
610QAbstractSpinBox { 617QAbstractSpinBox {
611 padding: 4px 6px;
612 border: 1px solid #54575B; 618 border: 1px solid #54575B;
613 background-color: #232629; 619 background-color: #232629;
614 color: #eff0f1; 620 color: #eff0f1;
615 border-radius: 2px; 621 border-radius: 2px;
616 min-width: 75px; 622 min-width: 52px;
623 min-height: 23px;
617} 624}
618 625
619QAbstractSpinBox:up-button { 626QAbstractSpinBox:up-button {
620 background-color: transparent; 627 background-color: transparent;
621 subcontrol-origin: border; 628 subcontrol-origin: border;
622 subcontrol-position: center right; 629 subcontrol-position: center right;
623 left: -6px; 630 left: -2px;
624} 631}
625 632
626QAbstractSpinBox:down-button { 633QAbstractSpinBox:down-button {
627 background-color: transparent; 634 background-color: transparent;
628 subcontrol-origin: border; 635 subcontrol-origin: border;
629 subcontrol-position: center left; 636 subcontrol-position: center left;
630 right: -6px; 637 right: -2px;
631} 638}
632 639
633QAbstractSpinBox::up-arrow, 640QAbstractSpinBox::up-arrow,
@@ -1277,41 +1284,33 @@ QPushButton#RendererStatusBarButton:!checked {
1277} 1284}
1278 1285
1279QPushButton#buttonRefreshDevices { 1286QPushButton#buttonRefreshDevices {
1280 min-width: 24px; 1287 min-width: 23px;
1281 min-height: 24px; 1288 min-height: 23px;
1282 max-width: 24px; 1289 max-width: 23px;
1283 max-height: 24px; 1290 max-height: 23px;
1284 padding: 0px 0px; 1291 padding: 0px 0px;
1285} 1292}
1286 1293
1287QSpinBox#spinboxLStickRange, 1294QSpinBox#spinboxLStickRange,
1288QSpinBox#spinboxRStickRange { 1295QSpinBox#spinboxRStickRange,
1289 padding: 4px 0px 5px 0px; 1296QSpinBox#vibrationSpinPlayer1,
1290 min-width: 63px; 1297QSpinBox#vibrationSpinPlayer2,
1291} 1298QSpinBox#vibrationSpinPlayer3,
1292 1299QSpinBox#vibrationSpinPlayer4,
1293QSpinBox#vibrationSpin { 1300QSpinBox#vibrationSpinPlayer5,
1294 padding: 4px 0px 5px 0px; 1301QSpinBox#vibrationSpinPlayer6,
1295 min-width: 63px; 1302QSpinBox#vibrationSpinPlayer7,
1296} 1303QSpinBox#vibrationSpinPlayer8 {
1297 1304 min-width: 68px;
1298QSpinBox#spinboxLStickRange:up-button, 1305}
1299QSpinBox#spinboxRStickRange:up-button, 1306
1300QSpinBox#vibrationSpin:up-button { 1307QDialog#ConfigureVibration QGroupBox::indicator,
1301 left: -2px;
1302}
1303
1304QSpinBox#spinboxLStickRange:down-button,
1305QSpinBox#spinboxRStickRange:down-button,
1306QSpinBox#vibrationSpin:down-button {
1307 right: -1px;
1308}
1309
1310QGroupBox#motionGroup::indicator, 1308QGroupBox#motionGroup::indicator,
1311QGroupBox#vibrationGroup::indicator { 1309QGroupBox#vibrationGroup::indicator {
1312 margin-left: 0px; 1310 margin-left: 0px;
1313} 1311}
1314 1312
1313QDialog#ConfigureVibration QGroupBox::title,
1315QGroupBox#motionGroup::title, 1314QGroupBox#motionGroup::title,
1316QGroupBox#vibrationGroup::title { 1315QGroupBox#vibrationGroup::title {
1317 spacing: 2px; 1316 spacing: 2px;
@@ -1340,16 +1339,7 @@ QWidget#middleControllerApplet {
1340 1339
1341QWidget#topPerGameInput QComboBox, 1340QWidget#topPerGameInput QComboBox,
1342QWidget#middleControllerApplet QComboBox { 1341QWidget#middleControllerApplet QComboBox {
1343 width: 119px; 1342 width: 120px;
1344}
1345
1346QRadioButton#radioDocked {
1347 margin-left: -3px;
1348}
1349
1350
1351QRadioButton#radioUndocked {
1352 margin-right: 5px;
1353} 1343}
1354 1344
1355QWidget#connectedControllers { 1345QWidget#connectedControllers {
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
index c6318ba4e..70e540b06 100644
--- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
@@ -172,8 +172,8 @@ QCheckBox {
172 color: #F0F0F0; 172 color: #F0F0F0;
173 spacing: 4px; 173 spacing: 4px;
174 outline: none; 174 outline: none;
175 padding-top: 4px; 175 padding-top: 2px;
176 padding-bottom: 4px; 176 padding-bottom: 2px;
177} 177}
178 178
179QCheckBox:focus { 179QCheckBox:focus {
@@ -239,7 +239,7 @@ QGroupBox {
239 border: 1px solid #32414B; 239 border: 1px solid #32414B;
240 border-radius: 4px; 240 border-radius: 4px;
241 margin-top: 12px; 241 margin-top: 12px;
242 padding: 4px; 242 padding: 2px;
243} 243}
244 244
245QGroupBox::title { 245QGroupBox::title {
@@ -247,7 +247,7 @@ QGroupBox::title {
247 subcontrol-position: top left; 247 subcontrol-position: top left;
248 padding-left: 3px; 248 padding-left: 3px;
249 padding-right: 5px; 249 padding-right: 5px;
250 padding-top: 4px; 250 padding-top: 2px;
251} 251}
252 252
253QGroupBox::indicator { 253QGroupBox::indicator {
@@ -298,6 +298,11 @@ QRadioButton {
298 outline: none; 298 outline: none;
299} 299}
300 300
301QGroupBox QRadioButton {
302 padding-left: 0px;
303 padding-right: 7px;
304}
305
301QRadioButton:focus { 306QRadioButton:focus {
302 border: none; 307 border: none;
303} 308}
@@ -321,7 +326,6 @@ QRadioButton QWidget {
321QRadioButton::indicator { 326QRadioButton::indicator {
322 border: none; 327 border: none;
323 outline: none; 328 outline: none;
324 margin-left: 4px;
325 height: 16px; 329 height: 16px;
326 width: 16px; 330 width: 16px;
327} 331}
@@ -785,14 +789,8 @@ QAbstractSpinBox {
785 background-color: #19232D; 789 background-color: #19232D;
786 border: 1px solid #32414B; 790 border: 1px solid #32414B;
787 color: #F0F0F0; 791 color: #F0F0F0;
788 /* This fixes 103, 111 */
789 padding-top: 2px;
790 /* This fixes 103, 111 */
791 padding-bottom: 2px;
792 padding-left: 4px;
793 padding-right: 4px;
794 border-radius: 4px; 792 border-radius: 4px;
795 /* min-width: 5px; removed to fix 109 */ 793 min-height: 19px;
796} 794}
797 795
798QAbstractSpinBox:up-button { 796QAbstractSpinBox:up-button {
@@ -997,10 +995,11 @@ QPushButton {
997 border: 1px solid #32414B; 995 border: 1px solid #32414B;
998 color: #F0F0F0; 996 color: #F0F0F0;
999 border-radius: 4px; 997 border-radius: 4px;
1000 padding: 3px; 998 padding: 3px 0px 3px 0px;
1001 outline: none; 999 outline: none;
1002 /* Issue #194 - Special case of QPushButton inside dialogs, for better UI */ 1000 /* Issue #194 - Special case of QPushButton inside dialogs, for better UI */
1003 min-width: 80px; 1001 min-width: 80px;
1002 min-height: 13px;
1004} 1003}
1005 1004
1006QPushButton:disabled { 1005QPushButton:disabled {
@@ -1008,14 +1007,14 @@ QPushButton:disabled {
1008 border: 1px solid #32414B; 1007 border: 1px solid #32414B;
1009 color: #787878; 1008 color: #787878;
1010 border-radius: 4px; 1009 border-radius: 4px;
1011 padding: 3px; 1010 padding: 3px 0px 3px 0px;
1012} 1011}
1013 1012
1014QPushButton:checked { 1013QPushButton:checked {
1015 background-color: #32414B; 1014 background-color: #32414B;
1016 border: 1px solid #32414B; 1015 border: 1px solid #32414B;
1017 border-radius: 4px; 1016 border-radius: 4px;
1018 padding: 3px; 1017 padding: 3px 0px 3px 0px;
1019 outline: none; 1018 outline: none;
1020} 1019}
1021 1020
@@ -1024,7 +1023,7 @@ QPushButton:checked:disabled {
1024 border: 1px solid #32414B; 1023 border: 1px solid #32414B;
1025 color: #787878; 1024 color: #787878;
1026 border-radius: 4px; 1025 border-radius: 4px;
1027 padding: 3px; 1026 padding: 3px 0px 3px 0px;
1028 outline: none; 1027 outline: none;
1029} 1028}
1030 1029
@@ -1197,15 +1196,9 @@ QComboBox {
1197 border: 1px solid #32414B; 1196 border: 1px solid #32414B;
1198 border-radius: 4px; 1197 border-radius: 4px;
1199 selection-background-color: #1464A0; 1198 selection-background-color: #1464A0;
1200 padding-left: 4px; 1199 padding: 0px 4px 0px 4px;
1201 padding-right: 36px; 1200 min-width: 60px;
1202 /* 4 + 16*2 See scrollbar size */ 1201 min-height: 19px;
1203 /* Fixes #103, #111 */
1204 min-height: 1.5em;
1205 /* padding-top: 2px; removed to fix #132 */
1206 /* padding-bottom: 2px; removed to fix #132 */
1207 /* min-width: 75px; removed to fix #109 */
1208 /* Needed to remove indicator - fix #132 */
1209} 1202}
1210 1203
1211QComboBox QAbstractItemView { 1204QComboBox QAbstractItemView {
@@ -2198,29 +2191,40 @@ QPushButton#RendererStatusBarButton:!checked {
2198} 2191}
2199 2192
2200QPushButton#buttonRefreshDevices { 2193QPushButton#buttonRefreshDevices {
2201 min-width: 20px; 2194 min-width: 19px;
2202 min-height: 20px; 2195 min-height: 19px;
2203 max-width: 20px; 2196 max-width: 19px;
2204 max-height: 20px; 2197 max-height: 19px;
2205 padding: 0px 0px; 2198 padding: 0px 0px;
2206} 2199}
2207 2200
2208QSpinBox#spinboxLStickRange, 2201QSpinBox#spinboxLStickRange,
2209QSpinBox#spinboxRStickRange { 2202QSpinBox#spinboxRStickRange,
2210 min-width: 38px; 2203QSpinBox#vibrationSpinPlayer1,
2211} 2204QSpinBox#vibrationSpinPlayer2,
2212 2205QSpinBox#vibrationSpinPlayer3,
2206QSpinBox#vibrationSpinPlayer4,
2207QSpinBox#vibrationSpinPlayer5,
2208QSpinBox#vibrationSpinPlayer6,
2209QSpinBox#vibrationSpinPlayer7,
2210QSpinBox#vibrationSpinPlayer8 {
2211 min-width: 68px;
2212}
2213
2214QDialog#ConfigureVibration QGroupBox::indicator,
2213QGroupBox#motionGroup::indicator, 2215QGroupBox#motionGroup::indicator,
2214QGroupBox#vibrationGroup::indicator { 2216QGroupBox#vibrationGroup::indicator {
2215 margin-left: 0px; 2217 margin-left: 0px;
2216} 2218}
2217 2219
2220QDialog#ConfigureVibration QGroupBox,
2218QWidget#bottomPerGameInput QGroupBox#motionGroup, 2221QWidget#bottomPerGameInput QGroupBox#motionGroup,
2219QWidget#bottomPerGameInput QGroupBox#vibrationGroup, 2222QWidget#bottomPerGameInput QGroupBox#vibrationGroup,
2220QWidget#bottomPerGameInput QGroupBox#inputConfigGroup { 2223QWidget#bottomPerGameInput QGroupBox#inputConfigGroup {
2221 padding: 0px; 2224 padding: 0px;
2222} 2225}
2223 2226
2227QDialog#ConfigureVibration QGroupBox::title,
2224QGroupBox#motionGroup::title, 2228QGroupBox#motionGroup::title,
2225QGroupBox#vibrationGroup::title { 2229QGroupBox#vibrationGroup::title {
2226 spacing: 2px; 2230 spacing: 2px;
@@ -2260,26 +2264,7 @@ QWidget#middleControllerApplet {
2260 2264
2261QWidget#topPerGameInput QComboBox, 2265QWidget#topPerGameInput QComboBox,
2262QWidget#middleControllerApplet QComboBox { 2266QWidget#middleControllerApplet QComboBox {
2263 padding-right: 2px; 2267 width: 120px;
2264 width: 127px;
2265}
2266
2267QGroupBox#handheldGroup {
2268 padding-left: 0px;
2269}
2270
2271QRadioButton#radioDocked {
2272 margin-left: -1px;
2273 padding-left: 0px;
2274}
2275
2276QRadioButton#radioDocked::indicator {
2277 margin-left: 0px;
2278}
2279
2280
2281QRadioButton#radioUndocked {
2282 margin-right: 2px;
2283} 2268}
2284 2269
2285QWidget#connectedControllers { 2270QWidget#connectedControllers {
@@ -2352,7 +2337,7 @@ QCheckBox#checkboxPlayer5Connected,
2352QCheckBox#checkboxPlayer6Connected, 2337QCheckBox#checkboxPlayer6Connected,
2353QCheckBox#checkboxPlayer7Connected, 2338QCheckBox#checkboxPlayer7Connected,
2354QCheckBox#checkboxPlayer8Connected { 2339QCheckBox#checkboxPlayer8Connected {
2355 spacing: 0px; 2340 spacing: 0px;
2356} 2341}
2357 2342
2358QWidget#connectedControllers QLabel { 2343QWidget#connectedControllers QLabel {
@@ -2427,7 +2412,7 @@ QCheckBox#checkboxPlayer7Connected::indicator,
2427QCheckBox#checkboxPlayer8Connected::indicator { 2412QCheckBox#checkboxPlayer8Connected::indicator {
2428 width: 14px; 2413 width: 14px;
2429 height: 14px; 2414 height: 14px;
2430 margin-left: 2px; 2415 margin-left: 0px;
2431} 2416}
2432 2417
2433QWidget#Player1LEDs QCheckBox::indicator:checked, 2418QWidget#Player1LEDs QCheckBox::indicator:checked,
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 207c7a0a6..d20e6c3b5 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -102,6 +102,7 @@ add_library(common STATIC
102 atomic_ops.h 102 atomic_ops.h
103 detached_tasks.cpp 103 detached_tasks.cpp
104 detached_tasks.h 104 detached_tasks.h
105 bit_cast.h
105 bit_field.h 106 bit_field.h
106 bit_util.h 107 bit_util.h
107 cityhash.cpp 108 cityhash.cpp
diff --git a/src/common/bit_cast.h b/src/common/bit_cast.h
new file mode 100644
index 000000000..a32a063d1
--- /dev/null
+++ b/src/common/bit_cast.h
@@ -0,0 +1,22 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstring>
8#include <type_traits>
9
10namespace Common {
11
12template <typename To, typename From>
13[[nodiscard]] std::enable_if_t<sizeof(To) == sizeof(From) && std::is_trivially_copyable_v<From> &&
14 std::is_trivially_copyable_v<To>,
15 To>
16BitCast(const From& src) noexcept {
17 To dst;
18 std::memcpy(&dst, &src, sizeof(To));
19 return dst;
20}
21
22} // namespace Common
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 90dfa22ca..631f64d05 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -23,6 +23,7 @@
23#include "common/logging/text_formatter.h" 23#include "common/logging/text_formatter.h"
24#include "common/string_util.h" 24#include "common/string_util.h"
25#include "common/threadsafe_queue.h" 25#include "common/threadsafe_queue.h"
26#include "core/settings.h"
26 27
27namespace Log { 28namespace Log {
28 29
@@ -152,10 +153,19 @@ FileBackend::FileBackend(const std::string& filename)
152void FileBackend::Write(const Entry& entry) { 153void FileBackend::Write(const Entry& entry) {
153 // prevent logs from going over the maximum size (in case its spamming and the user doesn't 154 // prevent logs from going over the maximum size (in case its spamming and the user doesn't
154 // know) 155 // know)
155 constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L; 156 constexpr std::size_t MAX_BYTES_WRITTEN = 100 * 1024 * 1024;
156 if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { 157 constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1024 * 1024 * 1024;
158
159 if (!file.IsOpen()) {
160 return;
161 }
162
163 if (Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN_EXTENDED) {
164 return;
165 } else if (!Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN) {
157 return; 166 return;
158 } 167 }
168
159 bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n')); 169 bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
160 if (entry.log_level >= Level::Error) { 170 if (entry.log_level >= Level::Error) {
161 file.Flush(); 171 file.Flush();
@@ -222,6 +232,7 @@ void DebuggerBackend::Write(const Entry& entry) {
222 SUB(Service, NPNS) \ 232 SUB(Service, NPNS) \
223 SUB(Service, NS) \ 233 SUB(Service, NS) \
224 SUB(Service, NVDRV) \ 234 SUB(Service, NVDRV) \
235 SUB(Service, OLSC) \
225 SUB(Service, PCIE) \ 236 SUB(Service, PCIE) \
226 SUB(Service, PCTL) \ 237 SUB(Service, PCTL) \
227 SUB(Service, PCV) \ 238 SUB(Service, PCV) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 13a4f1e30..835894918 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -95,6 +95,7 @@ enum class Class : ClassType {
95 Service_NPNS, ///< The NPNS service 95 Service_NPNS, ///< The NPNS service
96 Service_NS, ///< The NS services 96 Service_NS, ///< The NS services
97 Service_NVDRV, ///< The NVDRV (Nvidia driver) service 97 Service_NVDRV, ///< The NVDRV (Nvidia driver) service
98 Service_OLSC, ///< The OLSC service
98 Service_PCIE, ///< The PCIe service 99 Service_PCIE, ///< The PCIe service
99 Service_PCTL, ///< The PCTL (Parental control) service 100 Service_PCTL, ///< The PCTL (Parental control) service
100 Service_PCV, ///< The PCV service 101 Service_PCV, ///< The PCV service
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index e5d3090d5..bccea0894 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -8,7 +8,7 @@ namespace Common {
8 8
9PageTable::PageTable() = default; 9PageTable::PageTable() = default;
10 10
11PageTable::~PageTable() = default; 11PageTable::~PageTable() noexcept = default;
12 12
13void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, 13void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
14 bool has_attribute) { 14 bool has_attribute) {
diff --git a/src/common/page_table.h b/src/common/page_table.h
index cf5eed780..9754fabf9 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -4,9 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <vector> 7#include <tuple>
8
9#include <boost/icl/interval_map.hpp>
10 8
11#include "common/common_types.h" 9#include "common/common_types.h"
12#include "common/memory_hook.h" 10#include "common/memory_hook.h"
@@ -51,13 +49,21 @@ struct SpecialRegion {
51 */ 49 */
52struct PageTable { 50struct PageTable {
53 PageTable(); 51 PageTable();
54 ~PageTable(); 52 ~PageTable() noexcept;
53
54 PageTable(const PageTable&) = delete;
55 PageTable& operator=(const PageTable&) = delete;
56
57 PageTable(PageTable&&) noexcept = default;
58 PageTable& operator=(PageTable&&) noexcept = default;
55 59
56 /** 60 /**
57 * Resizes the page table to be able to accomodate enough pages within 61 * Resizes the page table to be able to accomodate enough pages within
58 * a given address space. 62 * a given address space.
59 * 63 *
60 * @param address_space_width_in_bits The address size width in bits. 64 * @param address_space_width_in_bits The address size width in bits.
65 * @param page_size_in_bits The page size in bits.
66 * @param has_attribute Whether or not this page has any backing attributes.
61 */ 67 */
62 void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, 68 void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
63 bool has_attribute); 69 bool has_attribute);
diff --git a/src/common/virtual_buffer.cpp b/src/common/virtual_buffer.cpp
index b009cb500..e3ca29258 100644
--- a/src/common/virtual_buffer.cpp
+++ b/src/common/virtual_buffer.cpp
@@ -13,7 +13,7 @@
13 13
14namespace Common { 14namespace Common {
15 15
16void* AllocateMemoryPages(std::size_t size) { 16void* AllocateMemoryPages(std::size_t size) noexcept {
17#ifdef _WIN32 17#ifdef _WIN32
18 void* base{VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE)}; 18 void* base{VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE)};
19#else 19#else
@@ -29,7 +29,7 @@ void* AllocateMemoryPages(std::size_t size) {
29 return base; 29 return base;
30} 30}
31 31
32void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) { 32void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
33 if (!base) { 33 if (!base) {
34 return; 34 return;
35 } 35 }
diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h
index 125cb42f0..91d430036 100644
--- a/src/common/virtual_buffer.h
+++ b/src/common/virtual_buffer.h
@@ -4,29 +4,53 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_funcs.h" 7#include <type_traits>
8#include <utility>
8 9
9namespace Common { 10namespace Common {
10 11
11void* AllocateMemoryPages(std::size_t size); 12void* AllocateMemoryPages(std::size_t size) noexcept;
12void FreeMemoryPages(void* base, std::size_t size); 13void FreeMemoryPages(void* base, std::size_t size) noexcept;
13 14
14template <typename T> 15template <typename T>
15class VirtualBuffer final : NonCopyable { 16class VirtualBuffer final {
16public: 17public:
18 static_assert(
19 std::is_trivially_constructible_v<T>,
20 "T must be trivially constructible, as non-trivial constructors will not be executed "
21 "with the current allocator");
22
17 constexpr VirtualBuffer() = default; 23 constexpr VirtualBuffer() = default;
18 explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} { 24 explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
19 base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size)); 25 base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
20 } 26 }
21 27
22 ~VirtualBuffer() { 28 ~VirtualBuffer() noexcept {
23 FreeMemoryPages(base_ptr, alloc_size); 29 FreeMemoryPages(base_ptr, alloc_size);
24 } 30 }
25 31
32 VirtualBuffer(const VirtualBuffer&) = delete;
33 VirtualBuffer& operator=(const VirtualBuffer&) = delete;
34
35 VirtualBuffer(VirtualBuffer&& other) noexcept
36 : alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr),
37 nullptr} {}
38
39 VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
40 alloc_size = std::exchange(other.alloc_size, 0);
41 base_ptr = std::exchange(other.base_ptr, nullptr);
42 return *this;
43 }
44
26 void resize(std::size_t count) { 45 void resize(std::size_t count) {
46 const auto new_size = count * sizeof(T);
47 if (new_size == alloc_size) {
48 return;
49 }
50
27 FreeMemoryPages(base_ptr, alloc_size); 51 FreeMemoryPages(base_ptr, alloc_size);
28 52
29 alloc_size = count * sizeof(T); 53 alloc_size = new_size;
30 base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size)); 54 base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
31 } 55 }
32 56
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index a1d8dcfa5..e370fd225 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -458,6 +458,8 @@ add_library(core STATIC
458 hle/service/nvflinger/buffer_queue.h 458 hle/service/nvflinger/buffer_queue.h
459 hle/service/nvflinger/nvflinger.cpp 459 hle/service/nvflinger/nvflinger.cpp
460 hle/service/nvflinger/nvflinger.h 460 hle/service/nvflinger/nvflinger.h
461 hle/service/olsc/olsc.cpp
462 hle/service/olsc/olsc.h
461 hle/service/pcie/pcie.cpp 463 hle/service/pcie/pcie.cpp
462 hle/service/pcie/pcie.h 464 hle/service/pcie/pcie.h
463 hle/service/pctl/module.cpp 465 hle/service/pctl/module.cpp
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
index 71e582f79..c20c280f1 100644
--- a/src/core/arm/cpu_interrupt_handler.h
+++ b/src/core/arm/cpu_interrupt_handler.h
@@ -21,8 +21,8 @@ public:
21 CPUInterruptHandler(const CPUInterruptHandler&) = delete; 21 CPUInterruptHandler(const CPUInterruptHandler&) = delete;
22 CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete; 22 CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete;
23 23
24 CPUInterruptHandler(CPUInterruptHandler&&) = default; 24 CPUInterruptHandler(CPUInterruptHandler&&) = delete;
25 CPUInterruptHandler& operator=(CPUInterruptHandler&&) = default; 25 CPUInterruptHandler& operator=(CPUInterruptHandler&&) = delete;
26 26
27 bool IsInterrupted() const { 27 bool IsInterrupted() const {
28 return is_interrupted; 28 return is_interrupted;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 242796008..7ca3652af 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -145,7 +145,7 @@ struct System::Impl {
145 } 145 }
146 146
147 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { 147 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
148 LOG_DEBUG(HW_Memory, "initialized OK"); 148 LOG_DEBUG(Core, "initialized OK");
149 149
150 device_memory = std::make_unique<Core::DeviceMemory>(); 150 device_memory = std::make_unique<Core::DeviceMemory>();
151 151
@@ -187,7 +187,7 @@ struct System::Impl {
187 187
188 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); 188 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
189 189
190 Service::Init(service_manager, system); 190 services = std::make_unique<Service::Services>(service_manager, system);
191 GDBStub::DeferStart(); 191 GDBStub::DeferStart();
192 192
193 interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); 193 interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
@@ -208,9 +208,11 @@ struct System::Impl {
208 return ResultStatus::Success; 208 return ResultStatus::Success;
209 } 209 }
210 210
211 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, 211 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath,
212 const std::string& filepath) { 212 std::size_t program_index) {
213 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); 213 app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath),
214 program_index);
215
214 if (!app_loader) { 216 if (!app_loader) {
215 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); 217 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
216 return ResultStatus::ErrorGetLoader; 218 return ResultStatus::ErrorGetLoader;
@@ -224,7 +226,7 @@ struct System::Impl {
224 return init_result; 226 return init_result;
225 } 227 }
226 228
227 telemetry_session->AddInitialInfo(*app_loader); 229 telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
228 auto main_process = 230 auto main_process =
229 Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland); 231 Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland);
230 const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); 232 const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
@@ -296,7 +298,7 @@ struct System::Impl {
296 298
297 // Shutdown emulation session 299 // Shutdown emulation session
298 GDBStub::Shutdown(); 300 GDBStub::Shutdown();
299 Service::Shutdown(); 301 services.reset();
300 service_manager.reset(); 302 service_manager.reset();
301 cheat_engine.reset(); 303 cheat_engine.reset();
302 telemetry_session.reset(); 304 telemetry_session.reset();
@@ -306,8 +308,8 @@ struct System::Impl {
306 cpu_manager.Shutdown(); 308 cpu_manager.Shutdown();
307 309
308 // Shutdown kernel and core timing 310 // Shutdown kernel and core timing
309 kernel.Shutdown();
310 core_timing.Shutdown(); 311 core_timing.Shutdown();
312 kernel.Shutdown();
311 313
312 // Close app loader 314 // Close app loader
313 app_loader.reset(); 315 app_loader.reset();
@@ -338,7 +340,7 @@ struct System::Impl {
338 Service::Glue::ApplicationLaunchProperty launch{}; 340 Service::Glue::ApplicationLaunchProperty launch{};
339 launch.title_id = process.GetTitleID(); 341 launch.title_id = process.GetTitleID();
340 342
341 FileSys::PatchManager pm{launch.title_id}; 343 FileSys::PatchManager pm{launch.title_id, fs_controller, *content_provider};
342 launch.version = pm.GetGameVersion().value_or(0); 344 launch.version = pm.GetGameVersion().value_or(0);
343 345
344 // TODO(DarkLordZach): When FSController/Game Card Support is added, if 346 // TODO(DarkLordZach): When FSController/Game Card Support is added, if
@@ -398,6 +400,9 @@ struct System::Impl {
398 /// Service manager 400 /// Service manager
399 std::shared_ptr<Service::SM::ServiceManager> service_manager; 401 std::shared_ptr<Service::SM::ServiceManager> service_manager;
400 402
403 /// Services
404 std::unique_ptr<Service::Services> services;
405
401 /// Telemetry session for this emulation session 406 /// Telemetry session for this emulation session
402 std::unique_ptr<Core::TelemetrySession> telemetry_session; 407 std::unique_ptr<Core::TelemetrySession> telemetry_session;
403 408
@@ -413,6 +418,8 @@ struct System::Impl {
413 bool is_multicore{}; 418 bool is_multicore{};
414 bool is_async_gpu{}; 419 bool is_async_gpu{};
415 420
421 ExecuteProgramCallback execute_program_callback;
422
416 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; 423 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
417 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{}; 424 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{};
418}; 425};
@@ -444,8 +451,13 @@ void System::InvalidateCpuInstructionCaches() {
444 impl->kernel.InvalidateAllInstructionCaches(); 451 impl->kernel.InvalidateAllInstructionCaches();
445} 452}
446 453
447System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 454void System::Shutdown() {
448 return impl->Load(*this, emu_window, filepath); 455 impl->Shutdown();
456}
457
458System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
459 std::size_t program_index) {
460 return impl->Load(*this, emu_window, filepath, program_index);
449} 461}
450 462
451bool System::IsPoweredOn() const { 463bool System::IsPoweredOn() const {
@@ -632,7 +644,11 @@ const std::string& System::GetStatusDetails() const {
632 return impl->status_details; 644 return impl->status_details;
633} 645}
634 646
635Loader::AppLoader& System::GetAppLoader() const { 647Loader::AppLoader& System::GetAppLoader() {
648 return *impl->app_loader;
649}
650
651const Loader::AppLoader& System::GetAppLoader() const {
636 return *impl->app_loader; 652 return *impl->app_loader;
637} 653}
638 654
@@ -748,14 +764,6 @@ const System::CurrentBuildProcessID& System::GetCurrentProcessBuildID() const {
748 return impl->build_id; 764 return impl->build_id;
749} 765}
750 766
751System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
752 return impl->Init(*this, emu_window);
753}
754
755void System::Shutdown() {
756 impl->Shutdown();
757}
758
759Service::SM::ServiceManager& System::ServiceManager() { 767Service::SM::ServiceManager& System::ServiceManager() {
760 return *impl->service_manager; 768 return *impl->service_manager;
761} 769}
@@ -786,4 +794,16 @@ bool System::IsMulticore() const {
786 return impl->is_multicore; 794 return impl->is_multicore;
787} 795}
788 796
797void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
798 impl->execute_program_callback = std::move(callback);
799}
800
801void System::ExecuteProgram(std::size_t program_index) {
802 if (impl->execute_program_callback) {
803 impl->execute_program_callback(program_index);
804 } else {
805 LOG_CRITICAL(Core, "execute_program_callback must be initialized by the frontend");
806 }
807}
808
789} // namespace Core 809} // namespace Core
diff --git a/src/core/core.h b/src/core/core.h
index 6db896bae..f642befc0 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <functional>
8#include <memory> 9#include <memory>
9#include <string> 10#include <string>
10#include <vector> 11#include <vector>
@@ -144,19 +145,19 @@ public:
144 * Run the OS and Application 145 * Run the OS and Application
145 * This function will start emulation and run the relevant devices 146 * This function will start emulation and run the relevant devices
146 */ 147 */
147 ResultStatus Run(); 148 [[nodiscard]] ResultStatus Run();
148 149
149 /** 150 /**
150 * Pause the OS and Application 151 * Pause the OS and Application
151 * This function will pause emulation and stop the relevant devices 152 * This function will pause emulation and stop the relevant devices
152 */ 153 */
153 ResultStatus Pause(); 154 [[nodiscard]] ResultStatus Pause();
154 155
155 /** 156 /**
156 * Step the CPU one instruction 157 * Step the CPU one instruction
157 * @return Result status, indicating whether or not the operation succeeded. 158 * @return Result status, indicating whether or not the operation succeeded.
158 */ 159 */
159 ResultStatus SingleStep(); 160 [[nodiscard]] ResultStatus SingleStep();
160 161
161 /** 162 /**
162 * Invalidate the CPU instruction caches 163 * Invalidate the CPU instruction caches
@@ -173,22 +174,24 @@ public:
173 * @param emu_window Reference to the host-system window used for video output and keyboard 174 * @param emu_window Reference to the host-system window used for video output and keyboard
174 * input. 175 * input.
175 * @param filepath String path to the executable application to load on the host file system. 176 * @param filepath String path to the executable application to load on the host file system.
177 * @param program_index Specifies the index within the container of the program to launch.
176 * @returns ResultStatus code, indicating if the operation succeeded. 178 * @returns ResultStatus code, indicating if the operation succeeded.
177 */ 179 */
178 ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath); 180 [[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
181 std::size_t program_index = 0);
179 182
180 /** 183 /**
181 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an 184 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an
182 * application). 185 * application).
183 * @returns True if the emulated system is powered on, otherwise false. 186 * @returns True if the emulated system is powered on, otherwise false.
184 */ 187 */
185 bool IsPoweredOn() const; 188 [[nodiscard]] bool IsPoweredOn() const;
186 189
187 /// Gets a reference to the telemetry session for this emulation session. 190 /// Gets a reference to the telemetry session for this emulation session.
188 Core::TelemetrySession& TelemetrySession(); 191 [[nodiscard]] Core::TelemetrySession& TelemetrySession();
189 192
190 /// Gets a reference to the telemetry session for this emulation session. 193 /// Gets a reference to the telemetry session for this emulation session.
191 const Core::TelemetrySession& TelemetrySession() const; 194 [[nodiscard]] const Core::TelemetrySession& TelemetrySession() const;
192 195
193 /// Prepare the core emulation for a reschedule 196 /// Prepare the core emulation for a reschedule
194 void PrepareReschedule(); 197 void PrepareReschedule();
@@ -197,185 +200,178 @@ public:
197 void PrepareReschedule(u32 core_index); 200 void PrepareReschedule(u32 core_index);
198 201
199 /// Gets and resets core performance statistics 202 /// Gets and resets core performance statistics
200 PerfStatsResults GetAndResetPerfStats(); 203 [[nodiscard]] PerfStatsResults GetAndResetPerfStats();
201 204
202 /// Gets an ARM interface to the CPU core that is currently running 205 /// Gets an ARM interface to the CPU core that is currently running
203 ARM_Interface& CurrentArmInterface(); 206 [[nodiscard]] ARM_Interface& CurrentArmInterface();
204 207
205 /// Gets an ARM interface to the CPU core that is currently running 208 /// Gets an ARM interface to the CPU core that is currently running
206 const ARM_Interface& CurrentArmInterface() const; 209 [[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
207 210
208 /// Gets the index of the currently running CPU core 211 /// Gets the index of the currently running CPU core
209 std::size_t CurrentCoreIndex() const; 212 [[nodiscard]] std::size_t CurrentCoreIndex() const;
210 213
211 /// Gets the scheduler for the CPU core that is currently running 214 /// Gets the scheduler for the CPU core that is currently running
212 Kernel::Scheduler& CurrentScheduler(); 215 [[nodiscard]] Kernel::Scheduler& CurrentScheduler();
213 216
214 /// Gets the scheduler for the CPU core that is currently running 217 /// Gets the scheduler for the CPU core that is currently running
215 const Kernel::Scheduler& CurrentScheduler() const; 218 [[nodiscard]] const Kernel::Scheduler& CurrentScheduler() const;
216 219
217 /// Gets the physical core for the CPU core that is currently running 220 /// Gets the physical core for the CPU core that is currently running
218 Kernel::PhysicalCore& CurrentPhysicalCore(); 221 [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();
219 222
220 /// Gets the physical core for the CPU core that is currently running 223 /// Gets the physical core for the CPU core that is currently running
221 const Kernel::PhysicalCore& CurrentPhysicalCore() const; 224 [[nodiscard]] const Kernel::PhysicalCore& CurrentPhysicalCore() const;
222 225
223 /// Gets a reference to an ARM interface for the CPU core with the specified index 226 /// Gets a reference to an ARM interface for the CPU core with the specified index
224 ARM_Interface& ArmInterface(std::size_t core_index); 227 [[nodiscard]] ARM_Interface& ArmInterface(std::size_t core_index);
225 228
226 /// Gets a const reference to an ARM interface from the CPU core with the specified index 229 /// Gets a const reference to an ARM interface from the CPU core with the specified index
227 const ARM_Interface& ArmInterface(std::size_t core_index) const; 230 [[nodiscard]] const ARM_Interface& ArmInterface(std::size_t core_index) const;
228 231
229 CpuManager& GetCpuManager(); 232 /// Gets a reference to the underlying CPU manager.
233 [[nodiscard]] CpuManager& GetCpuManager();
230 234
231 const CpuManager& GetCpuManager() const; 235 /// Gets a const reference to the underlying CPU manager
236 [[nodiscard]] const CpuManager& GetCpuManager() const;
232 237
233 /// Gets a reference to the exclusive monitor 238 /// Gets a reference to the exclusive monitor
234 ExclusiveMonitor& Monitor(); 239 [[nodiscard]] ExclusiveMonitor& Monitor();
235 240
236 /// Gets a constant reference to the exclusive monitor 241 /// Gets a constant reference to the exclusive monitor
237 const ExclusiveMonitor& Monitor() const; 242 [[nodiscard]] const ExclusiveMonitor& Monitor() const;
238 243
239 /// Gets a mutable reference to the system memory instance. 244 /// Gets a mutable reference to the system memory instance.
240 Core::Memory::Memory& Memory(); 245 [[nodiscard]] Core::Memory::Memory& Memory();
241 246
242 /// Gets a constant reference to the system memory instance. 247 /// Gets a constant reference to the system memory instance.
243 const Core::Memory::Memory& Memory() const; 248 [[nodiscard]] const Core::Memory::Memory& Memory() const;
244 249
245 /// Gets a mutable reference to the GPU interface 250 /// Gets a mutable reference to the GPU interface
246 Tegra::GPU& GPU(); 251 [[nodiscard]] Tegra::GPU& GPU();
247 252
248 /// Gets an immutable reference to the GPU interface. 253 /// Gets an immutable reference to the GPU interface.
249 const Tegra::GPU& GPU() const; 254 [[nodiscard]] const Tegra::GPU& GPU() const;
250 255
251 /// Gets a mutable reference to the renderer. 256 /// Gets a mutable reference to the renderer.
252 VideoCore::RendererBase& Renderer(); 257 [[nodiscard]] VideoCore::RendererBase& Renderer();
253 258
254 /// Gets an immutable reference to the renderer. 259 /// Gets an immutable reference to the renderer.
255 const VideoCore::RendererBase& Renderer() const; 260 [[nodiscard]] const VideoCore::RendererBase& Renderer() const;
256 261
257 /// Gets the scheduler for the CPU core with the specified index 262 /// Gets the scheduler for the CPU core with the specified index
258 Kernel::Scheduler& Scheduler(std::size_t core_index); 263 [[nodiscard]] Kernel::Scheduler& Scheduler(std::size_t core_index);
259 264
260 /// Gets the scheduler for the CPU core with the specified index 265 /// Gets the scheduler for the CPU core with the specified index
261 const Kernel::Scheduler& Scheduler(std::size_t core_index) const; 266 [[nodiscard]] const Kernel::Scheduler& Scheduler(std::size_t core_index) const;
262 267
263 /// Gets the global scheduler 268 /// Gets the global scheduler
264 Kernel::GlobalScheduler& GlobalScheduler(); 269 [[nodiscard]] Kernel::GlobalScheduler& GlobalScheduler();
265 270
266 /// Gets the global scheduler 271 /// Gets the global scheduler
267 const Kernel::GlobalScheduler& GlobalScheduler() const; 272 [[nodiscard]] const Kernel::GlobalScheduler& GlobalScheduler() const;
268 273
269 /// Gets the manager for the guest device memory 274 /// Gets the manager for the guest device memory
270 Core::DeviceMemory& DeviceMemory(); 275 [[nodiscard]] Core::DeviceMemory& DeviceMemory();
271 276
272 /// Gets the manager for the guest device memory 277 /// Gets the manager for the guest device memory
273 const Core::DeviceMemory& DeviceMemory() const; 278 [[nodiscard]] const Core::DeviceMemory& DeviceMemory() const;
274 279
275 /// Provides a pointer to the current process 280 /// Provides a pointer to the current process
276 Kernel::Process* CurrentProcess(); 281 [[nodiscard]] Kernel::Process* CurrentProcess();
277 282
278 /// Provides a constant pointer to the current process. 283 /// Provides a constant pointer to the current process.
279 const Kernel::Process* CurrentProcess() const; 284 [[nodiscard]] const Kernel::Process* CurrentProcess() const;
280 285
281 /// Provides a reference to the core timing instance. 286 /// Provides a reference to the core timing instance.
282 Timing::CoreTiming& CoreTiming(); 287 [[nodiscard]] Timing::CoreTiming& CoreTiming();
283 288
284 /// Provides a constant reference to the core timing instance. 289 /// Provides a constant reference to the core timing instance.
285 const Timing::CoreTiming& CoreTiming() const; 290 [[nodiscard]] const Timing::CoreTiming& CoreTiming() const;
286 291
287 /// Provides a reference to the interrupt manager instance. 292 /// Provides a reference to the interrupt manager instance.
288 Core::Hardware::InterruptManager& InterruptManager(); 293 [[nodiscard]] Core::Hardware::InterruptManager& InterruptManager();
289 294
290 /// Provides a constant reference to the interrupt manager instance. 295 /// Provides a constant reference to the interrupt manager instance.
291 const Core::Hardware::InterruptManager& InterruptManager() const; 296 [[nodiscard]] const Core::Hardware::InterruptManager& InterruptManager() const;
292 297
293 /// Provides a reference to the kernel instance. 298 /// Provides a reference to the kernel instance.
294 Kernel::KernelCore& Kernel(); 299 [[nodiscard]] Kernel::KernelCore& Kernel();
295 300
296 /// Provides a constant reference to the kernel instance. 301 /// Provides a constant reference to the kernel instance.
297 const Kernel::KernelCore& Kernel() const; 302 [[nodiscard]] const Kernel::KernelCore& Kernel() const;
298 303
299 /// Provides a reference to the internal PerfStats instance. 304 /// Provides a reference to the internal PerfStats instance.
300 Core::PerfStats& GetPerfStats(); 305 [[nodiscard]] Core::PerfStats& GetPerfStats();
301 306
302 /// Provides a constant reference to the internal PerfStats instance. 307 /// Provides a constant reference to the internal PerfStats instance.
303 const Core::PerfStats& GetPerfStats() const; 308 [[nodiscard]] const Core::PerfStats& GetPerfStats() const;
304 309
305 /// Provides a reference to the frame limiter; 310 /// Provides a reference to the frame limiter;
306 Core::FrameLimiter& FrameLimiter(); 311 [[nodiscard]] Core::FrameLimiter& FrameLimiter();
307 312
308 /// Provides a constant referent to the frame limiter 313 /// Provides a constant referent to the frame limiter
309 const Core::FrameLimiter& FrameLimiter() const; 314 [[nodiscard]] const Core::FrameLimiter& FrameLimiter() const;
310 315
311 /// Gets the name of the current game 316 /// Gets the name of the current game
312 Loader::ResultStatus GetGameName(std::string& out) const; 317 [[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
313 318
314 void SetStatus(ResultStatus new_status, const char* details); 319 void SetStatus(ResultStatus new_status, const char* details);
315 320
316 const std::string& GetStatusDetails() const; 321 [[nodiscard]] const std::string& GetStatusDetails() const;
317 322
318 Loader::AppLoader& GetAppLoader() const; 323 [[nodiscard]] Loader::AppLoader& GetAppLoader();
324 [[nodiscard]] const Loader::AppLoader& GetAppLoader() const;
319 325
320 Service::SM::ServiceManager& ServiceManager(); 326 [[nodiscard]] Service::SM::ServiceManager& ServiceManager();
321 const Service::SM::ServiceManager& ServiceManager() const; 327 [[nodiscard]] const Service::SM::ServiceManager& ServiceManager() const;
322 328
323 void SetFilesystem(FileSys::VirtualFilesystem vfs); 329 void SetFilesystem(FileSys::VirtualFilesystem vfs);
324 330
325 FileSys::VirtualFilesystem GetFilesystem() const; 331 [[nodiscard]] FileSys::VirtualFilesystem GetFilesystem() const;
326 332
327 void RegisterCheatList(const std::vector<Memory::CheatEntry>& list, 333 void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
328 const std::array<u8, 0x20>& build_id, VAddr main_region_begin, 334 const std::array<u8, 0x20>& build_id, VAddr main_region_begin,
329 u64 main_region_size); 335 u64 main_region_size);
330 336
331 void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set); 337 void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
332
333 void SetDefaultAppletFrontendSet(); 338 void SetDefaultAppletFrontendSet();
334 339
335 Service::AM::Applets::AppletManager& GetAppletManager(); 340 [[nodiscard]] Service::AM::Applets::AppletManager& GetAppletManager();
336 341 [[nodiscard]] const Service::AM::Applets::AppletManager& GetAppletManager() const;
337 const Service::AM::Applets::AppletManager& GetAppletManager() const;
338 342
339 void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider); 343 void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider);
340 344
341 FileSys::ContentProvider& GetContentProvider(); 345 [[nodiscard]] FileSys::ContentProvider& GetContentProvider();
342 346 [[nodiscard]] const FileSys::ContentProvider& GetContentProvider() const;
343 const FileSys::ContentProvider& GetContentProvider() const;
344 347
345 Service::FileSystem::FileSystemController& GetFileSystemController(); 348 [[nodiscard]] Service::FileSystem::FileSystemController& GetFileSystemController();
346 349 [[nodiscard]] const Service::FileSystem::FileSystemController& GetFileSystemController() const;
347 const Service::FileSystem::FileSystemController& GetFileSystemController() const;
348 350
349 void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, 351 void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
350 FileSys::ContentProvider* provider); 352 FileSys::ContentProvider* provider);
351 353
352 void ClearContentProvider(FileSys::ContentProviderUnionSlot slot); 354 void ClearContentProvider(FileSys::ContentProviderUnionSlot slot);
353 355
354 const Reporter& GetReporter() const; 356 [[nodiscard]] const Reporter& GetReporter() const;
355
356 Service::Glue::ARPManager& GetARPManager();
357 357
358 const Service::Glue::ARPManager& GetARPManager() const; 358 [[nodiscard]] Service::Glue::ARPManager& GetARPManager();
359 [[nodiscard]] const Service::Glue::ARPManager& GetARPManager() const;
359 360
360 Service::APM::Controller& GetAPMController(); 361 [[nodiscard]] Service::APM::Controller& GetAPMController();
362 [[nodiscard]] const Service::APM::Controller& GetAPMController() const;
361 363
362 const Service::APM::Controller& GetAPMController() const; 364 [[nodiscard]] Service::LM::Manager& GetLogManager();
365 [[nodiscard]] const Service::LM::Manager& GetLogManager() const;
363 366
364 Service::LM::Manager& GetLogManager(); 367 [[nodiscard]] Service::Time::TimeManager& GetTimeManager();
365 368 [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
366 const Service::LM::Manager& GetLogManager() const;
367
368 Service::Time::TimeManager& GetTimeManager();
369
370 const Service::Time::TimeManager& GetTimeManager() const;
371 369
372 void SetExitLock(bool locked); 370 void SetExitLock(bool locked);
373 371 [[nodiscard]] bool GetExitLock() const;
374 bool GetExitLock() const;
375 372
376 void SetCurrentProcessBuildID(const CurrentBuildProcessID& id); 373 void SetCurrentProcessBuildID(const CurrentBuildProcessID& id);
377 374 [[nodiscard]] const CurrentBuildProcessID& GetCurrentProcessBuildID() const;
378 const CurrentBuildProcessID& GetCurrentProcessBuildID() const;
379 375
380 /// Register a host thread as an emulated CPU Core. 376 /// Register a host thread as an emulated CPU Core.
381 void RegisterCoreThread(std::size_t id); 377 void RegisterCoreThread(std::size_t id);
@@ -390,18 +386,27 @@ public:
390 void ExitDynarmicProfile(); 386 void ExitDynarmicProfile();
391 387
392 /// Tells if system is running on multicore. 388 /// Tells if system is running on multicore.
393 bool IsMulticore() const; 389 [[nodiscard]] bool IsMulticore() const;
394 390
395private: 391 /// Type used for the frontend to designate a callback for System to re-launch the application
396 System(); 392 /// using a specified program index.
393 using ExecuteProgramCallback = std::function<void(std::size_t)>;
397 394
398 /** 395 /**
399 * Initialize the emulated system. 396 * Registers a callback from the frontend for System to re-launch the application using a
400 * @param emu_window Reference to the host-system window used for video output and keyboard 397 * specified program index.
401 * input. 398 * @param callback Callback from the frontend to relaunch the application.
402 * @return ResultStatus code, indicating if the operation succeeded. 399 */
400 void RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback);
401
402 /**
403 * Instructs the frontend to re-launch the application using the specified program_index.
404 * @param program_index Specifies the index within the application of the program to launch.
403 */ 405 */
404 ResultStatus Init(Frontend::EmuWindow& emu_window); 406 void ExecuteProgram(std::size_t program_index);
407
408private:
409 System();
405 410
406 struct Impl; 411 struct Impl;
407 std::unique_ptr<Impl> impl; 412 std::unique_ptr<Impl> impl;
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 956da68f7..8dee5590b 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -29,7 +29,7 @@ constexpr std::array partition_names{
29 "logo", 29 "logo",
30}; 30};
31 31
32XCI::XCI(VirtualFile file_) 32XCI::XCI(VirtualFile file_, std::size_t program_index)
33 : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, 33 : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
34 partitions(partition_names.size()), 34 partitions(partition_names.size()),
35 partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} { 35 partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} {
@@ -62,7 +62,8 @@ XCI::XCI(VirtualFile file_)
62 } 62 }
63 63
64 secure_partition = std::make_shared<NSP>( 64 secure_partition = std::make_shared<NSP>(
65 main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)])); 65 main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]),
66 program_index);
66 67
67 ncas = secure_partition->GetNCAsCollapsed(); 68 ncas = secure_partition->GetNCAsCollapsed();
68 program = 69 program =
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 2d0a0f285..4960e90fe 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -78,7 +78,7 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
78 78
79class XCI : public ReadOnlyVfsDirectory { 79class XCI : public ReadOnlyVfsDirectory {
80public: 80public:
81 explicit XCI(VirtualFile file); 81 explicit XCI(VirtualFile file, std::size_t program_index = 0);
82 ~XCI() override; 82 ~XCI() override;
83 83
84 Loader::ResultStatus GetStatus() const; 84 Loader::ResultStatus GetStatus() const;
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 807b05821..e9d1607d0 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -112,7 +112,10 @@ bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
112} 112}
113} // Anonymous namespace 113} // Anonymous namespace
114 114
115PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} 115PatchManager::PatchManager(u64 title_id_,
116 const Service::FileSystem::FileSystemController& fs_controller_,
117 const ContentProvider& content_provider_)
118 : title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
116 119
117PatchManager::~PatchManager() = default; 120PatchManager::~PatchManager() = default;
118 121
@@ -128,34 +131,30 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
128 131
129 if (Settings::values.dump_exefs) { 132 if (Settings::values.dump_exefs) {
130 LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); 133 LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
131 const auto dump_dir = 134 const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
132 Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
133 if (dump_dir != nullptr) { 135 if (dump_dir != nullptr) {
134 const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); 136 const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
135 VfsRawCopyD(exefs, exefs_dir); 137 VfsRawCopyD(exefs, exefs_dir);
136 } 138 }
137 } 139 }
138 140
139 const auto& installed = Core::System::GetInstance().GetContentProvider();
140
141 const auto& disabled = Settings::values.disabled_addons[title_id]; 141 const auto& disabled = Settings::values.disabled_addons[title_id];
142 const auto update_disabled = 142 const auto update_disabled =
143 std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend(); 143 std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
144 144
145 // Game Updates 145 // Game Updates
146 const auto update_tid = GetUpdateTitleID(title_id); 146 const auto update_tid = GetUpdateTitleID(title_id);
147 const auto update = installed.GetEntry(update_tid, ContentRecordType::Program); 147 const auto update = content_provider.GetEntry(update_tid, ContentRecordType::Program);
148 148
149 if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr && 149 if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr &&
150 update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { 150 update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
151 LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", 151 LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
152 FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); 152 FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
153 exefs = update->GetExeFS(); 153 exefs = update->GetExeFS();
154 } 154 }
155 155
156 // LayeredExeFS 156 // LayeredExeFS
157 const auto load_dir = 157 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
158 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
159 if (load_dir != nullptr && load_dir->GetSize() > 0) { 158 if (load_dir != nullptr && load_dir->GetSize() > 0) {
160 auto patch_dirs = load_dir->GetSubdirectories(); 159 auto patch_dirs = load_dir->GetSubdirectories();
161 std::sort( 160 std::sort(
@@ -241,8 +240,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
241 if (Settings::values.dump_nso) { 240 if (Settings::values.dump_nso) {
242 LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id, 241 LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
243 title_id); 242 title_id);
244 const auto dump_dir = 243 const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
245 Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
246 if (dump_dir != nullptr) { 244 if (dump_dir != nullptr) {
247 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); 245 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
248 const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id)); 246 const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
@@ -254,8 +252,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
254 252
255 LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id); 253 LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
256 254
257 const auto load_dir = 255 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
258 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
259 if (load_dir == nullptr) { 256 if (load_dir == nullptr) {
260 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); 257 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
261 return nso; 258 return nso;
@@ -298,8 +295,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_) const {
298 295
299 LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); 296 LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
300 297
301 const auto load_dir = 298 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
302 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
303 if (load_dir == nullptr) { 299 if (load_dir == nullptr) {
304 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); 300 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
305 return false; 301 return false;
@@ -313,8 +309,8 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_) const {
313} 309}
314 310
315std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( 311std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
316 const Core::System& system, const BuildID& build_id_) const { 312 const BuildID& build_id_) const {
317 const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id); 313 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
318 if (load_dir == nullptr) { 314 if (load_dir == nullptr) {
319 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); 315 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
320 return {}; 316 return {};
@@ -347,9 +343,9 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
347 return out; 343 return out;
348} 344}
349 345
350static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { 346static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
351 const auto load_dir = 347 const Service::FileSystem::FileSystemController& fs_controller) {
352 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); 348 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
353 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || 349 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
354 load_dir == nullptr || load_dir->GetSize() <= 0) { 350 load_dir == nullptr || load_dir->GetSize() <= 0) {
355 return; 351 return;
@@ -411,19 +407,19 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
411 const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}", 407 const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}",
412 title_id, static_cast<u8>(type)); 408 title_id, static_cast<u8>(type));
413 409
414 if (type == ContentRecordType::Program || type == ContentRecordType::Data) 410 if (type == ContentRecordType::Program || type == ContentRecordType::Data) {
415 LOG_INFO(Loader, "{}", log_string); 411 LOG_INFO(Loader, "{}", log_string);
416 else 412 } else {
417 LOG_DEBUG(Loader, "{}", log_string); 413 LOG_DEBUG(Loader, "{}", log_string);
414 }
418 415
419 if (romfs == nullptr) 416 if (romfs == nullptr) {
420 return romfs; 417 return romfs;
421 418 }
422 const auto& installed = Core::System::GetInstance().GetContentProvider();
423 419
424 // Game Updates 420 // Game Updates
425 const auto update_tid = GetUpdateTitleID(title_id); 421 const auto update_tid = GetUpdateTitleID(title_id);
426 const auto update = installed.GetEntryRaw(update_tid, type); 422 const auto update = content_provider.GetEntryRaw(update_tid, type);
427 423
428 const auto& disabled = Settings::values.disabled_addons[title_id]; 424 const auto& disabled = Settings::values.disabled_addons[title_id];
429 const auto update_disabled = 425 const auto update_disabled =
@@ -434,7 +430,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
434 if (new_nca->GetStatus() == Loader::ResultStatus::Success && 430 if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
435 new_nca->GetRomFS() != nullptr) { 431 new_nca->GetRomFS() != nullptr) {
436 LOG_INFO(Loader, " RomFS: Update ({}) applied successfully", 432 LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
437 FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); 433 FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
438 romfs = new_nca->GetRomFS(); 434 romfs = new_nca->GetRomFS();
439 } 435 }
440 } else if (!update_disabled && update_raw != nullptr) { 436 } else if (!update_disabled && update_raw != nullptr) {
@@ -447,7 +443,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
447 } 443 }
448 444
449 // LayeredFS 445 // LayeredFS
450 ApplyLayeredFS(romfs, title_id, type); 446 ApplyLayeredFS(romfs, title_id, type, fs_controller);
451 447
452 return romfs; 448 return romfs;
453} 449}
@@ -458,12 +454,11 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
458 } 454 }
459 455
460 std::map<std::string, std::string, std::less<>> out; 456 std::map<std::string, std::string, std::less<>> out;
461 const auto& installed = Core::System::GetInstance().GetContentProvider();
462 const auto& disabled = Settings::values.disabled_addons[title_id]; 457 const auto& disabled = Settings::values.disabled_addons[title_id];
463 458
464 // Game Updates 459 // Game Updates
465 const auto update_tid = GetUpdateTitleID(title_id); 460 const auto update_tid = GetUpdateTitleID(title_id);
466 PatchManager update{update_tid}; 461 PatchManager update{update_tid, fs_controller, content_provider};
467 const auto metadata = update.GetControlMetadata(); 462 const auto metadata = update.GetControlMetadata();
468 const auto& nacp = metadata.first; 463 const auto& nacp = metadata.first;
469 464
@@ -474,8 +469,8 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
474 if (nacp != nullptr) { 469 if (nacp != nullptr) {
475 out.insert_or_assign(update_label, nacp->GetVersionString()); 470 out.insert_or_assign(update_label, nacp->GetVersionString());
476 } else { 471 } else {
477 if (installed.HasEntry(update_tid, ContentRecordType::Program)) { 472 if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
478 const auto meta_ver = installed.GetEntryVersion(update_tid); 473 const auto meta_ver = content_provider.GetEntryVersion(update_tid);
479 if (meta_ver.value_or(0) == 0) { 474 if (meta_ver.value_or(0) == 0) {
480 out.insert_or_assign(update_label, ""); 475 out.insert_or_assign(update_label, "");
481 } else { 476 } else {
@@ -487,8 +482,7 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
487 } 482 }
488 483
489 // General Mods (LayeredFS and IPS) 484 // General Mods (LayeredFS and IPS)
490 const auto mod_dir = 485 const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id);
491 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
492 if (mod_dir != nullptr && mod_dir->GetSize() > 0) { 486 if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
493 for (const auto& mod : mod_dir->GetSubdirectories()) { 487 for (const auto& mod : mod_dir->GetSubdirectories()) {
494 std::string types; 488 std::string types;
@@ -532,13 +526,15 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
532 } 526 }
533 527
534 // DLC 528 // DLC
535 const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); 529 const auto dlc_entries =
530 content_provider.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
536 std::vector<ContentProviderEntry> dlc_match; 531 std::vector<ContentProviderEntry> dlc_match;
537 dlc_match.reserve(dlc_entries.size()); 532 dlc_match.reserve(dlc_entries.size());
538 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), 533 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
539 [this, &installed](const ContentProviderEntry& entry) { 534 [this](const ContentProviderEntry& entry) {
540 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id && 535 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id &&
541 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; 536 content_provider.GetEntry(entry)->GetStatus() ==
537 Loader::ResultStatus::Success;
542 }); 538 });
543 if (!dlc_match.empty()) { 539 if (!dlc_match.empty()) {
544 // Ensure sorted so DLC IDs show in order. 540 // Ensure sorted so DLC IDs show in order.
@@ -559,19 +555,16 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
559} 555}
560 556
561std::optional<u32> PatchManager::GetGameVersion() const { 557std::optional<u32> PatchManager::GetGameVersion() const {
562 const auto& installed = Core::System::GetInstance().GetContentProvider();
563 const auto update_tid = GetUpdateTitleID(title_id); 558 const auto update_tid = GetUpdateTitleID(title_id);
564 if (installed.HasEntry(update_tid, ContentRecordType::Program)) { 559 if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
565 return installed.GetEntryVersion(update_tid); 560 return content_provider.GetEntryVersion(update_tid);
566 } 561 }
567 562
568 return installed.GetEntryVersion(title_id); 563 return content_provider.GetEntryVersion(title_id);
569} 564}
570 565
571PatchManager::Metadata PatchManager::GetControlMetadata() const { 566PatchManager::Metadata PatchManager::GetControlMetadata() const {
572 const auto& installed = Core::System::GetInstance().GetContentProvider(); 567 const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
573
574 const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control);
575 if (base_control_nca == nullptr) { 568 if (base_control_nca == nullptr) {
576 return {}; 569 return {};
577 } 570 }
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index 1f28c6241..fb1853035 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -17,8 +17,13 @@ namespace Core {
17class System; 17class System;
18} 18}
19 19
20namespace Service::FileSystem {
21class FileSystemController;
22}
23
20namespace FileSys { 24namespace FileSys {
21 25
26class ContentProvider;
22class NCA; 27class NCA;
23class NACP; 28class NACP;
24 29
@@ -29,7 +34,9 @@ public:
29 using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>; 34 using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>;
30 using PatchVersionNames = std::map<std::string, std::string, std::less<>>; 35 using PatchVersionNames = std::map<std::string, std::string, std::less<>>;
31 36
32 explicit PatchManager(u64 title_id); 37 explicit PatchManager(u64 title_id_,
38 const Service::FileSystem::FileSystemController& fs_controller_,
39 const ContentProvider& content_provider_);
33 ~PatchManager(); 40 ~PatchManager();
34 41
35 [[nodiscard]] u64 GetTitleID() const; 42 [[nodiscard]] u64 GetTitleID() const;
@@ -50,7 +57,7 @@ public:
50 57
51 // Creates a CheatList object with all 58 // Creates a CheatList object with all
52 [[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList( 59 [[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList(
53 const Core::System& system, const BuildID& build_id) const; 60 const BuildID& build_id) const;
54 61
55 // Currently tracked RomFS patches: 62 // Currently tracked RomFS patches:
56 // - Game Updates 63 // - Game Updates
@@ -80,6 +87,8 @@ private:
80 const std::string& build_id) const; 87 const std::string& build_id) const;
81 88
82 u64 title_id; 89 u64 title_id;
90 const Service::FileSystem::FileSystemController& fs_controller;
91 const ContentProvider& content_provider;
83}; 92};
84 93
85} // namespace FileSys 94} // namespace FileSys
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index e967a254e..987199747 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -37,10 +37,12 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
37} 37}
38 38
39ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_title_id) const { 39ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_title_id) const {
40 if (!updatable) 40 if (!updatable) {
41 return MakeResult<VirtualFile>(file); 41 return MakeResult<VirtualFile>(file);
42 }
42 43
43 const PatchManager patch_manager(current_process_title_id); 44 const PatchManager patch_manager{current_process_title_id, filesystem_controller,
45 content_provider};
44 return MakeResult<VirtualFile>( 46 return MakeResult<VirtualFile>(
45 patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); 47 patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
46} 48}
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index 90641d23b..c05735ddd 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -20,8 +20,8 @@
20 20
21namespace FileSys { 21namespace FileSys {
22 22
23NSP::NSP(VirtualFile file_) 23NSP::NSP(VirtualFile file_, std::size_t program_index)
24 : file(std::move(file_)), status{Loader::ResultStatus::Success}, 24 : file(std::move(file_)), program_index(program_index), status{Loader::ResultStatus::Success},
25 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} { 25 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} {
26 if (pfs->GetStatus() != Loader::ResultStatus::Success) { 26 if (pfs->GetStatus() != Loader::ResultStatus::Success) {
27 status = pfs->GetStatus(); 27 status = pfs->GetStatus();
@@ -146,7 +146,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
146 if (extracted) 146 if (extracted)
147 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); 147 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
148 148
149 const auto title_id_iter = ncas.find(title_id); 149 const auto title_id_iter = ncas.find(title_id + program_index);
150 if (title_id_iter == ncas.end()) 150 if (title_id_iter == ncas.end())
151 return nullptr; 151 return nullptr;
152 152
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index c70a11b5b..54581a6f3 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -27,7 +27,7 @@ enum class ContentRecordType : u8;
27 27
28class NSP : public ReadOnlyVfsDirectory { 28class NSP : public ReadOnlyVfsDirectory {
29public: 29public:
30 explicit NSP(VirtualFile file); 30 explicit NSP(VirtualFile file, std::size_t program_index = 0);
31 ~NSP() override; 31 ~NSP() override;
32 32
33 Loader::ResultStatus GetStatus() const; 33 Loader::ResultStatus GetStatus() const;
@@ -69,6 +69,8 @@ private:
69 69
70 VirtualFile file; 70 VirtualFile file;
71 71
72 const std::size_t program_index;
73
72 bool extracted = false; 74 bool extracted = false;
73 Loader::ResultStatus status; 75 Loader::ResultStatus status;
74 std::map<u64, Loader::ResultStatus> program_status; 76 std::map<u64, Loader::ResultStatus> program_status;
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index 5582091f4..03bbedf8b 100644
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -27,19 +27,19 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
27 ->GetAppletResource() 27 ->GetAppletResource()
28 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad); 28 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
29 29
30 auto& players = Settings::values.players; 30 auto& players = Settings::values.players.GetValue();
31 31
32 const std::size_t min_supported_players = 32 const std::size_t min_supported_players =
33 parameters.enable_single_mode ? 1 : parameters.min_players; 33 parameters.enable_single_mode ? 1 : parameters.min_players;
34 34
35 // Disconnect Handheld first. 35 // Disconnect Handheld first.
36 npad.DisconnectNPadAtIndex(8); 36 npad.DisconnectNpadAtIndex(8);
37 37
38 // Deduce the best configuration based on the input parameters. 38 // Deduce the best configuration based on the input parameters.
39 for (std::size_t index = 0; index < players.size() - 2; ++index) { 39 for (std::size_t index = 0; index < players.size() - 2; ++index) {
40 // First, disconnect all controllers regardless of the value of keep_controllers_connected. 40 // First, disconnect all controllers regardless of the value of keep_controllers_connected.
41 // This makes it easy to connect the desired controllers. 41 // This makes it easy to connect the desired controllers.
42 npad.DisconnectNPadAtIndex(index); 42 npad.DisconnectNpadAtIndex(index);
43 43
44 // Only connect the minimum number of required players. 44 // Only connect the minimum number of required players.
45 if (index >= min_supported_players) { 45 if (index >= min_supported_players) {
@@ -66,7 +66,7 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
66 npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index); 66 npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index);
67 } 67 }
68 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && 68 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
69 !Settings::values.use_docked_mode) { 69 !Settings::values.use_docked_mode.GetValue()) {
70 // We should *never* reach here under any normal circumstances. 70 // We should *never* reach here under any normal circumstances.
71 npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld), 71 npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld),
72 index); 72 index);
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index 1acc82497..b9a270a55 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -47,7 +47,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
47FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) { 47FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
48 u32 width, height; 48 u32 width, height;
49 49
50 if (Settings::values.use_docked_mode) { 50 if (Settings::values.use_docked_mode.GetValue()) {
51 width = ScreenDocked::Width * res_scale; 51 width = ScreenDocked::Width * res_scale;
52 height = ScreenDocked::Height * res_scale; 52 height = ScreenDocked::Height * res_scale;
53 } else { 53 } else {
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 277b70e53..11c2e96ca 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -30,10 +30,12 @@ public:
30 virtual StatusType GetStatus() const { 30 virtual StatusType GetStatus() const {
31 return {}; 31 return {};
32 } 32 }
33 virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { 33 virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const {
34 return {}; 34 return {};
35 } 35 }
36 virtual bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const { 36 virtual bool SetRumblePlay([[maybe_unused]] f32 amp_low, [[maybe_unused]] f32 freq_low,
37 [[maybe_unused]] f32 amp_high,
38 [[maybe_unused]] f32 freq_high) const {
37 return {}; 39 return {};
38 } 40 }
39}; 41};
@@ -122,6 +124,13 @@ using ButtonDevice = InputDevice<bool>;
122using AnalogDevice = InputDevice<std::tuple<float, float>>; 124using AnalogDevice = InputDevice<std::tuple<float, float>>;
123 125
124/** 126/**
127 * A vibration device is an input device that returns an unsigned byte as status.
128 * It represents whether the vibration device supports vibration or not.
129 * If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
130 */
131using VibrationDevice = InputDevice<u8>;
132
133/**
125 * A motion status is an object that returns a tuple of accelerometer state vector, 134 * A motion status is an object that returns a tuple of accelerometer state vector,
126 * gyroscope state vector, rotation state vector and orientation state matrix. 135 * gyroscope state vector, rotation state vector and orientation state matrix.
127 * 136 *
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 1c354037d..d57776ce9 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -12,7 +12,6 @@
12#include <utility> 12#include <utility>
13#include "common/assert.h" 13#include "common/assert.h"
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "core/core.h"
16#include "core/hle/ipc.h" 15#include "core/hle/ipc.h"
17#include "core/hle/kernel/client_port.h" 16#include "core/hle/kernel/client_port.h"
18#include "core/hle/kernel/client_session.h" 17#include "core/hle/kernel/client_session.h"
@@ -73,14 +72,12 @@ public:
73 AlwaysMoveHandles = 1, 72 AlwaysMoveHandles = 1,
74 }; 73 };
75 74
76 explicit ResponseBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
77
78 explicit ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size, 75 explicit ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size,
79 u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0, 76 u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0,
80 Flags flags = Flags::None) 77 Flags flags = Flags::None)
81
82 : RequestHelperBase(context), normal_params_size(normal_params_size), 78 : RequestHelperBase(context), normal_params_size(normal_params_size),
83 num_handles_to_copy(num_handles_to_copy), num_objects_to_move(num_objects_to_move) { 79 num_handles_to_copy(num_handles_to_copy),
80 num_objects_to_move(num_objects_to_move), kernel{context.kernel} {
84 81
85 memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH); 82 memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
86 83
@@ -140,7 +137,6 @@ public:
140 if (context->Session()->IsDomain()) { 137 if (context->Session()->IsDomain()) {
141 context->AddDomainObject(std::move(iface)); 138 context->AddDomainObject(std::move(iface));
142 } else { 139 } else {
143 auto& kernel = Core::System::GetInstance().Kernel();
144 auto [client, server] = Kernel::Session::Create(kernel, iface->GetServiceName()); 140 auto [client, server] = Kernel::Session::Create(kernel, iface->GetServiceName());
145 context->AddMoveObject(std::move(client)); 141 context->AddMoveObject(std::move(client));
146 iface->ClientConnected(std::move(server)); 142 iface->ClientConnected(std::move(server));
@@ -214,6 +210,7 @@ private:
214 u32 num_handles_to_copy{}; 210 u32 num_handles_to_copy{};
215 u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent 211 u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
216 std::ptrdiff_t datapayload_index{}; 212 std::ptrdiff_t datapayload_index{};
213 Kernel::KernelCore& kernel;
217}; 214};
218 215
219/// Push /// 216/// Push ///
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index f3277b766..c31a65476 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -24,6 +24,10 @@ namespace Core::Memory {
24class Memory; 24class Memory;
25} 25}
26 26
27namespace IPC {
28class ResponseBuilder;
29}
30
27namespace Service { 31namespace Service {
28class ServiceFrameworkBase; 32class ServiceFrameworkBase;
29} 33}
@@ -287,6 +291,8 @@ public:
287 } 291 }
288 292
289private: 293private:
294 friend class IPC::ResponseBuilder;
295
290 void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming); 296 void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
291 297
292 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; 298 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index bafd1ced7..e3b770d66 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -681,7 +681,7 @@ static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
681} 681}
682 682
683/// Used to output a message on a debug hardware unit - does nothing on a retail unit 683/// Used to output a message on a debug hardware unit - does nothing on a retail unit
684static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) { 684static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
685 if (len == 0) { 685 if (len == 0) {
686 return; 686 return;
687 } 687 }
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 2850dd805..c2c11dbcb 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -11,6 +11,7 @@
11#include "common/string_util.h" 11#include "common/string_util.h"
12#include "common/swap.h" 12#include "common/swap.h"
13#include "core/constants.h" 13#include "core/constants.h"
14#include "core/core.h"
14#include "core/core_timing.h" 15#include "core/core_timing.h"
15#include "core/file_sys/control_metadata.h" 16#include "core/file_sys/control_metadata.h"
16#include "core/file_sys/patch_manager.h" 17#include "core/file_sys/patch_manager.h"
@@ -741,8 +742,10 @@ void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx
741 bool is_locked = false; 742 bool is_locked = false;
742 743
743 if (res != Loader::ResultStatus::Success) { 744 if (res != Loader::ResultStatus::Success) {
744 FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; 745 const FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID(),
745 auto nacp_unique = pm.GetControlMetadata().first; 746 system.GetFileSystemController(),
747 system.GetContentProvider()};
748 const auto nacp_unique = pm.GetControlMetadata().first;
746 749
747 if (nacp_unique != nullptr) { 750 if (nacp_unique != nullptr) {
748 is_locked = nacp_unique->GetUserAccountSwitchLock(); 751 is_locked = nacp_unique->GetUserAccountSwitchLock();
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 2ce742e35..703a9b234 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -246,9 +246,8 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
246 246
247IDebugFunctions::~IDebugFunctions() = default; 247IDebugFunctions::~IDebugFunctions() = default;
248 248
249ISelfController::ISelfController(Core::System& system, 249ISelfController::ISelfController(Core::System& system, NVFlinger::NVFlinger& nvflinger)
250 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 250 : ServiceFramework("ISelfController"), system(system), nvflinger(nvflinger) {
251 : ServiceFramework("ISelfController"), system(system), nvflinger(std::move(nvflinger)) {
252 // clang-format off 251 // clang-format off
253 static const FunctionInfo functions[] = { 252 static const FunctionInfo functions[] = {
254 {0, &ISelfController::Exit, "Exit"}, 253 {0, &ISelfController::Exit, "Exit"},
@@ -458,8 +457,8 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx)
458 457
459 // TODO(Subv): Find out how AM determines the display to use, for now just 458 // TODO(Subv): Find out how AM determines the display to use, for now just
460 // create the layer in the Default display. 459 // create the layer in the Default display.
461 const auto display_id = nvflinger->OpenDisplay("Default"); 460 const auto display_id = nvflinger.OpenDisplay("Default");
462 const auto layer_id = nvflinger->CreateLayer(*display_id); 461 const auto layer_id = nvflinger.CreateLayer(*display_id);
463 462
464 IPC::ResponseBuilder rb{ctx, 4}; 463 IPC::ResponseBuilder rb{ctx, 4};
465 rb.Push(RESULT_SUCCESS); 464 rb.Push(RESULT_SUCCESS);
@@ -476,8 +475,8 @@ void ISelfController::CreateManagedDisplaySeparableLayer(Kernel::HLERequestConte
476 // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse 475 // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
477 // side effects. 476 // side effects.
478 // TODO: Support multiple layers 477 // TODO: Support multiple layers
479 const auto display_id = nvflinger->OpenDisplay("Default"); 478 const auto display_id = nvflinger.OpenDisplay("Default");
480 const auto layer_id = nvflinger->CreateLayer(*display_id); 479 const auto layer_id = nvflinger.CreateLayer(*display_id);
481 480
482 IPC::ResponseBuilder rb{ctx, 4}; 481 IPC::ResponseBuilder rb{ctx, 4};
483 rb.Push(RESULT_SUCCESS); 482 rb.Push(RESULT_SUCCESS);
@@ -751,7 +750,7 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
751 IPC::ResponseBuilder rb{ctx, 4}; 750 IPC::ResponseBuilder rb{ctx, 4};
752 rb.Push(RESULT_SUCCESS); 751 rb.Push(RESULT_SUCCESS);
753 752
754 if (Settings::values.use_docked_mode) { 753 if (Settings::values.use_docked_mode.GetValue()) {
755 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * 754 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
756 static_cast<u32>(Settings::values.resolution_factor.GetValue())); 755 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
757 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * 756 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
@@ -824,7 +823,7 @@ void IStorage::Open(Kernel::HLERequestContext& ctx) {
824} 823}
825 824
826void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { 825void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
827 const bool use_docked_mode{Settings::values.use_docked_mode}; 826 const bool use_docked_mode{Settings::values.use_docked_mode.GetValue()};
828 LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); 827 LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
829 828
830 IPC::ResponseBuilder rb{ctx, 3}; 829 IPC::ResponseBuilder rb{ctx, 3};
@@ -1189,9 +1188,9 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1189 {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"}, 1188 {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"},
1190 {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"}, 1189 {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"},
1191 {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"}, 1190 {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"},
1192 {120, nullptr, "ExecuteProgram"}, 1191 {120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"},
1193 {121, nullptr, "ClearUserChannel"}, 1192 {121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"},
1194 {122, nullptr, "UnpopToUserChannel"}, 1193 {122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"},
1195 {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"}, 1194 {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
1196 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, 1195 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
1197 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, 1196 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
@@ -1381,13 +1380,16 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
1381 const auto res = [this] { 1380 const auto res = [this] {
1382 const auto title_id = system.CurrentProcess()->GetTitleID(); 1381 const auto title_id = system.CurrentProcess()->GetTitleID();
1383 1382
1384 FileSys::PatchManager pm{title_id}; 1383 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
1384 system.GetContentProvider()};
1385 auto res = pm.GetControlMetadata(); 1385 auto res = pm.GetControlMetadata();
1386 if (res.first != nullptr) { 1386 if (res.first != nullptr) {
1387 return res; 1387 return res;
1388 } 1388 }
1389 1389
1390 FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)}; 1390 const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id),
1391 system.GetFileSystemController(),
1392 system.GetContentProvider()};
1391 return pm_update.GetControlMetadata(); 1393 return pm_update.GetControlMetadata();
1392 }(); 1394 }();
1393 1395
@@ -1415,13 +1417,16 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
1415 const auto res = [this] { 1417 const auto res = [this] {
1416 const auto title_id = system.CurrentProcess()->GetTitleID(); 1418 const auto title_id = system.CurrentProcess()->GetTitleID();
1417 1419
1418 FileSys::PatchManager pm{title_id}; 1420 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
1421 system.GetContentProvider()};
1419 auto res = pm.GetControlMetadata(); 1422 auto res = pm.GetControlMetadata();
1420 if (res.first != nullptr) { 1423 if (res.first != nullptr) {
1421 return res; 1424 return res;
1422 } 1425 }
1423 1426
1424 FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)}; 1427 const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id),
1428 system.GetFileSystemController(),
1429 system.GetContentProvider()};
1425 return pm_update.GetControlMetadata(); 1430 return pm_update.GetControlMetadata();
1426 }(); 1431 }();
1427 1432
@@ -1556,6 +1561,34 @@ void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(Kernel::HLEReque
1556 rb.Push<u32>(0); 1561 rb.Push<u32>(0);
1557} 1562}
1558 1563
1564void IApplicationFunctions::ExecuteProgram(Kernel::HLERequestContext& ctx) {
1565 LOG_WARNING(Service_AM, "(STUBBED) called");
1566
1567 IPC::RequestParser rp{ctx};
1568 [[maybe_unused]] const auto unk_1 = rp.Pop<u32>();
1569 [[maybe_unused]] const auto unk_2 = rp.Pop<u32>();
1570 const auto program_index = rp.Pop<u64>();
1571
1572 IPC::ResponseBuilder rb{ctx, 2};
1573 rb.Push(RESULT_SUCCESS);
1574
1575 system.ExecuteProgram(program_index);
1576}
1577
1578void IApplicationFunctions::ClearUserChannel(Kernel::HLERequestContext& ctx) {
1579 LOG_WARNING(Service_AM, "(STUBBED) called");
1580
1581 IPC::ResponseBuilder rb{ctx, 2};
1582 rb.Push(RESULT_SUCCESS);
1583}
1584
1585void IApplicationFunctions::UnpopToUserChannel(Kernel::HLERequestContext& ctx) {
1586 LOG_WARNING(Service_AM, "(STUBBED) called");
1587
1588 IPC::ResponseBuilder rb{ctx, 2};
1589 rb.Push(RESULT_SUCCESS);
1590}
1591
1559void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) { 1592void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) {
1560 LOG_WARNING(Service_AM, "(STUBBED) called"); 1593 LOG_WARNING(Service_AM, "(STUBBED) called");
1561 1594
@@ -1580,8 +1613,8 @@ void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(Kernel::HLERe
1580 rb.PushCopyObjects(friend_invitation_storage_channel_event.readable); 1613 rb.PushCopyObjects(friend_invitation_storage_channel_event.readable);
1581} 1614}
1582 1615
1583void InstallInterfaces(SM::ServiceManager& service_manager, 1616void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
1584 std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) { 1617 Core::System& system) {
1585 auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel()); 1618 auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel());
1586 // Needed on game boot 1619 // Needed on game boot
1587 message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); 1620 message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index bcc06affe..af97c303a 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -121,8 +121,7 @@ public:
121 121
122class ISelfController final : public ServiceFramework<ISelfController> { 122class ISelfController final : public ServiceFramework<ISelfController> {
123public: 123public:
124 explicit ISelfController(Core::System& system_, 124 explicit ISelfController(Core::System& system_, NVFlinger::NVFlinger& nvflinger_);
125 std::shared_ptr<NVFlinger::NVFlinger> nvflinger_);
126 ~ISelfController() override; 125 ~ISelfController() override;
127 126
128private: 127private:
@@ -156,7 +155,7 @@ private:
156 }; 155 };
157 156
158 Core::System& system; 157 Core::System& system;
159 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 158 NVFlinger::NVFlinger& nvflinger;
160 Kernel::EventPair launchable_event; 159 Kernel::EventPair launchable_event;
161 Kernel::EventPair accumulated_suspended_tick_changed_event; 160 Kernel::EventPair accumulated_suspended_tick_changed_event;
162 161
@@ -288,6 +287,9 @@ private:
288 void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx); 287 void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx);
289 void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx); 288 void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx);
290 void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx); 289 void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx);
290 void ExecuteProgram(Kernel::HLERequestContext& ctx);
291 void ClearUserChannel(Kernel::HLERequestContext& ctx);
292 void UnpopToUserChannel(Kernel::HLERequestContext& ctx);
291 void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx); 293 void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx);
292 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); 294 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
293 void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx); 295 void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx);
@@ -332,7 +334,7 @@ public:
332}; 334};
333 335
334/// Registers all AM services with the specified service manager. 336/// Registers all AM services with the specified service manager.
335void InstallInterfaces(SM::ServiceManager& service_manager, 337void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
336 std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system); 338 Core::System& system);
337 339
338} // namespace Service::AM 340} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index 9df286d17..7de506b70 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -3,8 +3,8 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/core.h"
6#include "core/hle/ipc_helpers.h" 7#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/process.h"
8#include "core/hle/service/am/am.h" 8#include "core/hle/service/am/am.h"
9#include "core/hle/service/am/applet_ae.h" 9#include "core/hle/service/am/applet_ae.h"
10#include "core/hle/service/nvflinger/nvflinger.h" 10#include "core/hle/service/nvflinger/nvflinger.h"
@@ -13,10 +13,10 @@ namespace Service::AM {
13 13
14class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { 14class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
15public: 15public:
16 explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 16 explicit ILibraryAppletProxy(NVFlinger::NVFlinger& nvflinger,
17 std::shared_ptr<AppletMessageQueue> msg_queue, 17 std::shared_ptr<AppletMessageQueue> msg_queue,
18 Core::System& system) 18 Core::System& system)
19 : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)), 19 : ServiceFramework("ILibraryAppletProxy"), nvflinger(nvflinger),
20 msg_queue(std::move(msg_queue)), system(system) { 20 msg_queue(std::move(msg_queue)), system(system) {
21 // clang-format off 21 // clang-format off
22 static const FunctionInfo functions[] = { 22 static const FunctionInfo functions[] = {
@@ -109,16 +109,16 @@ private:
109 rb.PushIpcInterface<IApplicationFunctions>(system); 109 rb.PushIpcInterface<IApplicationFunctions>(system);
110 } 110 }
111 111
112 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 112 NVFlinger::NVFlinger& nvflinger;
113 std::shared_ptr<AppletMessageQueue> msg_queue; 113 std::shared_ptr<AppletMessageQueue> msg_queue;
114 Core::System& system; 114 Core::System& system;
115}; 115};
116 116
117class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { 117class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
118public: 118public:
119 explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 119 explicit ISystemAppletProxy(NVFlinger::NVFlinger& nvflinger,
120 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) 120 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
121 : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)), 121 : ServiceFramework("ISystemAppletProxy"), nvflinger(nvflinger),
122 msg_queue(std::move(msg_queue)), system(system) { 122 msg_queue(std::move(msg_queue)), system(system) {
123 // clang-format off 123 // clang-format off
124 static const FunctionInfo functions[] = { 124 static const FunctionInfo functions[] = {
@@ -220,7 +220,8 @@ private:
220 rb.Push(RESULT_SUCCESS); 220 rb.Push(RESULT_SUCCESS);
221 rb.PushIpcInterface<IApplicationCreator>(); 221 rb.PushIpcInterface<IApplicationCreator>();
222 } 222 }
223 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 223
224 NVFlinger::NVFlinger& nvflinger;
224 std::shared_ptr<AppletMessageQueue> msg_queue; 225 std::shared_ptr<AppletMessageQueue> msg_queue;
225 Core::System& system; 226 Core::System& system;
226}; 227};
@@ -249,10 +250,10 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
249 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system); 250 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system);
250} 251}
251 252
252AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 253AppletAE::AppletAE(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue,
253 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) 254 Core::System& system)
254 : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)), 255 : ServiceFramework("appletAE"), nvflinger(nvflinger), msg_queue(std::move(msg_queue)),
255 msg_queue(std::move(msg_queue)), system(system) { 256 system(system) {
256 // clang-format off 257 // clang-format off
257 static const FunctionInfo functions[] = { 258 static const FunctionInfo functions[] = {
258 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, 259 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index 2e3e45915..761844a1f 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -23,7 +23,7 @@ class AppletMessageQueue;
23 23
24class AppletAE final : public ServiceFramework<AppletAE> { 24class AppletAE final : public ServiceFramework<AppletAE> {
25public: 25public:
26 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 26 explicit AppletAE(NVFlinger::NVFlinger& nvflinger,
27 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system); 27 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system);
28 ~AppletAE() override; 28 ~AppletAE() override;
29 29
@@ -34,7 +34,7 @@ private:
34 void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx); 34 void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx);
35 void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx); 35 void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx);
36 36
37 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 37 NVFlinger::NVFlinger& nvflinger;
38 std::shared_ptr<AppletMessageQueue> msg_queue; 38 std::shared_ptr<AppletMessageQueue> msg_queue;
39 Core::System& system; 39 Core::System& system;
40}; 40};
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index a2ffaa440..7bed86ec4 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -12,9 +12,9 @@ namespace Service::AM {
12 12
13class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { 13class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
14public: 14public:
15 explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 15 explicit IApplicationProxy(NVFlinger::NVFlinger& nvflinger,
16 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) 16 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
17 : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)), 17 : ServiceFramework("IApplicationProxy"), nvflinger(nvflinger),
18 msg_queue(std::move(msg_queue)), system(system) { 18 msg_queue(std::move(msg_queue)), system(system) {
19 // clang-format off 19 // clang-format off
20 static const FunctionInfo functions[] = { 20 static const FunctionInfo functions[] = {
@@ -98,7 +98,7 @@ private:
98 rb.PushIpcInterface<IApplicationFunctions>(system); 98 rb.PushIpcInterface<IApplicationFunctions>(system);
99 } 99 }
100 100
101 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 101 NVFlinger::NVFlinger& nvflinger;
102 std::shared_ptr<AppletMessageQueue> msg_queue; 102 std::shared_ptr<AppletMessageQueue> msg_queue;
103 Core::System& system; 103 Core::System& system;
104}; 104};
@@ -111,10 +111,10 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
111 rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue, system); 111 rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue, system);
112} 112}
113 113
114AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 114AppletOE::AppletOE(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue,
115 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) 115 Core::System& system)
116 : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)), 116 : ServiceFramework("appletOE"), nvflinger(nvflinger), msg_queue(std::move(msg_queue)),
117 msg_queue(std::move(msg_queue)), system(system) { 117 system(system) {
118 static const FunctionInfo functions[] = { 118 static const FunctionInfo functions[] = {
119 {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, 119 {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
120 }; 120 };
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index 758da792d..88906d354 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -23,7 +23,7 @@ class AppletMessageQueue;
23 23
24class AppletOE final : public ServiceFramework<AppletOE> { 24class AppletOE final : public ServiceFramework<AppletOE> {
25public: 25public:
26 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 26 explicit AppletOE(NVFlinger::NVFlinger& nvflinger,
27 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system); 27 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system);
28 ~AppletOE() override; 28 ~AppletOE() override;
29 29
@@ -32,7 +32,7 @@ public:
32private: 32private:
33 void OpenApplicationProxy(Kernel::HLERequestContext& ctx); 33 void OpenApplicationProxy(Kernel::HLERequestContext& ctx);
34 34
35 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 35 NVFlinger::NVFlinger& nvflinger;
36 std::shared_ptr<AppletMessageQueue> msg_queue; 36 std::shared_ptr<AppletMessageQueue> msg_queue;
37 Core::System& system; 37 Core::System& system;
38}; 38};
diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp
index 2151da783..3ca63f020 100644
--- a/src/core/hle/service/am/applets/controller.cpp
+++ b/src/core/hle/service/am/applets/controller.cpp
@@ -25,7 +25,7 @@ namespace Service::AM::Applets {
25static Core::Frontend::ControllerParameters ConvertToFrontendParameters( 25static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
26 ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, 26 ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
27 std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) { 27 std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
28 HID::Controller_NPad::NPadType npad_style_set; 28 HID::Controller_NPad::NpadStyleSet npad_style_set;
29 npad_style_set.raw = private_arg.style_set; 29 npad_style_set.raw = private_arg.style_set;
30 30
31 return { 31 return {
@@ -62,7 +62,7 @@ void Controller::Initialize() {
62 common_args.play_startup_sound, common_args.size, common_args.system_tick, 62 common_args.play_startup_sound, common_args.size, common_args.system_tick,
63 common_args.theme_color); 63 common_args.theme_color);
64 64
65 library_applet_version = LibraryAppletVersion{common_args.library_version}; 65 controller_applet_version = ControllerAppletVersion{common_args.library_version};
66 66
67 const auto private_arg_storage = broker.PopNormalDataToApplet(); 67 const auto private_arg_storage = broker.PopNormalDataToApplet();
68 ASSERT(private_arg_storage != nullptr); 68 ASSERT(private_arg_storage != nullptr);
@@ -70,39 +70,78 @@ void Controller::Initialize() {
70 const auto& private_arg = private_arg_storage->GetData(); 70 const auto& private_arg = private_arg_storage->GetData();
71 ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate)); 71 ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate));
72 72
73 std::memcpy(&controller_private_arg, private_arg.data(), sizeof(ControllerSupportArgPrivate)); 73 std::memcpy(&controller_private_arg, private_arg.data(), private_arg.size());
74 ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate), 74 ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate),
75 "Unknown ControllerSupportArgPrivate revision={} with size={}", 75 "Unknown ControllerSupportArgPrivate revision={} with size={}",
76 library_applet_version, controller_private_arg.arg_private_size); 76 controller_applet_version, controller_private_arg.arg_private_size);
77
78 // Some games such as Cave Story+ set invalid values for the ControllerSupportMode.
79 // Defer to arg_size to set the ControllerSupportMode.
80 if (controller_private_arg.mode >= ControllerSupportMode::MaxControllerSupportMode) {
81 switch (controller_private_arg.arg_size) {
82 case sizeof(ControllerSupportArgOld):
83 case sizeof(ControllerSupportArgNew):
84 controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport;
85 break;
86 case sizeof(ControllerUpdateFirmwareArg):
87 controller_private_arg.mode = ControllerSupportMode::ShowControllerFirmwareUpdate;
88 break;
89 default:
90 UNIMPLEMENTED_MSG("Unknown ControllerPrivateArg mode={} with arg_size={}",
91 controller_private_arg.mode, controller_private_arg.arg_size);
92 controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport;
93 break;
94 }
95 }
96
97 // Some games such as Cave Story+ set invalid values for the ControllerSupportCaller.
98 // This is always 0 (Application) except with ShowControllerFirmwareUpdateForSystem.
99 if (controller_private_arg.caller >= ControllerSupportCaller::MaxControllerSupportCaller) {
100 if (controller_private_arg.flag_1 &&
101 controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate) {
102 controller_private_arg.caller = ControllerSupportCaller::System;
103 } else {
104 controller_private_arg.caller = ControllerSupportCaller::Application;
105 }
106 }
77 107
78 switch (controller_private_arg.mode) { 108 switch (controller_private_arg.mode) {
79 case ControllerSupportMode::ShowControllerSupport: { 109 case ControllerSupportMode::ShowControllerSupport:
110 case ControllerSupportMode::ShowControllerStrapGuide: {
80 const auto user_arg_storage = broker.PopNormalDataToApplet(); 111 const auto user_arg_storage = broker.PopNormalDataToApplet();
81 ASSERT(user_arg_storage != nullptr); 112 ASSERT(user_arg_storage != nullptr);
82 113
83 const auto& user_arg = user_arg_storage->GetData(); 114 const auto& user_arg = user_arg_storage->GetData();
84 switch (library_applet_version) { 115 switch (controller_applet_version) {
85 case LibraryAppletVersion::Version3: 116 case ControllerAppletVersion::Version3:
86 case LibraryAppletVersion::Version4: 117 case ControllerAppletVersion::Version4:
87 case LibraryAppletVersion::Version5: 118 case ControllerAppletVersion::Version5:
88 ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld)); 119 ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld));
89 std::memcpy(&controller_user_arg_old, user_arg.data(), sizeof(ControllerSupportArgOld)); 120 std::memcpy(&controller_user_arg_old, user_arg.data(), user_arg.size());
90 break; 121 break;
91 case LibraryAppletVersion::Version7: 122 case ControllerAppletVersion::Version7:
92 ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew)); 123 ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew));
93 std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew)); 124 std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size());
94 break; 125 break;
95 default: 126 default:
96 UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}", 127 UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}",
97 library_applet_version, controller_private_arg.arg_size); 128 controller_applet_version, controller_private_arg.arg_size);
98 ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew)); 129 ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew));
99 std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew)); 130 std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
100 break; 131 break;
101 } 132 }
102 break; 133 break;
103 } 134 }
104 case ControllerSupportMode::ShowControllerStrapGuide: 135 case ControllerSupportMode::ShowControllerFirmwareUpdate: {
105 case ControllerSupportMode::ShowControllerFirmwareUpdate: 136 const auto update_arg_storage = broker.PopNormalDataToApplet();
137 ASSERT(update_arg_storage != nullptr);
138
139 const auto& update_arg = update_arg_storage->GetData();
140 ASSERT(update_arg.size() == sizeof(ControllerUpdateFirmwareArg));
141
142 std::memcpy(&controller_update_arg, update_arg.data(), update_arg.size());
143 break;
144 }
106 default: { 145 default: {
107 UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode); 146 UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode);
108 break; 147 break;
@@ -126,10 +165,10 @@ void Controller::Execute() {
126 switch (controller_private_arg.mode) { 165 switch (controller_private_arg.mode) {
127 case ControllerSupportMode::ShowControllerSupport: { 166 case ControllerSupportMode::ShowControllerSupport: {
128 const auto parameters = [this] { 167 const auto parameters = [this] {
129 switch (library_applet_version) { 168 switch (controller_applet_version) {
130 case LibraryAppletVersion::Version3: 169 case ControllerAppletVersion::Version3:
131 case LibraryAppletVersion::Version4: 170 case ControllerAppletVersion::Version4:
132 case LibraryAppletVersion::Version5: 171 case ControllerAppletVersion::Version5:
133 return ConvertToFrontendParameters( 172 return ConvertToFrontendParameters(
134 controller_private_arg, controller_user_arg_old.header, 173 controller_private_arg, controller_user_arg_old.header,
135 controller_user_arg_old.enable_explain_text, 174 controller_user_arg_old.enable_explain_text,
@@ -138,7 +177,7 @@ void Controller::Execute() {
138 controller_user_arg_old.identification_colors.end()), 177 controller_user_arg_old.identification_colors.end()),
139 std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(), 178 std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(),
140 controller_user_arg_old.explain_text.end())); 179 controller_user_arg_old.explain_text.end()));
141 case LibraryAppletVersion::Version7: 180 case ControllerAppletVersion::Version7:
142 default: 181 default:
143 return ConvertToFrontendParameters( 182 return ConvertToFrontendParameters(
144 controller_private_arg, controller_user_arg_new.header, 183 controller_private_arg, controller_user_arg_new.header,
@@ -170,6 +209,9 @@ void Controller::Execute() {
170 } 209 }
171 case ControllerSupportMode::ShowControllerStrapGuide: 210 case ControllerSupportMode::ShowControllerStrapGuide:
172 case ControllerSupportMode::ShowControllerFirmwareUpdate: 211 case ControllerSupportMode::ShowControllerFirmwareUpdate:
212 UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented",
213 controller_private_arg.mode);
214 [[fallthrough]];
173 default: { 215 default: {
174 ConfigurationComplete(); 216 ConfigurationComplete();
175 break; 217 break;
@@ -180,7 +222,7 @@ void Controller::Execute() {
180void Controller::ConfigurationComplete() { 222void Controller::ConfigurationComplete() {
181 ControllerSupportResultInfo result_info{}; 223 ControllerSupportResultInfo result_info{};
182 224
183 const auto& players = Settings::values.players; 225 const auto& players = Settings::values.players.GetValue();
184 226
185 // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters. 227 // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
186 // Otherwise, only count connected players from P1-P8. 228 // Otherwise, only count connected players from P1-P8.
diff --git a/src/core/hle/service/am/applets/controller.h b/src/core/hle/service/am/applets/controller.h
index f7bb3fba9..a7a1f2b65 100644
--- a/src/core/hle/service/am/applets/controller.h
+++ b/src/core/hle/service/am/applets/controller.h
@@ -21,7 +21,7 @@ namespace Service::AM::Applets {
21using IdentificationColor = std::array<u8, 4>; 21using IdentificationColor = std::array<u8, 4>;
22using ExplainText = std::array<char, 0x81>; 22using ExplainText = std::array<char, 0x81>;
23 23
24enum class LibraryAppletVersion : u32_le { 24enum class ControllerAppletVersion : u32_le {
25 Version3 = 0x3, // 1.0.0 - 2.3.0 25 Version3 = 0x3, // 1.0.0 - 2.3.0
26 Version4 = 0x4, // 3.0.0 - 5.1.0 26 Version4 = 0x4, // 3.0.0 - 5.1.0
27 Version5 = 0x5, // 6.0.0 - 7.0.1 27 Version5 = 0x5, // 6.0.0 - 7.0.1
@@ -29,14 +29,18 @@ enum class LibraryAppletVersion : u32_le {
29}; 29};
30 30
31enum class ControllerSupportMode : u8 { 31enum class ControllerSupportMode : u8 {
32 ShowControllerSupport = 0, 32 ShowControllerSupport,
33 ShowControllerStrapGuide = 1, 33 ShowControllerStrapGuide,
34 ShowControllerFirmwareUpdate = 2, 34 ShowControllerFirmwareUpdate,
35
36 MaxControllerSupportMode,
35}; 37};
36 38
37enum class ControllerSupportCaller : u8 { 39enum class ControllerSupportCaller : u8 {
38 Application = 0, 40 Application,
39 System = 1, 41 System,
42
43 MaxControllerSupportCaller,
40}; 44};
41 45
42struct ControllerSupportArgPrivate { 46struct ControllerSupportArgPrivate {
@@ -84,6 +88,13 @@ struct ControllerSupportArgNew {
84static_assert(sizeof(ControllerSupportArgNew) == 0x430, 88static_assert(sizeof(ControllerSupportArgNew) == 0x430,
85 "ControllerSupportArgNew has incorrect size."); 89 "ControllerSupportArgNew has incorrect size.");
86 90
91struct ControllerUpdateFirmwareArg {
92 bool enable_force_update{};
93 INSERT_PADDING_BYTES(3);
94};
95static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4,
96 "ControllerUpdateFirmwareArg has incorrect size.");
97
87struct ControllerSupportResultInfo { 98struct ControllerSupportResultInfo {
88 s8 player_count{}; 99 s8 player_count{};
89 INSERT_PADDING_BYTES(3); 100 INSERT_PADDING_BYTES(3);
@@ -110,10 +121,11 @@ public:
110private: 121private:
111 const Core::Frontend::ControllerApplet& frontend; 122 const Core::Frontend::ControllerApplet& frontend;
112 123
113 LibraryAppletVersion library_applet_version; 124 ControllerAppletVersion controller_applet_version;
114 ControllerSupportArgPrivate controller_private_arg; 125 ControllerSupportArgPrivate controller_private_arg;
115 ControllerSupportArgOld controller_user_arg_old; 126 ControllerSupportArgOld controller_user_arg_old;
116 ControllerSupportArgNew controller_user_arg_new; 127 ControllerSupportArgNew controller_user_arg_new;
128 ControllerUpdateFirmwareArg controller_update_arg;
117 bool complete{false}; 129 bool complete{false};
118 ResultCode status{RESULT_SUCCESS}; 130 ResultCode status{RESULT_SUCCESS};
119 bool is_single_mode{false}; 131 bool is_single_mode{false};
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 8e79f707b..173b36da4 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -6,6 +6,7 @@
6#include <numeric> 6#include <numeric>
7#include <vector> 7#include <vector>
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/core.h"
9#include "core/file_sys/content_archive.h" 10#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h" 11#include "core/file_sys/control_metadata.h"
11#include "core/file_sys/nca_metadata.h" 12#include "core/file_sys/nca_metadata.h"
@@ -163,7 +164,8 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
163 rb.Push(RESULT_SUCCESS); 164 rb.Push(RESULT_SUCCESS);
164 165
165 const auto title_id = system.CurrentProcess()->GetTitleID(); 166 const auto title_id = system.CurrentProcess()->GetTitleID();
166 FileSys::PatchManager pm{title_id}; 167 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
168 system.GetContentProvider()};
167 169
168 const auto res = pm.GetControlMetadata(); 170 const auto res = pm.GetControlMetadata();
169 if (res.first == nullptr) { 171 if (res.first == nullptr) {
diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp
index 85bbf5988..e2d8f0027 100644
--- a/src/core/hle/service/apm/apm.cpp
+++ b/src/core/hle/service/apm/apm.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 "core/core.h"
5#include "core/hle/ipc_helpers.h" 6#include "core/hle/ipc_helpers.h"
6#include "core/hle/service/apm/apm.h" 7#include "core/hle/service/apm/apm.h"
7#include "core/hle/service/apm/interface.h" 8#include "core/hle/service/apm/interface.h"
diff --git a/src/core/hle/service/apm/controller.cpp b/src/core/hle/service/apm/controller.cpp
index 25a886238..ce993bad3 100644
--- a/src/core/hle/service/apm/controller.cpp
+++ b/src/core/hle/service/apm/controller.cpp
@@ -69,7 +69,8 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
69} 69}
70 70
71PerformanceMode Controller::GetCurrentPerformanceMode() const { 71PerformanceMode Controller::GetCurrentPerformanceMode() const {
72 return Settings::values.use_docked_mode ? PerformanceMode::Docked : PerformanceMode::Handheld; 72 return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked
73 : PerformanceMode::Handheld;
73} 74}
74 75
75PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { 76PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp
index db0e06ca1..68deb0600 100644
--- a/src/core/hle/service/bcat/module.cpp
+++ b/src/core/hle/service/bcat/module.cpp
@@ -8,6 +8,7 @@
8#include "common/hex_util.h" 8#include "common/hex_util.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/string_util.h" 10#include "common/string_util.h"
11#include "core/core.h"
11#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
12#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/process.h" 14#include "core/hle/kernel/process.h"
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index f311afa2f..d4f0dd1ab 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.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 "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/core.h"
6#include "core/hle/ipc_helpers.h" 7#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/hle_ipc.h" 8#include "core/hle/kernel/hle_ipc.h"
8#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/kernel.h"
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 0d251c6d0..c8f8ddbd5 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -5,6 +5,7 @@
5#include <memory> 5#include <memory>
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h"
8#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/hle_ipc.h" 10#include "core/hle/kernel/hle_ipc.h"
10#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp
index 8e2b83629..f9479bdb3 100644
--- a/src/core/hle/service/caps/caps_u.cpp
+++ b/src/core/hle/service/caps/caps_u.cpp
@@ -41,7 +41,7 @@ CAPS_U::CAPS_U() : ServiceFramework("caps:u") {
41 {130, nullptr, "PrecheckToCreateContentsForApplication"}, 41 {130, nullptr, "PrecheckToCreateContentsForApplication"},
42 {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, 42 {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
43 {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, 43 {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
44 {142, nullptr, "GetAlbumFileList3AaeAruid"}, 44 {142, &CAPS_U::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"},
45 {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, 45 {143, nullptr, "GetAlbumFileList4AaeUidAruid"},
46 {60002, nullptr, "OpenAccessorSessionForApplication"}, 46 {60002, nullptr, "OpenAccessorSessionForApplication"},
47 }; 47 };
@@ -77,17 +77,24 @@ void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& c
77 77
78 // TODO: Update this when we implement the album. 78 // TODO: Update this when we implement the album.
79 // Currently we do not have a method of accessing album entries, set this to 0 for now. 79 // Currently we do not have a method of accessing album entries, set this to 0 for now.
80 constexpr s32 total_entries{0}; 80 constexpr u32 total_entries_1{};
81 constexpr u32 total_entries_2{};
81 82
82 LOG_WARNING(Service_Capture, 83 LOG_WARNING(
83 "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, " 84 Service_Capture,
84 "end_posix_time={}, applet_resource_user_id={}, total_entries={}", 85 "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
85 pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id, 86 "end_posix_time={}, applet_resource_user_id={}, total_entries_1={}, total_entries_2={}",
86 total_entries); 87 pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id,
88 total_entries_1, total_entries_2);
87 89
88 IPC::ResponseBuilder rb{ctx, 3}; 90 IPC::ResponseBuilder rb{ctx, 4};
89 rb.Push(RESULT_SUCCESS); 91 rb.Push(RESULT_SUCCESS);
90 rb.Push(total_entries); 92 rb.Push(total_entries_1);
93 rb.Push(total_entries_2);
94}
95
96void CAPS_U::GetAlbumFileList3AaeAruid(Kernel::HLERequestContext& ctx) {
97 GetAlbumContentsFileListForApplication(ctx);
91} 98}
92 99
93} // namespace Service::Capture 100} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h
index e04e56bbc..4b80f3156 100644
--- a/src/core/hle/service/caps/caps_u.h
+++ b/src/core/hle/service/caps/caps_u.h
@@ -20,6 +20,7 @@ public:
20private: 20private:
21 void SetShimLibraryVersion(Kernel::HLERequestContext& ctx); 21 void SetShimLibraryVersion(Kernel::HLERequestContext& ctx);
22 void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx); 22 void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx);
23 void GetAlbumFileList3AaeAruid(Kernel::HLERequestContext& ctx);
23}; 24};
24 25
25} // namespace Service::Capture 26} // namespace Service::Capture
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 3cdef4888..2e53cae5b 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -455,7 +455,9 @@ FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataTy
455 const auto res = system.GetAppLoader().ReadControlData(nacp); 455 const auto res = system.GetAppLoader().ReadControlData(nacp);
456 456
457 if (res != Loader::ResultStatus::Success) { 457 if (res != Loader::ResultStatus::Success) {
458 FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; 458 const FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID(),
459 system.GetFileSystemController(),
460 system.GetContentProvider()};
459 const auto metadata = pm.GetControlMetadata(); 461 const auto metadata = pm.GetControlMetadata();
460 const auto& nacp_unique = metadata.first; 462 const auto& nacp_unique = metadata.first;
461 463
@@ -728,7 +730,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
728void InstallInterfaces(Core::System& system) { 730void InstallInterfaces(Core::System& system) {
729 std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager()); 731 std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager());
730 std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager()); 732 std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager());
731 std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetReporter()) 733 std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetContentProvider(),
734 system.GetReporter())
732 ->InstallAsService(system.ServiceManager()); 735 ->InstallAsService(system.ServiceManager());
733} 736}
734 737
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 649128be4..031c6dbf6 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -650,8 +650,10 @@ private:
650 u64 next_entry_index = 0; 650 u64 next_entry_index = 0;
651}; 651};
652 652
653FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter) 653FSP_SRV::FSP_SRV(FileSystemController& fsc_, const FileSys::ContentProvider& content_provider_,
654 : ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) { 654 const Core::Reporter& reporter_)
655 : ServiceFramework("fsp-srv"), fsc(fsc_), content_provider{content_provider_},
656 reporter(reporter_) {
655 // clang-format off 657 // clang-format off
656 static const FunctionInfo functions[] = { 658 static const FunctionInfo functions[] = {
657 {0, nullptr, "OpenFileSystem"}, 659 {0, nullptr, "OpenFileSystem"},
@@ -968,7 +970,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
968 return; 970 return;
969 } 971 }
970 972
971 FileSys::PatchManager pm{title_id}; 973 const FileSys::PatchManager pm{title_id, fsc, content_provider};
972 974
973 auto storage = std::make_shared<IStorage>( 975 auto storage = std::make_shared<IStorage>(
974 pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data)); 976 pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 4964e874e..6c7239e6a 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -12,8 +12,9 @@ class Reporter;
12} 12}
13 13
14namespace FileSys { 14namespace FileSys {
15class ContentProvider;
15class FileSystemBackend; 16class FileSystemBackend;
16} 17} // namespace FileSys
17 18
18namespace Service::FileSystem { 19namespace Service::FileSystem {
19 20
@@ -32,7 +33,8 @@ enum class LogMode : u32 {
32 33
33class FSP_SRV final : public ServiceFramework<FSP_SRV> { 34class FSP_SRV final : public ServiceFramework<FSP_SRV> {
34public: 35public:
35 explicit FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter); 36 explicit FSP_SRV(FileSystemController& fsc_, const FileSys::ContentProvider& content_provider_,
37 const Core::Reporter& reporter_);
36 ~FSP_SRV() override; 38 ~FSP_SRV() override;
37 39
38private: 40private:
@@ -55,6 +57,7 @@ private:
55 void OpenMultiCommitManager(Kernel::HLERequestContext& ctx); 57 void OpenMultiCommitManager(Kernel::HLERequestContext& ctx);
56 58
57 FileSystemController& fsc; 59 FileSystemController& fsc;
60 const FileSys::ContentProvider& content_provider;
58 61
59 FileSys::VirtualFile romfs; 62 FileSys::VirtualFile romfs;
60 u64 current_process_id = 0; 63 u64 current_process_id = 0;
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index b7adaffc7..ebb323da2 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -5,6 +5,7 @@
5#include <queue> 5#include <queue>
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "common/uuid.h" 7#include "common/uuid.h"
8#include "core/core.h"
8#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/readable_event.h" 10#include "core/hle/kernel/readable_event.h"
10#include "core/hle/kernel/writable_event.h" 11#include "core/hle/kernel/writable_event.h"
diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp
index b591ce31b..c6252ff89 100644
--- a/src/core/hle/service/glue/arp.cpp
+++ b/src/core/hle/service/glue/arp.cpp
@@ -5,6 +5,7 @@
5#include <memory> 5#include <memory>
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h"
8#include "core/file_sys/control_metadata.h" 9#include "core/file_sys/control_metadata.h"
9#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
10#include "core/hle/kernel/hle_ipc.h" 11#include "core/hle/kernel/hle_ipc.h"
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index e311bc18c..e2539ded8 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -117,7 +117,10 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
117} 117}
118 118
119Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} 119Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
120Controller_NPad::~Controller_NPad() = default; 120
121Controller_NPad::~Controller_NPad() {
122 OnRelease();
123}
121 124
122void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { 125void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
123 const auto controller_type = connected_controllers[controller_idx].type; 126 const auto controller_type = connected_controllers[controller_idx].type;
@@ -139,7 +142,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
139 controller.properties.is_vertical.Assign(1); 142 controller.properties.is_vertical.Assign(1);
140 controller.properties.use_plus.Assign(1); 143 controller.properties.use_plus.Assign(1);
141 controller.properties.use_minus.Assign(1); 144 controller.properties.use_minus.Assign(1);
142 controller.pad_assignment = NPadAssignments::Single; 145 controller.pad_assignment = NpadAssignments::Single;
143 break; 146 break;
144 case NPadControllerType::Handheld: 147 case NPadControllerType::Handheld:
145 controller.joy_styles.handheld.Assign(1); 148 controller.joy_styles.handheld.Assign(1);
@@ -147,7 +150,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
147 controller.properties.is_vertical.Assign(1); 150 controller.properties.is_vertical.Assign(1);
148 controller.properties.use_plus.Assign(1); 151 controller.properties.use_plus.Assign(1);
149 controller.properties.use_minus.Assign(1); 152 controller.properties.use_minus.Assign(1);
150 controller.pad_assignment = NPadAssignments::Dual; 153 controller.pad_assignment = NpadAssignments::Dual;
151 break; 154 break;
152 case NPadControllerType::JoyDual: 155 case NPadControllerType::JoyDual:
153 controller.joy_styles.joycon_dual.Assign(1); 156 controller.joy_styles.joycon_dual.Assign(1);
@@ -156,26 +159,26 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
156 controller.properties.is_vertical.Assign(1); 159 controller.properties.is_vertical.Assign(1);
157 controller.properties.use_plus.Assign(1); 160 controller.properties.use_plus.Assign(1);
158 controller.properties.use_minus.Assign(1); 161 controller.properties.use_minus.Assign(1);
159 controller.pad_assignment = NPadAssignments::Dual; 162 controller.pad_assignment = NpadAssignments::Dual;
160 break; 163 break;
161 case NPadControllerType::JoyLeft: 164 case NPadControllerType::JoyLeft:
162 controller.joy_styles.joycon_left.Assign(1); 165 controller.joy_styles.joycon_left.Assign(1);
163 controller.device_type.joycon_left.Assign(1); 166 controller.device_type.joycon_left.Assign(1);
164 controller.properties.is_horizontal.Assign(1); 167 controller.properties.is_horizontal.Assign(1);
165 controller.properties.use_minus.Assign(1); 168 controller.properties.use_minus.Assign(1);
166 controller.pad_assignment = NPadAssignments::Single; 169 controller.pad_assignment = NpadAssignments::Single;
167 break; 170 break;
168 case NPadControllerType::JoyRight: 171 case NPadControllerType::JoyRight:
169 controller.joy_styles.joycon_right.Assign(1); 172 controller.joy_styles.joycon_right.Assign(1);
170 controller.device_type.joycon_right.Assign(1); 173 controller.device_type.joycon_right.Assign(1);
171 controller.properties.is_horizontal.Assign(1); 174 controller.properties.is_horizontal.Assign(1);
172 controller.properties.use_plus.Assign(1); 175 controller.properties.use_plus.Assign(1);
173 controller.pad_assignment = NPadAssignments::Single; 176 controller.pad_assignment = NpadAssignments::Single;
174 break; 177 break;
175 case NPadControllerType::Pokeball: 178 case NPadControllerType::Pokeball:
176 controller.joy_styles.pokeball.Assign(1); 179 controller.joy_styles.pokeball.Assign(1);
177 controller.device_type.pokeball.Assign(1); 180 controller.device_type.pokeball.Assign(1);
178 controller.pad_assignment = NPadAssignments::Single; 181 controller.pad_assignment = NpadAssignments::Single;
179 break; 182 break;
180 } 183 }
181 184
@@ -184,11 +187,14 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
184 controller.single_color.button_color = 0; 187 controller.single_color.button_color = 0;
185 188
186 controller.dual_color_error = ColorReadError::ReadOk; 189 controller.dual_color_error = ColorReadError::ReadOk;
187 controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left; 190 controller.left_color.body_color =
188 controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left; 191 Settings::values.players.GetValue()[controller_idx].body_color_left;
189 controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right; 192 controller.left_color.button_color =
193 Settings::values.players.GetValue()[controller_idx].button_color_left;
194 controller.right_color.body_color =
195 Settings::values.players.GetValue()[controller_idx].body_color_right;
190 controller.right_color.button_color = 196 controller.right_color.button_color =
191 Settings::values.players[controller_idx].button_color_right; 197 Settings::values.players.GetValue()[controller_idx].button_color_right;
192 198
193 controller.battery_level[0] = BATTERY_FULL; 199 controller.battery_level[0] = BATTERY_FULL;
194 controller.battery_level[1] = BATTERY_FULL; 200 controller.battery_level[1] = BATTERY_FULL;
@@ -199,7 +205,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
199 205
200void Controller_NPad::OnInit() { 206void Controller_NPad::OnInit() {
201 auto& kernel = system.Kernel(); 207 auto& kernel = system.Kernel();
202 for (std::size_t i = 0; i < styleset_changed_events.size(); i++) { 208 for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
203 styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair( 209 styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
204 kernel, fmt::format("npad:NpadStyleSetChanged_{}", i)); 210 kernel, fmt::format("npad:NpadStyleSetChanged_{}", i));
205 } 211 }
@@ -208,6 +214,8 @@ void Controller_NPad::OnInit() {
208 return; 214 return;
209 } 215 }
210 216
217 OnLoadInputDevices();
218
211 if (style.raw == 0) { 219 if (style.raw == 0) {
212 // We want to support all controllers 220 // We want to support all controllers
213 style.handheld.Assign(1); 221 style.handheld.Assign(1);
@@ -218,12 +226,27 @@ void Controller_NPad::OnInit() {
218 style.pokeball.Assign(1); 226 style.pokeball.Assign(1);
219 } 227 }
220 228
221 std::transform(Settings::values.players.begin(), Settings::values.players.end(), 229 std::transform(Settings::values.players.GetValue().begin(),
222 connected_controllers.begin(), [](const Settings::PlayerInput& player) { 230 Settings::values.players.GetValue().end(), connected_controllers.begin(),
231 [](const Settings::PlayerInput& player) {
223 return ControllerHolder{MapSettingsTypeToNPad(player.controller_type), 232 return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
224 player.connected}; 233 player.connected};
225 }); 234 });
226 235
236 // Connect the Player 1 or Handheld controller if none are connected.
237 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
238 [](const ControllerHolder& controller) { return controller.is_connected; })) {
239 const auto controller =
240 MapSettingsTypeToNPad(Settings::values.players.GetValue()[0].controller_type);
241 if (controller == NPadControllerType::Handheld) {
242 Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
243 connected_controllers[HANDHELD_INDEX] = {controller, true};
244 } else {
245 Settings::values.players.GetValue()[0].connected = true;
246 connected_controllers[0] = {controller, true};
247 }
248 }
249
227 // Account for handheld 250 // Account for handheld
228 if (connected_controllers[HANDHELD_INDEX].is_connected) { 251 if (connected_controllers[HANDHELD_INDEX].is_connected) {
229 connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld; 252 connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
@@ -242,7 +265,7 @@ void Controller_NPad::OnInit() {
242} 265}
243 266
244void Controller_NPad::OnLoadInputDevices() { 267void Controller_NPad::OnLoadInputDevices() {
245 const auto& players = Settings::values.players; 268 const auto& players = Settings::values.players.GetValue();
246 for (std::size_t i = 0; i < players.size(); ++i) { 269 for (std::size_t i = 0; i < players.size(); ++i) {
247 std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, 270 std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
248 players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END, 271 players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
@@ -250,13 +273,26 @@ void Controller_NPad::OnLoadInputDevices() {
250 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 273 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
251 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, 274 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
252 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>); 275 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
276 std::transform(players[i].vibrations.begin() +
277 Settings::NativeVibration::VIBRATION_HID_BEGIN,
278 players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END,
279 vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>);
253 std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, 280 std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
254 players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END, 281 players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
255 motions[i].begin(), Input::CreateDevice<Input::MotionDevice>); 282 motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
283 for (std::size_t device_idx = 0; device_idx < vibrations[i].size(); ++device_idx) {
284 InitializeVibrationDeviceAtIndex(i, device_idx);
285 }
256 } 286 }
257} 287}
258 288
259void Controller_NPad::OnRelease() {} 289void Controller_NPad::OnRelease() {
290 for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) {
291 for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) {
292 VibrateControllerAtIndex(npad_idx, device_idx, {});
293 }
294 }
295}
260 296
261void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { 297void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
262 const auto controller_idx = NPadIdToIndex(npad_id); 298 const auto controller_idx = NPadIdToIndex(npad_id);
@@ -339,7 +375,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
339 if (!IsControllerActivated()) { 375 if (!IsControllerActivated()) {
340 return; 376 return;
341 } 377 }
342 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { 378 for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
343 auto& npad = shared_memory_entries[i]; 379 auto& npad = shared_memory_entries[i];
344 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states, 380 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
345 &npad.handheld_states, 381 &npad.handheld_states,
@@ -481,7 +517,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
481 if (!IsControllerActivated()) { 517 if (!IsControllerActivated()) {
482 return; 518 return;
483 } 519 }
484 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { 520 for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
485 auto& npad = shared_memory_entries[i]; 521 auto& npad = shared_memory_entries[i];
486 522
487 const auto& controller_type = connected_controllers[i].type; 523 const auto& controller_type = connected_controllers[i].type;
@@ -515,7 +551,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
515 // Try to read sixaxis sensor states 551 // Try to read sixaxis sensor states
516 std::array<MotionDevice, 2> motion_devices; 552 std::array<MotionDevice, 2> motion_devices;
517 553
518 if (sixaxis_sensors_enabled && Settings::values.motion_enabled) { 554 if (sixaxis_sensors_enabled && Settings::values.motion_enabled.GetValue()) {
519 sixaxis_at_rest = true; 555 sixaxis_at_rest = true;
520 for (std::size_t e = 0; e < motion_devices.size(); ++e) { 556 for (std::size_t e = 0; e < motion_devices.size(); ++e) {
521 const auto& device = motions[i][e]; 557 const auto& device = motions[i][e];
@@ -601,15 +637,15 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
601 shared_memory_entries.size() * sizeof(NPadEntry)); 637 shared_memory_entries.size() * sizeof(NPadEntry));
602} 638}
603 639
604void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { 640void Controller_NPad::SetSupportedStyleSet(NpadStyleSet style_set) {
605 style.raw = style_set.raw; 641 style.raw = style_set.raw;
606} 642}
607 643
608Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const { 644Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const {
609 return style; 645 return style;
610} 646}
611 647
612void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { 648void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) {
613 ASSERT(length > 0 && (length % sizeof(u32)) == 0); 649 ASSERT(length > 0 && (length % sizeof(u32)) == 0);
614 supported_npad_id_types.clear(); 650 supported_npad_id_types.clear();
615 supported_npad_id_types.resize(length / sizeof(u32)); 651 supported_npad_id_types.resize(length / sizeof(u32));
@@ -621,7 +657,7 @@ void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length)
621 std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size()); 657 std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size());
622} 658}
623 659
624std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const { 660std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
625 return supported_npad_id_types.size(); 661 return supported_npad_id_types.size();
626} 662}
627 663
@@ -641,7 +677,7 @@ Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActi
641 return handheld_activation_mode; 677 return handheld_activation_mode;
642} 678}
643 679
644void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { 680void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) {
645 const std::size_t npad_index = NPadIdToIndex(npad_id); 681 const std::size_t npad_index = NPadIdToIndex(npad_id);
646 ASSERT(npad_index < shared_memory_entries.size()); 682 ASSERT(npad_index < shared_memory_entries.size());
647 if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) { 683 if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) {
@@ -649,35 +685,140 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode)
649 } 685 }
650} 686}
651 687
652void Controller_NPad::VibrateController(const std::vector<u32>& controllers, 688bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
653 const std::vector<Vibration>& vibrations) { 689 const VibrationValue& vibration_value) {
654 LOG_TRACE(Service_HID, "called"); 690 if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) {
655 691 return false;
656 if (!Settings::values.vibration_enabled || !can_controllers_vibrate) {
657 return;
658 } 692 }
659 bool success = true; 693
660 for (std::size_t i = 0; i < controllers.size(); ++i) { 694 const auto& player = Settings::values.players.GetValue()[npad_index];
661 if (!connected_controllers[i].is_connected) { 695
662 continue; 696 if (!player.vibration_enabled) {
697 if (latest_vibration_values[npad_index][device_index].amp_low != 0.0f ||
698 latest_vibration_values[npad_index][device_index].amp_high != 0.0f) {
699 // Send an empty vibration to stop any vibrations.
700 vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f);
701 // Then reset the vibration value to its default value.
702 latest_vibration_values[npad_index][device_index] = {};
663 } 703 }
664 using namespace Settings::NativeButton; 704
665 const auto& button_state = buttons[i]; 705 return false;
666 if (button_state[A - BUTTON_HID_BEGIN]) { 706 }
667 if (button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( 707
668 vibrations[0].amp_high, vibrations[0].amp_low, vibrations[0].freq_high, 708 if (!Settings::values.enable_accurate_vibrations.GetValue()) {
669 vibrations[0].freq_low)) { 709 using std::chrono::duration_cast;
670 success = false; 710 using std::chrono::milliseconds;
671 } 711 using std::chrono::steady_clock;
712
713 const auto now = steady_clock::now();
714
715 // Filter out non-zero vibrations that are within 10ms of each other.
716 if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) &&
717 duration_cast<milliseconds>(now - last_vibration_timepoints[npad_index][device_index]) <
718 milliseconds(10)) {
719 return false;
672 } 720 }
721
722 last_vibration_timepoints[npad_index][device_index] = now;
723 }
724
725 auto& vibration = vibrations[npad_index][device_index];
726 const auto player_vibration_strength = static_cast<f32>(player.vibration_strength);
727 const auto amp_low =
728 std::min(vibration_value.amp_low * player_vibration_strength / 100.0f, 1.0f);
729 const auto amp_high =
730 std::min(vibration_value.amp_high * player_vibration_strength / 100.0f, 1.0f);
731 return vibration->SetRumblePlay(amp_low, vibration_value.freq_low, amp_high,
732 vibration_value.freq_high);
733}
734
735void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle,
736 const VibrationValue& vibration_value) {
737 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
738 return;
739 }
740
741 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
742 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
743
744 if (!vibration_devices_mounted[npad_index][device_index] ||
745 !connected_controllers[npad_index].is_connected) {
746 return;
673 } 747 }
674 if (success) { 748
675 last_processed_vibration = vibrations.back(); 749 if (vibration_device_handle.device_index == DeviceIndex::None) {
750 UNREACHABLE_MSG("DeviceIndex should never be None!");
751 return;
752 }
753
754 // Some games try to send mismatched parameters in the device handle, block these.
755 if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft &&
756 (vibration_device_handle.npad_type == NpadType::JoyconRight ||
757 vibration_device_handle.device_index == DeviceIndex::Right)) ||
758 (connected_controllers[npad_index].type == NPadControllerType::JoyRight &&
759 (vibration_device_handle.npad_type == NpadType::JoyconLeft ||
760 vibration_device_handle.device_index == DeviceIndex::Left))) {
761 return;
762 }
763
764 // Filter out vibrations with equivalent values to reduce unnecessary state changes.
765 if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low &&
766 vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) {
767 return;
768 }
769
770 if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) {
771 latest_vibration_values[npad_index][device_index] = vibration_value;
676 } 772 }
677} 773}
678 774
679Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { 775void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
680 return last_processed_vibration; 776 const std::vector<VibrationValue>& vibration_values) {
777 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
778 return;
779 }
780
781 ASSERT_OR_EXECUTE_MSG(
782 vibration_device_handles.size() == vibration_values.size(), { return; },
783 "The amount of device handles does not match with the amount of vibration values,"
784 "this is undefined behavior!");
785
786 for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) {
787 VibrateController(vibration_device_handles[i], vibration_values[i]);
788 }
789}
790
791Controller_NPad::VibrationValue Controller_NPad::GetLastVibration(
792 const DeviceHandle& vibration_device_handle) const {
793 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
794 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
795 return latest_vibration_values[npad_index][device_index];
796}
797
798void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_device_handle) {
799 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
800 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
801 InitializeVibrationDeviceAtIndex(npad_index, device_index);
802}
803
804void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index,
805 std::size_t device_index) {
806 if (vibrations[npad_index][device_index]) {
807 vibration_devices_mounted[npad_index][device_index] =
808 vibrations[npad_index][device_index]->GetStatus() == 1;
809 } else {
810 vibration_devices_mounted[npad_index][device_index] = false;
811 }
812}
813
814void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
815 permit_vibration_session_enabled = permit_vibration_session;
816}
817
818bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const {
819 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
820 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
821 return vibration_devices_mounted[npad_index][device_index];
681} 822}
682 823
683std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { 824std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
@@ -696,31 +837,38 @@ void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::siz
696void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, 837void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
697 bool connected) { 838 bool connected) {
698 if (!connected) { 839 if (!connected) {
699 DisconnectNPadAtIndex(npad_index); 840 DisconnectNpadAtIndex(npad_index);
700 return; 841 return;
701 } 842 }
702 843
703 if (controller == NPadControllerType::Handheld) { 844 if (controller == NPadControllerType::Handheld) {
704 Settings::values.players[HANDHELD_INDEX].controller_type = 845 Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type =
705 MapNPadToSettingsType(controller); 846 MapNPadToSettingsType(controller);
706 Settings::values.players[HANDHELD_INDEX].connected = true; 847 Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
707 connected_controllers[HANDHELD_INDEX] = {controller, true}; 848 connected_controllers[HANDHELD_INDEX] = {controller, true};
708 InitNewlyAddedController(HANDHELD_INDEX); 849 InitNewlyAddedController(HANDHELD_INDEX);
709 return; 850 return;
710 } 851 }
711 852
712 Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller); 853 Settings::values.players.GetValue()[npad_index].controller_type =
713 Settings::values.players[npad_index].connected = true; 854 MapNPadToSettingsType(controller);
855 Settings::values.players.GetValue()[npad_index].connected = true;
714 connected_controllers[npad_index] = {controller, true}; 856 connected_controllers[npad_index] = {controller, true};
715 InitNewlyAddedController(npad_index); 857 InitNewlyAddedController(npad_index);
716} 858}
717 859
718void Controller_NPad::DisconnectNPad(u32 npad_id) { 860void Controller_NPad::DisconnectNpad(u32 npad_id) {
719 DisconnectNPadAtIndex(NPadIdToIndex(npad_id)); 861 DisconnectNpadAtIndex(NPadIdToIndex(npad_id));
720} 862}
721 863
722void Controller_NPad::DisconnectNPadAtIndex(std::size_t npad_index) { 864void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) {
723 Settings::values.players[npad_index].connected = false; 865 for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) {
866 // Send an empty vibration to stop any vibrations.
867 VibrateControllerAtIndex(npad_index, device_idx, {});
868 vibration_devices_mounted[npad_index][device_idx] = false;
869 }
870
871 Settings::values.players.GetValue()[npad_index].connected = false;
724 connected_controllers[npad_index].is_connected = false; 872 connected_controllers[npad_index].is_connected = false;
725 873
726 auto& controller = shared_memory_entries[npad_index]; 874 auto& controller = shared_memory_entries[npad_index];
@@ -758,7 +906,7 @@ void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
758 (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft && 906 (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft &&
759 connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) { 907 connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) {
760 // Disconnect the joycon at the second id and connect the dual joycon at the first index. 908 // Disconnect the joycon at the second id and connect the dual joycon at the first index.
761 DisconnectNPad(npad_id_2); 909 DisconnectNpad(npad_id_2);
762 AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1); 910 AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1);
763 } 911 }
764} 912}
@@ -830,14 +978,6 @@ void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_prot
830 unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled; 978 unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled;
831} 979}
832 980
833void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
834 can_controllers_vibrate = can_vibrate;
835}
836
837bool Controller_NPad::IsVibrationEnabled() const {
838 return can_controllers_vibrate;
839}
840
841void Controller_NPad::ClearAllConnectedControllers() { 981void Controller_NPad::ClearAllConnectedControllers() {
842 for (auto& controller : connected_controllers) { 982 for (auto& controller : connected_controllers) {
843 if (controller.is_connected && controller.type != NPadControllerType::None) { 983 if (controller.is_connected && controller.type != NPadControllerType::None) {
@@ -882,7 +1022,7 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
882 return false; 1022 return false;
883 } 1023 }
884 // Handheld should not be supported in docked mode 1024 // Handheld should not be supported in docked mode
885 if (Settings::values.use_docked_mode) { 1025 if (Settings::values.use_docked_mode.GetValue()) {
886 return false; 1026 return false;
887 } 1027 }
888 1028
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index fd5c5a6eb..160dcbbe3 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -39,28 +39,30 @@ public:
39 // Called when input devices should be loaded 39 // Called when input devices should be loaded
40 void OnLoadInputDevices() override; 40 void OnLoadInputDevices() override;
41 41
42 struct NPadType { 42 enum class NPadControllerType {
43 union { 43 None,
44 u32_le raw{}; 44 ProController,
45 45 Handheld,
46 BitField<0, 1, u32> pro_controller; 46 JoyDual,
47 BitField<1, 1, u32> handheld; 47 JoyLeft,
48 BitField<2, 1, u32> joycon_dual; 48 JoyRight,
49 BitField<3, 1, u32> joycon_left; 49 Pokeball,
50 BitField<4, 1, u32> joycon_right; 50 };
51 51
52 BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible 52 enum class NpadType : u8 {
53 }; 53 ProController = 3,
54 Handheld = 4,
55 JoyconDual = 5,
56 JoyconLeft = 6,
57 JoyconRight = 7,
58 Pokeball = 9,
54 }; 59 };
55 static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
56 60
57 struct Vibration { 61 enum class DeviceIndex : u8 {
58 f32 amp_low; 62 Left = 0,
59 f32 freq_low; 63 Right = 1,
60 f32 amp_high; 64 None = 2,
61 f32 freq_high;
62 }; 65 };
63 static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size");
64 66
65 enum class GyroscopeZeroDriftMode : u32 { 67 enum class GyroscopeZeroDriftMode : u32 {
66 Loose = 0, 68 Loose = 0,
@@ -73,7 +75,7 @@ public:
73 Horizontal = 1, 75 Horizontal = 1,
74 }; 76 };
75 77
76 enum class NPadAssignments : u32_le { 78 enum class NpadAssignments : u32 {
77 Dual = 0, 79 Dual = 0,
78 Single = 1, 80 Single = 1,
79 }; 81 };
@@ -84,15 +86,36 @@ public:
84 None = 2, 86 None = 2,
85 }; 87 };
86 88
87 enum class NPadControllerType { 89 struct DeviceHandle {
88 None, 90 NpadType npad_type{};
89 ProController, 91 u8 npad_id{};
90 Handheld, 92 DeviceIndex device_index{};
91 JoyDual, 93 INSERT_PADDING_BYTES(1);
92 JoyLeft, 94 };
93 JoyRight, 95 static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
94 Pokeball, 96
97 struct NpadStyleSet {
98 union {
99 u32_le raw{};
100
101 BitField<0, 1, u32> pro_controller;
102 BitField<1, 1, u32> handheld;
103 BitField<2, 1, u32> joycon_dual;
104 BitField<3, 1, u32> joycon_left;
105 BitField<4, 1, u32> joycon_right;
106
107 BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
108 };
109 };
110 static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
111
112 struct VibrationValue {
113 f32 amp_low{0.0f};
114 f32 freq_low{160.0f};
115 f32 amp_high{0.0f};
116 f32 freq_high{320.0f};
95 }; 117 };
118 static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size");
96 119
97 struct LedPattern { 120 struct LedPattern {
98 explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) { 121 explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
@@ -110,12 +133,12 @@ public:
110 }; 133 };
111 }; 134 };
112 135
113 void SetSupportedStyleSet(NPadType style_set); 136 void SetSupportedStyleSet(NpadStyleSet style_set);
114 NPadType GetSupportedStyleSet() const; 137 NpadStyleSet GetSupportedStyleSet() const;
115 138
116 void SetSupportedNPadIdTypes(u8* data, std::size_t length); 139 void SetSupportedNpadIdTypes(u8* data, std::size_t length);
117 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); 140 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
118 std::size_t GetSupportedNPadIdTypesSize() const; 141 std::size_t GetSupportedNpadIdTypesSize() const;
119 142
120 void SetHoldType(NpadHoldType joy_hold_type); 143 void SetHoldType(NpadHoldType joy_hold_type);
121 NpadHoldType GetHoldType() const; 144 NpadHoldType GetHoldType() const;
@@ -123,12 +146,26 @@ public:
123 void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode); 146 void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
124 NpadHandheldActivationMode GetNpadHandheldActivationMode() const; 147 NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
125 148
126 void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode); 149 void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode);
150
151 bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
152 const VibrationValue& vibration_value);
153
154 void VibrateController(const DeviceHandle& vibration_device_handle,
155 const VibrationValue& vibration_value);
156
157 void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
158 const std::vector<VibrationValue>& vibration_values);
159
160 VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const;
161
162 void InitializeVibrationDevice(const DeviceHandle& vibration_device_handle);
163
164 void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index);
127 165
128 void VibrateController(const std::vector<u32>& controllers, 166 void SetPermitVibrationSession(bool permit_vibration_session);
129 const std::vector<Vibration>& vibrations);
130 167
131 Vibration GetLastVibration() const; 168 bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const;
132 169
133 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const; 170 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
134 void SignalStyleSetChangedEvent(u32 npad_id) const; 171 void SignalStyleSetChangedEvent(u32 npad_id) const;
@@ -138,8 +175,8 @@ public:
138 // Adds a new controller at an index with connection status. 175 // Adds a new controller at an index with connection status.
139 void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected); 176 void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
140 177
141 void DisconnectNPad(u32 npad_id); 178 void DisconnectNpad(u32 npad_id);
142 void DisconnectNPadAtIndex(std::size_t index); 179 void DisconnectNpadAtIndex(std::size_t index);
143 180
144 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); 181 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
145 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; 182 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
@@ -148,8 +185,6 @@ public:
148 LedPattern GetLedPattern(u32 npad_id); 185 LedPattern GetLedPattern(u32 npad_id);
149 bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const; 186 bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const;
150 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id); 187 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id);
151 void SetVibrationEnabled(bool can_vibrate);
152 bool IsVibrationEnabled() const;
153 void ClearAllConnectedControllers(); 188 void ClearAllConnectedControllers();
154 void DisconnectAllConnectedControllers(); 189 void DisconnectAllConnectedControllers();
155 void ConnectAllDisconnectedControllers(); 190 void ConnectAllDisconnectedControllers();
@@ -324,8 +359,8 @@ private:
324 }; 359 };
325 360
326 struct NPadEntry { 361 struct NPadEntry {
327 NPadType joy_styles; 362 NpadStyleSet joy_styles;
328 NPadAssignments pad_assignment; 363 NpadAssignments pad_assignment;
329 364
330 ColorReadError single_color_error; 365 ColorReadError single_color_error;
331 ControllerColor single_color; 366 ControllerColor single_color;
@@ -368,7 +403,7 @@ private:
368 403
369 u32 press_state{}; 404 u32 press_state{};
370 405
371 NPadType style{}; 406 NpadStyleSet style{};
372 std::array<NPadEntry, 10> shared_memory_entries{}; 407 std::array<NPadEntry, 10> shared_memory_entries{};
373 using ButtonArray = std::array< 408 using ButtonArray = std::array<
374 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>, 409 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
@@ -376,22 +411,28 @@ private:
376 using StickArray = std::array< 411 using StickArray = std::array<
377 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, 412 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
378 10>; 413 10>;
414 using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>,
415 Settings::NativeVibration::NUM_VIBRATIONS_HID>,
416 10>;
379 using MotionArray = std::array< 417 using MotionArray = std::array<
380 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTION_HID>, 418 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
381 10>; 419 10>;
382 ButtonArray buttons; 420 ButtonArray buttons;
383 StickArray sticks; 421 StickArray sticks;
422 VibrationArray vibrations;
384 MotionArray motions; 423 MotionArray motions;
385 std::vector<u32> supported_npad_id_types{}; 424 std::vector<u32> supported_npad_id_types{};
386 NpadHoldType hold_type{NpadHoldType::Vertical}; 425 NpadHoldType hold_type{NpadHoldType::Vertical};
387 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; 426 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
388 // Each controller should have their own styleset changed event 427 // Each controller should have their own styleset changed event
389 std::array<Kernel::EventPair, 10> styleset_changed_events; 428 std::array<Kernel::EventPair, 10> styleset_changed_events;
390 Vibration last_processed_vibration{}; 429 std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10> last_vibration_timepoints;
430 std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
431 bool permit_vibration_session_enabled{false};
432 std::array<std::array<bool, 2>, 10> vibration_devices_mounted{};
391 std::array<ControllerHolder, 10> connected_controllers{}; 433 std::array<ControllerHolder, 10> connected_controllers{};
392 std::array<bool, 10> unintended_home_button_input_protection{}; 434 std::array<bool, 10> unintended_home_button_input_protection{};
393 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; 435 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
394 bool can_controllers_vibrate{true};
395 bool sixaxis_sensors_enabled{true}; 436 bool sixaxis_sensors_enabled{true};
396 bool sixaxis_at_rest{true}; 437 bool sixaxis_at_rest{true};
397 std::array<ControllerPad, 10> npad_pad_states{}; 438 std::array<ControllerPad, 10> npad_pad_states{};
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 50f709b25..902516b29 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -139,20 +139,34 @@ void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanose
139 139
140class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { 140class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
141public: 141public:
142 IActiveVibrationDeviceList() : ServiceFramework("IActiveVibrationDeviceList") { 142 explicit IActiveVibrationDeviceList(std::shared_ptr<IAppletResource> applet_resource_)
143 : ServiceFramework("IActiveVibrationDeviceList"), applet_resource(applet_resource_) {
144 // clang-format off
143 static const FunctionInfo functions[] = { 145 static const FunctionInfo functions[] = {
144 {0, &IActiveVibrationDeviceList::ActivateVibrationDevice, "ActivateVibrationDevice"}, 146 {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"},
145 }; 147 };
148 // clang-format on
149
146 RegisterHandlers(functions); 150 RegisterHandlers(functions);
147 } 151 }
148 152
149private: 153private:
150 void ActivateVibrationDevice(Kernel::HLERequestContext& ctx) { 154 void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) {
151 LOG_WARNING(Service_HID, "(STUBBED) called"); 155 IPC::RequestParser rp{ctx};
156 const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
157
158 applet_resource->GetController<Controller_NPad>(HidController::NPad)
159 .InitializeVibrationDevice(vibration_device_handle);
160
161 LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}",
162 vibration_device_handle.npad_type, vibration_device_handle.npad_id,
163 vibration_device_handle.device_index);
152 164
153 IPC::ResponseBuilder rb{ctx, 2}; 165 IPC::ResponseBuilder rb{ctx, 2};
154 rb.Push(RESULT_SUCCESS); 166 rb.Push(RESULT_SUCCESS);
155 } 167 }
168
169 std::shared_ptr<IAppletResource> applet_resource;
156}; 170};
157 171
158std::shared_ptr<IAppletResource> Hid::GetAppletResource() { 172std::shared_ptr<IAppletResource> Hid::GetAppletResource() {
@@ -241,7 +255,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
241 {208, nullptr, "GetActualVibrationGcErmCommand"}, 255 {208, nullptr, "GetActualVibrationGcErmCommand"},
242 {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, 256 {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
243 {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, 257 {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
244 {211, nullptr, "IsVibrationDeviceMounted"}, 258 {211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
245 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, 259 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
246 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, 260 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
247 {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, 261 {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
@@ -320,142 +334,152 @@ void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) {
320 rb.PushIpcInterface<IAppletResource>(applet_resource); 334 rb.PushIpcInterface<IAppletResource>(applet_resource);
321} 335}
322 336
323void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) { 337void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) {
324 IPC::RequestParser rp{ctx}; 338 IPC::RequestParser rp{ctx};
325 const auto basic_xpad_id{rp.Pop<u32>()};
326 const auto applet_resource_user_id{rp.Pop<u64>()}; 339 const auto applet_resource_user_id{rp.Pop<u64>()};
327 340
328 LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", basic_xpad_id, 341 applet_resource->ActivateController(HidController::DebugPad);
329 applet_resource_user_id); 342
343 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
330 344
331 applet_resource->ActivateController(HidController::XPad);
332 IPC::ResponseBuilder rb{ctx, 2}; 345 IPC::ResponseBuilder rb{ctx, 2};
333 rb.Push(RESULT_SUCCESS); 346 rb.Push(RESULT_SUCCESS);
334} 347}
335 348
336void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) { 349void Hid::ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
337 IPC::RequestParser rp{ctx}; 350 IPC::RequestParser rp{ctx};
338 const auto applet_resource_user_id{rp.Pop<u64>()}; 351 const auto applet_resource_user_id{rp.Pop<u64>()};
339 352
340 LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id); 353 applet_resource->ActivateController(HidController::Touchscreen);
341
342 IPC::ResponseBuilder rb{ctx, 3};
343 rb.Push(RESULT_SUCCESS);
344 rb.Push(0);
345}
346 354
347void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { 355 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
348 IPC::RequestParser rp{ctx};
349 const auto handle{rp.Pop<u32>()};
350 const auto applet_resource_user_id{rp.Pop<u64>()};
351 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
352 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
353 applet_resource_user_id);
354 356
355 IPC::ResponseBuilder rb{ctx, 2}; 357 IPC::ResponseBuilder rb{ctx, 2};
356 rb.Push(RESULT_SUCCESS); 358 rb.Push(RESULT_SUCCESS);
357} 359}
358 360
359void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { 361void Hid::ActivateMouse(Kernel::HLERequestContext& ctx) {
360 IPC::RequestParser rp{ctx}; 362 IPC::RequestParser rp{ctx};
361 const auto handle{rp.Pop<u32>()};
362 const auto applet_resource_user_id{rp.Pop<u64>()}; 363 const auto applet_resource_user_id{rp.Pop<u64>()};
363 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
364 364
365 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, 365 applet_resource->ActivateController(HidController::Mouse);
366 applet_resource_user_id); 366
367 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
367 368
368 IPC::ResponseBuilder rb{ctx, 2}; 369 IPC::ResponseBuilder rb{ctx, 2};
369 rb.Push(RESULT_SUCCESS); 370 rb.Push(RESULT_SUCCESS);
370} 371}
371 372
372void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { 373void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) {
373 IPC::RequestParser rp{ctx}; 374 IPC::RequestParser rp{ctx};
374 const auto applet_resource_user_id{rp.Pop<u64>()}; 375 const auto applet_resource_user_id{rp.Pop<u64>()};
375 376
377 applet_resource->ActivateController(HidController::Keyboard);
378
376 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 379 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
377 380
378 applet_resource->ActivateController(HidController::DebugPad);
379 IPC::ResponseBuilder rb{ctx, 2}; 381 IPC::ResponseBuilder rb{ctx, 2};
380 rb.Push(RESULT_SUCCESS); 382 rb.Push(RESULT_SUCCESS);
381} 383}
382 384
383void Hid::ActivateTouchScreen(Kernel::HLERequestContext& ctx) { 385void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) {
384 IPC::RequestParser rp{ctx}; 386 IPC::RequestParser rp{ctx};
385 const auto applet_resource_user_id{rp.Pop<u64>()}; 387 const auto flags{rp.Pop<u32>()};
386 388
387 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 389 LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags);
388 390
389 applet_resource->ActivateController(HidController::Touchscreen);
390 IPC::ResponseBuilder rb{ctx, 2}; 391 IPC::ResponseBuilder rb{ctx, 2};
391 rb.Push(RESULT_SUCCESS); 392 rb.Push(RESULT_SUCCESS);
392} 393}
393 394
394void Hid::ActivateMouse(Kernel::HLERequestContext& ctx) { 395void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
395 IPC::RequestParser rp{ctx}; 396 IPC::RequestParser rp{ctx};
396 const auto applet_resource_user_id{rp.Pop<u64>()}; 397 struct Parameters {
398 u32 basic_xpad_id{};
399 INSERT_PADDING_WORDS(1);
400 u64 applet_resource_user_id{};
401 };
397 402
398 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 403 const auto parameters{rp.PopRaw<Parameters>()};
404
405 applet_resource->ActivateController(HidController::XPad);
406
407 LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}",
408 parameters.basic_xpad_id, parameters.applet_resource_user_id);
399 409
400 applet_resource->ActivateController(HidController::Mouse);
401 IPC::ResponseBuilder rb{ctx, 2}; 410 IPC::ResponseBuilder rb{ctx, 2};
402 rb.Push(RESULT_SUCCESS); 411 rb.Push(RESULT_SUCCESS);
403} 412}
404 413
405void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) { 414void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
406 IPC::RequestParser rp{ctx}; 415 IPC::RequestParser rp{ctx};
407 const auto applet_resource_user_id{rp.Pop<u64>()}; 416 const auto applet_resource_user_id{rp.Pop<u64>()};
408 417
409 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 418 LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id);
410 419
411 applet_resource->ActivateController(HidController::Keyboard); 420 IPC::ResponseBuilder rb{ctx, 3};
412 IPC::ResponseBuilder rb{ctx, 2};
413 rb.Push(RESULT_SUCCESS); 421 rb.Push(RESULT_SUCCESS);
422 rb.Push(0);
414} 423}
415 424
416void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) { 425void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
417 IPC::RequestParser rp{ctx}; 426 IPC::RequestParser rp{ctx};
418 const auto flags{rp.Pop<u32>()}; 427 struct Parameters {
419 LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); 428 Controller_NPad::DeviceHandle sixaxis_handle{};
429 INSERT_PADDING_WORDS(1);
430 u64 applet_resource_user_id{};
431 };
420 432
421 IPC::ResponseBuilder rb{ctx, 2}; 433 const auto parameters{rp.PopRaw<Parameters>()};
422 rb.Push(RESULT_SUCCESS);
423}
424 434
425void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { 435 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
426 IPC::RequestParser rp{ctx};
427 const auto unknown{rp.Pop<u32>()};
428 const auto applet_resource_user_id{rp.Pop<u64>()};
429 436
430 LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown, 437 LOG_DEBUG(Service_HID,
431 applet_resource_user_id); 438 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
439 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
440 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
432 441
433 applet_resource->ActivateController(HidController::Gesture);
434 IPC::ResponseBuilder rb{ctx, 2}; 442 IPC::ResponseBuilder rb{ctx, 2};
435 rb.Push(RESULT_SUCCESS); 443 rb.Push(RESULT_SUCCESS);
436} 444}
437 445
438void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { 446void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
439 // Should have no effect with how our npad sets up the data
440 IPC::RequestParser rp{ctx}; 447 IPC::RequestParser rp{ctx};
441 const auto unknown{rp.Pop<u32>()}; 448 struct Parameters {
442 const auto applet_resource_user_id{rp.Pop<u64>()}; 449 Controller_NPad::DeviceHandle sixaxis_handle{};
450 INSERT_PADDING_WORDS(1);
451 u64 applet_resource_user_id{};
452 };
443 453
444 LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown, 454 const auto parameters{rp.PopRaw<Parameters>()};
445 applet_resource_user_id); 455
456 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
457
458 LOG_DEBUG(Service_HID,
459 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
460 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
461 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
446 462
447 applet_resource->ActivateController(HidController::NPad);
448 IPC::ResponseBuilder rb{ctx, 2}; 463 IPC::ResponseBuilder rb{ctx, 2};
449 rb.Push(RESULT_SUCCESS); 464 rb.Push(RESULT_SUCCESS);
450} 465}
451 466
452void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { 467void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
453 IPC::RequestParser rp{ctx}; 468 IPC::RequestParser rp{ctx};
454 const auto handle{rp.Pop<u32>()}; 469 struct Parameters {
455 const auto applet_resource_user_id{rp.Pop<u64>()}; 470 Controller_NPad::DeviceHandle sixaxis_handle{};
471 INSERT_PADDING_WORDS(1);
472 u64 applet_resource_user_id{};
473 };
456 474
457 LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, 475 const auto parameters{rp.PopRaw<Parameters>()};
458 applet_resource_user_id); 476
477 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
478
479 LOG_DEBUG(Service_HID,
480 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
481 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
482 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
459 483
460 IPC::ResponseBuilder rb{ctx, 2}; 484 IPC::ResponseBuilder rb{ctx, 2};
461 rb.Push(RESULT_SUCCESS); 485 rb.Push(RESULT_SUCCESS);
@@ -463,11 +487,20 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
463 487
464void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { 488void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
465 IPC::RequestParser rp{ctx}; 489 IPC::RequestParser rp{ctx};
466 const auto handle{rp.Pop<u32>()}; 490 struct Parameters {
467 const auto applet_resource_user_id{rp.Pop<u64>()}; 491 Controller_NPad::DeviceHandle sixaxis_handle{};
492 INSERT_PADDING_WORDS(1);
493 u64 applet_resource_user_id{};
494 };
468 495
469 LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, 496 const auto parameters{rp.PopRaw<Parameters>()};
470 applet_resource_user_id); 497
498 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
499
500 LOG_DEBUG(Service_HID,
501 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
502 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
503 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
471 504
472 IPC::ResponseBuilder rb{ctx, 2}; 505 IPC::ResponseBuilder rb{ctx, 2};
473 rb.Push(RESULT_SUCCESS); 506 rb.Push(RESULT_SUCCESS);
@@ -475,12 +508,21 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
475 508
476void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { 509void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
477 IPC::RequestParser rp{ctx}; 510 IPC::RequestParser rp{ctx};
478 [[maybe_unused]] const auto enable{rp.Pop<bool>()}; 511 struct Parameters {
479 const auto handle{rp.Pop<u32>()}; 512 bool enable_sixaxis_sensor_fusion{};
480 const auto applet_resource_user_id{rp.Pop<u64>()}; 513 INSERT_PADDING_BYTES(3);
514 Controller_NPad::DeviceHandle sixaxis_handle{};
515 u64 applet_resource_user_id{};
516 };
481 517
482 LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, 518 const auto parameters{rp.PopRaw<Parameters>()};
483 applet_resource_user_id); 519
520 LOG_WARNING(Service_HID,
521 "(STUBBED) called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
522 "device_index={}, applet_resource_user_id={}",
523 parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
524 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
525 parameters.applet_resource_user_id);
484 526
485 IPC::ResponseBuilder rb{ctx, 2}; 527 IPC::ResponseBuilder rb{ctx, 2};
486 rb.Push(RESULT_SUCCESS); 528 rb.Push(RESULT_SUCCESS);
@@ -488,14 +530,17 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
488 530
489void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 531void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
490 IPC::RequestParser rp{ctx}; 532 IPC::RequestParser rp{ctx};
491 const auto handle{rp.Pop<u32>()}; 533 const auto sixaxis_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
492 const auto drift_mode{rp.Pop<u32>()}; 534 const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()};
493 const auto applet_resource_user_id{rp.Pop<u64>()}; 535 const auto applet_resource_user_id{rp.Pop<u64>()};
494 536
495 applet_resource->GetController<Controller_NPad>(HidController::NPad) 537 applet_resource->GetController<Controller_NPad>(HidController::NPad)
496 .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode{drift_mode}); 538 .SetGyroscopeZeroDriftMode(drift_mode);
497 539
498 LOG_DEBUG(Service_HID, "called, handle={}, drift_mode={}, applet_resource_user_id={}", handle, 540 LOG_DEBUG(Service_HID,
541 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
542 "applet_resource_user_id={}",
543 sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index,
499 drift_mode, applet_resource_user_id); 544 drift_mode, applet_resource_user_id);
500 545
501 IPC::ResponseBuilder rb{ctx, 2}; 546 IPC::ResponseBuilder rb{ctx, 2};
@@ -504,29 +549,42 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
504 549
505void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 550void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
506 IPC::RequestParser rp{ctx}; 551 IPC::RequestParser rp{ctx};
507 const auto handle{rp.Pop<u32>()}; 552 struct Parameters {
508 const auto applet_resource_user_id{rp.Pop<u64>()}; 553 Controller_NPad::DeviceHandle sixaxis_handle{};
554 INSERT_PADDING_WORDS(1);
555 u64 applet_resource_user_id{};
556 };
557
558 const auto parameters{rp.PopRaw<Parameters>()};
509 559
510 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, 560 LOG_DEBUG(Service_HID,
511 applet_resource_user_id); 561 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
562 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
563 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
512 564
513 IPC::ResponseBuilder rb{ctx, 3}; 565 IPC::ResponseBuilder rb{ctx, 3};
514 rb.Push(RESULT_SUCCESS); 566 rb.Push(RESULT_SUCCESS);
515 rb.Push<u32>( 567 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
516 static_cast<u32>(applet_resource->GetController<Controller_NPad>(HidController::NPad) 568 .GetGyroscopeZeroDriftMode());
517 .GetGyroscopeZeroDriftMode()));
518} 569}
519 570
520void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 571void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
521 IPC::RequestParser rp{ctx}; 572 IPC::RequestParser rp{ctx};
522 const auto handle{rp.Pop<u32>()}; 573 struct Parameters {
523 const auto applet_resource_user_id{rp.Pop<u64>()}; 574 Controller_NPad::DeviceHandle sixaxis_handle{};
575 INSERT_PADDING_WORDS(1);
576 u64 applet_resource_user_id{};
577 };
578
579 const auto parameters{rp.PopRaw<Parameters>()};
524 580
525 applet_resource->GetController<Controller_NPad>(HidController::NPad) 581 applet_resource->GetController<Controller_NPad>(HidController::NPad)
526 .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard); 582 .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard);
527 583
528 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, 584 LOG_DEBUG(Service_HID,
529 applet_resource_user_id); 585 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
586 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
587 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
530 588
531 IPC::ResponseBuilder rb{ctx, 2}; 589 IPC::ResponseBuilder rb{ctx, 2};
532 rb.Push(RESULT_SUCCESS); 590 rb.Push(RESULT_SUCCESS);
@@ -534,11 +592,18 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
534 592
535void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { 593void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
536 IPC::RequestParser rp{ctx}; 594 IPC::RequestParser rp{ctx};
537 const auto handle{rp.Pop<u32>()}; 595 struct Parameters {
538 const auto applet_resource_user_id{rp.Pop<u64>()}; 596 Controller_NPad::DeviceHandle sixaxis_handle{};
597 INSERT_PADDING_WORDS(1);
598 u64 applet_resource_user_id{};
599 };
600
601 const auto parameters{rp.PopRaw<Parameters>()};
539 602
540 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, 603 LOG_DEBUG(Service_HID,
541 applet_resource_user_id); 604 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
605 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
606 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
542 607
543 IPC::ResponseBuilder rb{ctx, 3}; 608 IPC::ResponseBuilder rb{ctx, 3};
544 rb.Push(RESULT_SUCCESS); 609 rb.Push(RESULT_SUCCESS);
@@ -546,15 +611,34 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
546 .IsSixAxisSensorAtRest()); 611 .IsSixAxisSensorAtRest());
547} 612}
548 613
614void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
615 IPC::RequestParser rp{ctx};
616 struct Parameters {
617 u32 unknown{};
618 INSERT_PADDING_WORDS(1);
619 u64 applet_resource_user_id{};
620 };
621
622 const auto parameters{rp.PopRaw<Parameters>()};
623
624 applet_resource->ActivateController(HidController::Gesture);
625
626 LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown,
627 parameters.applet_resource_user_id);
628
629 IPC::ResponseBuilder rb{ctx, 2};
630 rb.Push(RESULT_SUCCESS);
631}
632
549void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 633void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
550 IPC::RequestParser rp{ctx}; 634 IPC::RequestParser rp{ctx};
551 const auto supported_styleset{rp.Pop<u32>()}; 635 const auto supported_styleset{rp.Pop<u32>()};
552 636
553 LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset);
554
555 applet_resource->GetController<Controller_NPad>(HidController::NPad) 637 applet_resource->GetController<Controller_NPad>(HidController::NPad)
556 .SetSupportedStyleSet({supported_styleset}); 638 .SetSupportedStyleSet({supported_styleset});
557 639
640 LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset);
641
558 IPC::ResponseBuilder rb{ctx, 2}; 642 IPC::ResponseBuilder rb{ctx, 2};
559 rb.Push(RESULT_SUCCESS); 643 rb.Push(RESULT_SUCCESS);
560} 644}
@@ -565,21 +649,22 @@ void Hid::GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
565 649
566 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 650 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
567 651
568 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
569
570 IPC::ResponseBuilder rb{ctx, 3}; 652 IPC::ResponseBuilder rb{ctx, 3};
571 rb.Push(RESULT_SUCCESS); 653 rb.Push(RESULT_SUCCESS);
572 rb.Push<u32>(controller.GetSupportedStyleSet().raw); 654 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
655 .GetSupportedStyleSet()
656 .raw);
573} 657}
574 658
575void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { 659void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
576 IPC::RequestParser rp{ctx}; 660 IPC::RequestParser rp{ctx};
577 const auto applet_resource_user_id{rp.Pop<u64>()}; 661 const auto applet_resource_user_id{rp.Pop<u64>()};
578 662
663 applet_resource->GetController<Controller_NPad>(HidController::NPad)
664 .SetSupportedNpadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
665
579 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 666 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
580 667
581 applet_resource->GetController<Controller_NPad>(HidController::NPad)
582 .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
583 IPC::ResponseBuilder rb{ctx, 2}; 668 IPC::ResponseBuilder rb{ctx, 2};
584 rb.Push(RESULT_SUCCESS); 669 rb.Push(RESULT_SUCCESS);
585} 670}
@@ -588,48 +673,62 @@ void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) {
588 IPC::RequestParser rp{ctx}; 673 IPC::RequestParser rp{ctx};
589 const auto applet_resource_user_id{rp.Pop<u64>()}; 674 const auto applet_resource_user_id{rp.Pop<u64>()};
590 675
676 applet_resource->ActivateController(HidController::NPad);
677
591 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 678 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
592 679
593 IPC::ResponseBuilder rb{ctx, 2}; 680 IPC::ResponseBuilder rb{ctx, 2};
594 rb.Push(RESULT_SUCCESS); 681 rb.Push(RESULT_SUCCESS);
595 applet_resource->ActivateController(HidController::NPad);
596} 682}
597 683
598void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) { 684void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
599 IPC::RequestParser rp{ctx}; 685 IPC::RequestParser rp{ctx};
600 const auto applet_resource_user_id{rp.Pop<u64>()}; 686 const auto applet_resource_user_id{rp.Pop<u64>()};
601 687
688 applet_resource->DeactivateController(HidController::NPad);
689
602 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 690 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
603 691
604 IPC::ResponseBuilder rb{ctx, 2}; 692 IPC::ResponseBuilder rb{ctx, 2};
605 rb.Push(RESULT_SUCCESS); 693 rb.Push(RESULT_SUCCESS);
606 applet_resource->DeactivateController(HidController::NPad);
607} 694}
608 695
609void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { 696void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
610 IPC::RequestParser rp{ctx}; 697 IPC::RequestParser rp{ctx};
611 const auto npad_id{rp.Pop<u32>()}; 698 struct Parameters {
612 const auto applet_resource_user_id{rp.Pop<u64>()}; 699 u32 npad_id{};
613 const auto unknown{rp.Pop<u64>()}; 700 INSERT_PADDING_WORDS(1);
701 u64 applet_resource_user_id{};
702 u64 unknown{};
703 };
614 704
615 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", npad_id, 705 const auto parameters{rp.PopRaw<Parameters>()};
616 applet_resource_user_id, unknown); 706
707 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
708 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
617 709
618 IPC::ResponseBuilder rb{ctx, 2, 1}; 710 IPC::ResponseBuilder rb{ctx, 2, 1};
619 rb.Push(RESULT_SUCCESS); 711 rb.Push(RESULT_SUCCESS);
620 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) 712 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
621 .GetStyleSetChangedEvent(npad_id)); 713 .GetStyleSetChangedEvent(parameters.npad_id));
622} 714}
623 715
624void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { 716void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
625 IPC::RequestParser rp{ctx}; 717 IPC::RequestParser rp{ctx};
626 const auto npad_id{rp.Pop<u32>()}; 718 struct Parameters {
627 const auto applet_resource_user_id{rp.Pop<u64>()}; 719 u32 npad_id{};
720 INSERT_PADDING_WORDS(1);
721 u64 applet_resource_user_id{};
722 };
628 723
629 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, 724 const auto parameters{rp.PopRaw<Parameters>()};
630 applet_resource_user_id); 725
726 applet_resource->GetController<Controller_NPad>(HidController::NPad)
727 .DisconnectNpad(parameters.npad_id);
728
729 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
730 parameters.applet_resource_user_id);
631 731
632 applet_resource->GetController<Controller_NPad>(HidController::NPad).DisconnectNPad(npad_id);
633 IPC::ResponseBuilder rb{ctx, 2}; 732 IPC::ResponseBuilder rb{ctx, 2};
634 rb.Push(RESULT_SUCCESS); 733 rb.Push(RESULT_SUCCESS);
635} 734}
@@ -642,22 +741,41 @@ void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
642 741
643 IPC::ResponseBuilder rb{ctx, 4}; 742 IPC::ResponseBuilder rb{ctx, 4};
644 rb.Push(RESULT_SUCCESS); 743 rb.Push(RESULT_SUCCESS);
645 rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad) 744 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
646 .GetLedPattern(npad_id) 745 .GetLedPattern(npad_id)
647 .raw); 746 .raw);
747}
748
749void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
750 // Should have no effect with how our npad sets up the data
751 IPC::RequestParser rp{ctx};
752 struct Parameters {
753 u32 unknown{};
754 INSERT_PADDING_WORDS(1);
755 u64 applet_resource_user_id{};
756 };
757
758 const auto parameters{rp.PopRaw<Parameters>()};
759
760 applet_resource->ActivateController(HidController::NPad);
761
762 LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown,
763 parameters.applet_resource_user_id);
764
765 IPC::ResponseBuilder rb{ctx, 2};
766 rb.Push(RESULT_SUCCESS);
648} 767}
649 768
650void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { 769void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
651 IPC::RequestParser rp{ctx}; 770 IPC::RequestParser rp{ctx};
652 const auto applet_resource_user_id{rp.Pop<u64>()}; 771 const auto applet_resource_user_id{rp.Pop<u64>()};
653 const auto hold_type{rp.Pop<u64>()}; 772 const auto hold_type{rp.PopEnum<Controller_NPad::NpadHoldType>()};
773
774 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type);
654 775
655 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", 776 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}",
656 applet_resource_user_id, hold_type); 777 applet_resource_user_id, hold_type);
657 778
658 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
659 controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
660
661 IPC::ResponseBuilder rb{ctx, 2}; 779 IPC::ResponseBuilder rb{ctx, 2};
662 rb.Push(RESULT_SUCCESS); 780 rb.Push(RESULT_SUCCESS);
663} 781}
@@ -668,22 +786,26 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
668 786
669 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 787 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
670 788
671 const auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
672 IPC::ResponseBuilder rb{ctx, 4}; 789 IPC::ResponseBuilder rb{ctx, 4};
673 rb.Push(RESULT_SUCCESS); 790 rb.Push(RESULT_SUCCESS);
674 rb.Push<u64>(static_cast<u64>(controller.GetHoldType())); 791 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad).GetHoldType());
675} 792}
676 793
677void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { 794void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
678 IPC::RequestParser rp{ctx}; 795 IPC::RequestParser rp{ctx};
679 const auto npad_id{rp.Pop<u32>()}; 796 struct Parameters {
680 const auto applet_resource_user_id{rp.Pop<u64>()}; 797 u32 npad_id{};
798 INSERT_PADDING_WORDS(1);
799 u64 applet_resource_user_id{};
800 };
681 801
682 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id, 802 const auto parameters{rp.PopRaw<Parameters>()};
683 applet_resource_user_id); 803
804 applet_resource->GetController<Controller_NPad>(HidController::NPad)
805 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single);
684 806
685 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); 807 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
686 controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single); 808 parameters.npad_id, parameters.applet_resource_user_id);
687 809
688 IPC::ResponseBuilder rb{ctx, 2}; 810 IPC::ResponseBuilder rb{ctx, 2};
689 rb.Push(RESULT_SUCCESS); 811 rb.Push(RESULT_SUCCESS);
@@ -692,16 +814,22 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
692void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { 814void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
693 // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault 815 // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
694 IPC::RequestParser rp{ctx}; 816 IPC::RequestParser rp{ctx};
695 const auto npad_id{rp.Pop<u32>()}; 817 struct Parameters {
696 const auto applet_resource_user_id{rp.Pop<u64>()}; 818 u32 npad_id{};
697 const auto npad_joy_device_type{rp.Pop<u64>()}; 819 INSERT_PADDING_WORDS(1);
820 u64 applet_resource_user_id{};
821 u64 npad_joy_device_type{};
822 };
823
824 const auto parameters{rp.PopRaw<Parameters>()};
825
826 applet_resource->GetController<Controller_NPad>(HidController::NPad)
827 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single);
698 828
699 LOG_WARNING(Service_HID, 829 LOG_WARNING(Service_HID,
700 "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", 830 "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
701 npad_id, applet_resource_user_id, npad_joy_device_type); 831 parameters.npad_id, parameters.applet_resource_user_id,
702 832 parameters.npad_joy_device_type);
703 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
704 controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single);
705 833
706 IPC::ResponseBuilder rb{ctx, 2}; 834 IPC::ResponseBuilder rb{ctx, 2};
707 rb.Push(RESULT_SUCCESS); 835 rb.Push(RESULT_SUCCESS);
@@ -709,14 +837,19 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
709 837
710void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { 838void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
711 IPC::RequestParser rp{ctx}; 839 IPC::RequestParser rp{ctx};
712 const auto npad_id{rp.Pop<u32>()}; 840 struct Parameters {
713 const auto applet_resource_user_id{rp.Pop<u64>()}; 841 u32 npad_id{};
842 INSERT_PADDING_WORDS(1);
843 u64 applet_resource_user_id{};
844 };
845
846 const auto parameters{rp.PopRaw<Parameters>()};
714 847
715 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, 848 applet_resource->GetController<Controller_NPad>(HidController::NPad)
716 applet_resource_user_id); 849 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Dual);
717 850
718 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); 851 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
719 controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual); 852 parameters.npad_id, parameters.applet_resource_user_id);
720 853
721 IPC::ResponseBuilder rb{ctx, 2}; 854 IPC::ResponseBuilder rb{ctx, 2};
722 rb.Push(RESULT_SUCCESS); 855 rb.Push(RESULT_SUCCESS);
@@ -728,12 +861,12 @@ void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
728 const auto npad_id_2{rp.Pop<u32>()}; 861 const auto npad_id_2{rp.Pop<u32>()};
729 const auto applet_resource_user_id{rp.Pop<u64>()}; 862 const auto applet_resource_user_id{rp.Pop<u64>()};
730 863
864 applet_resource->GetController<Controller_NPad>(HidController::NPad)
865 .MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
866
731 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", 867 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
732 npad_id_1, npad_id_2, applet_resource_user_id); 868 npad_id_1, npad_id_2, applet_resource_user_id);
733 869
734 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
735 controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
736
737 IPC::ResponseBuilder rb{ctx, 2}; 870 IPC::ResponseBuilder rb{ctx, 2};
738 rb.Push(RESULT_SUCCESS); 871 rb.Push(RESULT_SUCCESS);
739} 872}
@@ -742,9 +875,9 @@ void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
742 IPC::RequestParser rp{ctx}; 875 IPC::RequestParser rp{ctx};
743 const auto applet_resource_user_id{rp.Pop<u64>()}; 876 const auto applet_resource_user_id{rp.Pop<u64>()};
744 877
878 applet_resource->GetController<Controller_NPad>(HidController::NPad).StartLRAssignmentMode();
879
745 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 880 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
746 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
747 controller.StartLRAssignmentMode();
748 881
749 IPC::ResponseBuilder rb{ctx, 2}; 882 IPC::ResponseBuilder rb{ctx, 2};
750 rb.Push(RESULT_SUCCESS); 883 rb.Push(RESULT_SUCCESS);
@@ -754,9 +887,9 @@ void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) {
754 IPC::RequestParser rp{ctx}; 887 IPC::RequestParser rp{ctx};
755 const auto applet_resource_user_id{rp.Pop<u64>()}; 888 const auto applet_resource_user_id{rp.Pop<u64>()};
756 889
890 applet_resource->GetController<Controller_NPad>(HidController::NPad).StopLRAssignmentMode();
891
757 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 892 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
758 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
759 controller.StopLRAssignmentMode();
760 893
761 IPC::ResponseBuilder rb{ctx, 2}; 894 IPC::ResponseBuilder rb{ctx, 2};
762 rb.Push(RESULT_SUCCESS); 895 rb.Push(RESULT_SUCCESS);
@@ -765,13 +898,13 @@ void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) {
765void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { 898void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
766 IPC::RequestParser rp{ctx}; 899 IPC::RequestParser rp{ctx};
767 const auto applet_resource_user_id{rp.Pop<u64>()}; 900 const auto applet_resource_user_id{rp.Pop<u64>()};
768 const auto mode{rp.Pop<u64>()}; 901 const auto activation_mode{rp.PopEnum<Controller_NPad::NpadHandheldActivationMode>()};
769
770 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, mode={}", applet_resource_user_id,
771 mode);
772 902
773 applet_resource->GetController<Controller_NPad>(HidController::NPad) 903 applet_resource->GetController<Controller_NPad>(HidController::NPad)
774 .SetNpadHandheldActivationMode(Controller_NPad::NpadHandheldActivationMode{mode}); 904 .SetNpadHandheldActivationMode(activation_mode);
905
906 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}",
907 applet_resource_user_id, activation_mode);
775 908
776 IPC::ResponseBuilder rb{ctx, 2}; 909 IPC::ResponseBuilder rb{ctx, 2};
777 rb.Push(RESULT_SUCCESS); 910 rb.Push(RESULT_SUCCESS);
@@ -785,23 +918,24 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
785 918
786 IPC::ResponseBuilder rb{ctx, 4}; 919 IPC::ResponseBuilder rb{ctx, 4};
787 rb.Push(RESULT_SUCCESS); 920 rb.Push(RESULT_SUCCESS);
788 rb.Push<u64>( 921 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
789 static_cast<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad) 922 .GetNpadHandheldActivationMode());
790 .GetNpadHandheldActivationMode()));
791} 923}
792 924
793void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { 925void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
794 IPC::RequestParser rp{ctx}; 926 IPC::RequestParser rp{ctx};
795 const auto npad_1{rp.Pop<u32>()}; 927 const auto npad_id_1{rp.Pop<u32>()};
796 const auto npad_2{rp.Pop<u32>()}; 928 const auto npad_id_2{rp.Pop<u32>()};
797 const auto applet_resource_user_id{rp.Pop<u64>()}; 929 const auto applet_resource_user_id{rp.Pop<u64>()};
798 930
799 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_1={}, npad_2={}", 931 const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad)
800 applet_resource_user_id, npad_1, npad_2); 932 .SwapNpadAssignment(npad_id_1, npad_id_2);
933
934 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
935 npad_id_1, npad_id_2, applet_resource_user_id);
801 936
802 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
803 IPC::ResponseBuilder rb{ctx, 2}; 937 IPC::ResponseBuilder rb{ctx, 2};
804 if (controller.SwapNpadAssignment(npad_1, npad_2)) { 938 if (res) {
805 rb.Push(RESULT_SUCCESS); 939 rb.Push(RESULT_SUCCESS);
806 } else { 940 } else {
807 LOG_ERROR(Service_HID, "Npads are not connected!"); 941 LOG_ERROR(Service_HID, "Npads are not connected!");
@@ -811,144 +945,219 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
811 945
812void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { 946void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
813 IPC::RequestParser rp{ctx}; 947 IPC::RequestParser rp{ctx};
814 const auto npad_id{rp.Pop<u32>()}; 948 struct Parameters {
815 const auto applet_resource_user_id{rp.Pop<u64>()}; 949 u32 npad_id{};
950 INSERT_PADDING_WORDS(1);
951 u64 applet_resource_user_id{};
952 };
816 953
817 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id, 954 const auto parameters{rp.PopRaw<Parameters>()};
818 applet_resource_user_id);
819 955
820 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); 956 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
957 parameters.npad_id, parameters.applet_resource_user_id);
821 958
822 IPC::ResponseBuilder rb{ctx, 3}; 959 IPC::ResponseBuilder rb{ctx, 3};
823 rb.Push(RESULT_SUCCESS); 960 rb.Push(RESULT_SUCCESS);
824 rb.Push<bool>(controller.IsUnintendedHomeButtonInputProtectionEnabled(npad_id)); 961 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
962 .IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id));
825} 963}
826 964
827void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) { 965void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) {
828 IPC::RequestParser rp{ctx}; 966 IPC::RequestParser rp{ctx};
829 const auto unintended_home_button_input_protection{rp.Pop<bool>()}; 967 struct Parameters {
830 const auto npad_id{rp.Pop<u32>()}; 968 bool unintended_home_button_input_protection{};
831 const auto applet_resource_user_id{rp.Pop<u64>()}; 969 INSERT_PADDING_BYTES(3);
970 u32 npad_id{};
971 u64 applet_resource_user_id{};
972 };
973
974 const auto parameters{rp.PopRaw<Parameters>()};
975
976 applet_resource->GetController<Controller_NPad>(HidController::NPad)
977 .SetUnintendedHomeButtonInputProtectionEnabled(
978 parameters.unintended_home_button_input_protection, parameters.npad_id);
832 979
833 LOG_WARNING(Service_HID, 980 LOG_WARNING(Service_HID,
834 "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={}," 981 "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={},"
835 "applet_resource_user_id={}", 982 "applet_resource_user_id={}",
836 npad_id, unintended_home_button_input_protection, applet_resource_user_id); 983 parameters.unintended_home_button_input_protection, parameters.npad_id,
837 984 parameters.applet_resource_user_id);
838 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
839 controller.SetUnintendedHomeButtonInputProtectionEnabled(
840 unintended_home_button_input_protection, npad_id);
841 985
842 IPC::ResponseBuilder rb{ctx, 2}; 986 IPC::ResponseBuilder rb{ctx, 2};
843 rb.Push(RESULT_SUCCESS); 987 rb.Push(RESULT_SUCCESS);
844} 988}
845 989
846void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { 990void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
847 IPC::RequestParser rp{ctx}; 991 IPC::RequestParser rp{ctx};
848 const auto applet_resource_user_id{rp.Pop<u64>()}; 992 const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
993
994 VibrationDeviceInfo vibration_device_info;
995
996 vibration_device_info.type = VibrationDeviceType::LinearResonantActuator;
997
998 switch (vibration_device_handle.device_index) {
999 case Controller_NPad::DeviceIndex::Left:
1000 vibration_device_info.position = VibrationDevicePosition::Left;
1001 break;
1002 case Controller_NPad::DeviceIndex::Right:
1003 vibration_device_info.position = VibrationDevicePosition::Right;
1004 break;
1005 case Controller_NPad::DeviceIndex::None:
1006 default:
1007 UNREACHABLE_MSG("DeviceIndex should never be None!");
1008 vibration_device_info.position = VibrationDevicePosition::None;
1009 break;
1010 }
849 1011
850 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1012 LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
1013 vibration_device_info.type, vibration_device_info.position);
1014
1015 IPC::ResponseBuilder rb{ctx, 4};
1016 rb.Push(RESULT_SUCCESS);
1017 rb.PushRaw(vibration_device_info);
1018}
1019
1020void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
1021 IPC::RequestParser rp{ctx};
1022 struct Parameters {
1023 Controller_NPad::DeviceHandle vibration_device_handle{};
1024 Controller_NPad::VibrationValue vibration_value{};
1025 INSERT_PADDING_WORDS(1);
1026 u64 applet_resource_user_id{};
1027 };
1028
1029 const auto parameters{rp.PopRaw<Parameters>()};
1030
1031 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1032 .VibrateController(parameters.vibration_device_handle, parameters.vibration_value);
1033
1034 LOG_DEBUG(Service_HID,
1035 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1036 parameters.vibration_device_handle.npad_type,
1037 parameters.vibration_device_handle.npad_id,
1038 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
851 1039
852 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetVibrationEnabled(true);
853 IPC::ResponseBuilder rb{ctx, 2}; 1040 IPC::ResponseBuilder rb{ctx, 2};
854 rb.Push(RESULT_SUCCESS); 1041 rb.Push(RESULT_SUCCESS);
855} 1042}
856 1043
857void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { 1044void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
1045 IPC::RequestParser rp{ctx};
1046 struct Parameters {
1047 Controller_NPad::DeviceHandle vibration_device_handle{};
1048 INSERT_PADDING_WORDS(1);
1049 u64 applet_resource_user_id{};
1050 };
1051
1052 const auto parameters{rp.PopRaw<Parameters>()};
1053
1054 LOG_DEBUG(Service_HID,
1055 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1056 parameters.vibration_device_handle.npad_type,
1057 parameters.vibration_device_handle.npad_id,
1058 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1059
1060 IPC::ResponseBuilder rb{ctx, 6};
1061 rb.Push(RESULT_SUCCESS);
1062 rb.PushRaw(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1063 .GetLastVibration(parameters.vibration_device_handle));
1064}
1065
1066void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
858 LOG_DEBUG(Service_HID, "called"); 1067 LOG_DEBUG(Service_HID, "called");
859 1068
860 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetVibrationEnabled(false); 1069 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
861 IPC::ResponseBuilder rb{ctx, 2};
862 rb.Push(RESULT_SUCCESS); 1070 rb.Push(RESULT_SUCCESS);
1071 rb.PushIpcInterface<IActiveVibrationDeviceList>(applet_resource);
863} 1072}
864 1073
865void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { 1074void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
866 IPC::RequestParser rp{ctx}; 1075 IPC::RequestParser rp{ctx};
867 const auto controller{rp.Pop<u32>()}; 1076 const auto can_vibrate{rp.Pop<bool>()};
868 const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()};
869 const auto applet_resource_user_id{rp.Pop<u64>()};
870 1077
871 LOG_DEBUG(Service_HID, "called, controller={}, applet_resource_user_id={}", controller, 1078 Settings::values.vibration_enabled.SetValue(can_vibrate);
872 applet_resource_user_id); 1079
1080 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
873 1081
874 IPC::ResponseBuilder rb{ctx, 2}; 1082 IPC::ResponseBuilder rb{ctx, 2};
875 rb.Push(RESULT_SUCCESS); 1083 rb.Push(RESULT_SUCCESS);
1084}
876 1085
877 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1086void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
878 .VibrateController({controller}, {vibration_values}); 1087 LOG_DEBUG(Service_HID, "called");
1088
1089 IPC::ResponseBuilder rb{ctx, 3};
1090 rb.Push(RESULT_SUCCESS);
1091 rb.Push(Settings::values.vibration_enabled.GetValue());
879} 1092}
880 1093
881void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { 1094void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
882 IPC::RequestParser rp{ctx}; 1095 IPC::RequestParser rp{ctx};
883 const auto applet_resource_user_id{rp.Pop<u64>()}; 1096 const auto applet_resource_user_id{rp.Pop<u64>()};
884 1097
885 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1098 const auto handles = ctx.ReadBuffer(0);
886
887 const auto controllers = ctx.ReadBuffer(0);
888 const auto vibrations = ctx.ReadBuffer(1); 1099 const auto vibrations = ctx.ReadBuffer(1);
889 1100
890 std::vector<u32> controller_list(controllers.size() / sizeof(u32)); 1101 std::vector<Controller_NPad::DeviceHandle> vibration_device_handles(
891 std::vector<Controller_NPad::Vibration> vibration_list(vibrations.size() / 1102 handles.size() / sizeof(Controller_NPad::DeviceHandle));
892 sizeof(Controller_NPad::Vibration)); 1103 std::vector<Controller_NPad::VibrationValue> vibration_values(
1104 vibrations.size() / sizeof(Controller_NPad::VibrationValue));
893 1105
894 std::memcpy(controller_list.data(), controllers.data(), controllers.size()); 1106 std::memcpy(vibration_device_handles.data(), handles.data(), handles.size());
895 std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size()); 1107 std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size());
896 1108
897 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1109 applet_resource->GetController<Controller_NPad>(HidController::NPad)
898 .VibrateController(controller_list, vibration_list); 1110 .VibrateControllers(vibration_device_handles, vibration_values);
1111
1112 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
899 1113
900 IPC::ResponseBuilder rb{ctx, 2}; 1114 IPC::ResponseBuilder rb{ctx, 2};
901 rb.Push(RESULT_SUCCESS); 1115 rb.Push(RESULT_SUCCESS);
902} 1116}
903 1117
904void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) { 1118void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
905 IPC::RequestParser rp{ctx}; 1119 IPC::RequestParser rp{ctx};
906 const auto controller_id{rp.Pop<u32>()};
907 const auto applet_resource_user_id{rp.Pop<u64>()}; 1120 const auto applet_resource_user_id{rp.Pop<u64>()};
908 1121
909 LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id, 1122 applet_resource->GetController<Controller_NPad>(HidController::NPad)
910 applet_resource_user_id); 1123 .SetPermitVibrationSession(true);
911
912 IPC::ResponseBuilder rb{ctx, 6};
913 rb.Push(RESULT_SUCCESS);
914 rb.PushRaw<Controller_NPad::Vibration>(
915 applet_resource->GetController<Controller_NPad>(HidController::NPad).GetLastVibration());
916}
917 1124
918void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { 1125 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
919 LOG_DEBUG(Service_HID, "called");
920 1126
921 IPC::ResponseBuilder rb{ctx, 4}; 1127 IPC::ResponseBuilder rb{ctx, 2};
922 rb.Push(RESULT_SUCCESS); 1128 rb.Push(RESULT_SUCCESS);
923 rb.Push<u32>(1);
924 rb.Push<u32>(0);
925} 1129}
926 1130
927void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { 1131void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
1132 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1133 .SetPermitVibrationSession(false);
1134
928 LOG_DEBUG(Service_HID, "called"); 1135 LOG_DEBUG(Service_HID, "called");
929 1136
930 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1137 IPC::ResponseBuilder rb{ctx, 2};
931 rb.Push(RESULT_SUCCESS); 1138 rb.Push(RESULT_SUCCESS);
932 rb.PushIpcInterface<IActiveVibrationDeviceList>();
933} 1139}
934 1140
935void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { 1141void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) {
936 IPC::RequestParser rp{ctx}; 1142 IPC::RequestParser rp{ctx};
937 const auto can_vibrate{rp.Pop<bool>()}; 1143 struct Parameters {
938 Settings::values.vibration_enabled = can_vibrate; 1144 Controller_NPad::DeviceHandle vibration_device_handle{};
1145 INSERT_PADDING_WORDS(1);
1146 u64 applet_resource_user_id{};
1147 };
939 1148
940 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); 1149 const auto parameters{rp.PopRaw<Parameters>()};
941 1150
942 IPC::ResponseBuilder rb{ctx, 2}; 1151 LOG_DEBUG(Service_HID,
943 rb.Push(RESULT_SUCCESS); 1152 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
944} 1153 parameters.vibration_device_handle.npad_type,
945 1154 parameters.vibration_device_handle.npad_id,
946void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) { 1155 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
947 LOG_DEBUG(Service_HID, "called");
948 1156
949 IPC::ResponseBuilder rb{ctx, 3}; 1157 IPC::ResponseBuilder rb{ctx, 3};
950 rb.Push(RESULT_SUCCESS); 1158 rb.Push(RESULT_SUCCESS);
951 rb.Push(Settings::values.vibration_enabled); 1159 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1160 .IsVibrationDeviceMounted(parameters.vibration_device_handle));
952} 1161}
953 1162
954void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 1163void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
@@ -964,11 +1173,19 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
964 1173
965void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 1174void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
966 IPC::RequestParser rp{ctx}; 1175 IPC::RequestParser rp{ctx};
967 const auto handle{rp.Pop<u32>()}; 1176 struct Parameters {
968 const auto applet_resource_user_id{rp.Pop<u64>()}; 1177 Controller_NPad::DeviceHandle sixaxis_handle{};
1178 INSERT_PADDING_WORDS(1);
1179 u64 applet_resource_user_id{};
1180 };
969 1181
970 LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, 1182 const auto parameters{rp.PopRaw<Parameters>()};
971 applet_resource_user_id); 1183
1184 LOG_WARNING(
1185 Service_HID,
1186 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1187 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
1188 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
972 1189
973 IPC::ResponseBuilder rb{ctx, 2}; 1190 IPC::ResponseBuilder rb{ctx, 2};
974 rb.Push(RESULT_SUCCESS); 1191 rb.Push(RESULT_SUCCESS);
@@ -976,11 +1193,19 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
976 1193
977void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 1194void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
978 IPC::RequestParser rp{ctx}; 1195 IPC::RequestParser rp{ctx};
979 const auto handle{rp.Pop<u32>()}; 1196 struct Parameters {
980 const auto applet_resource_user_id{rp.Pop<u64>()}; 1197 Controller_NPad::DeviceHandle sixaxis_handle{};
1198 INSERT_PADDING_WORDS(1);
1199 u64 applet_resource_user_id{};
1200 };
981 1201
982 LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, 1202 const auto parameters{rp.PopRaw<Parameters>()};
983 applet_resource_user_id); 1203
1204 LOG_WARNING(
1205 Service_HID,
1206 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1207 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
1208 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
984 1209
985 IPC::ResponseBuilder rb{ctx, 2}; 1210 IPC::ResponseBuilder rb{ctx, 2};
986 rb.Push(RESULT_SUCCESS); 1211 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index fd0372b18..c8e4a4b55 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -86,17 +86,15 @@ public:
86 86
87private: 87private:
88 void CreateAppletResource(Kernel::HLERequestContext& ctx); 88 void CreateAppletResource(Kernel::HLERequestContext& ctx);
89 void ActivateXpad(Kernel::HLERequestContext& ctx);
90 void GetXpadIDs(Kernel::HLERequestContext& ctx);
91 void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
92 void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
93 void ActivateDebugPad(Kernel::HLERequestContext& ctx); 89 void ActivateDebugPad(Kernel::HLERequestContext& ctx);
94 void ActivateTouchScreen(Kernel::HLERequestContext& ctx); 90 void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
95 void ActivateMouse(Kernel::HLERequestContext& ctx); 91 void ActivateMouse(Kernel::HLERequestContext& ctx);
96 void ActivateKeyboard(Kernel::HLERequestContext& ctx); 92 void ActivateKeyboard(Kernel::HLERequestContext& ctx);
97 void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx); 93 void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx);
98 void ActivateGesture(Kernel::HLERequestContext& ctx); 94 void ActivateXpad(Kernel::HLERequestContext& ctx);
99 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); 95 void GetXpadIDs(Kernel::HLERequestContext& ctx);
96 void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
97 void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
100 void StartSixAxisSensor(Kernel::HLERequestContext& ctx); 98 void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
101 void StopSixAxisSensor(Kernel::HLERequestContext& ctx); 99 void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
102 void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx); 100 void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
@@ -104,6 +102,7 @@ private:
104 void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); 102 void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
105 void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); 103 void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
106 void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); 104 void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
105 void ActivateGesture(Kernel::HLERequestContext& ctx);
107 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); 106 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
108 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); 107 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
109 void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx); 108 void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
@@ -112,6 +111,7 @@ private:
112 void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx); 111 void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
113 void DisconnectNpad(Kernel::HLERequestContext& ctx); 112 void DisconnectNpad(Kernel::HLERequestContext& ctx);
114 void GetPlayerLedPattern(Kernel::HLERequestContext& ctx); 113 void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);
114 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
115 void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx); 115 void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
116 void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx); 116 void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
117 void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx); 117 void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx);
@@ -125,15 +125,16 @@ private:
125 void SwapNpadAssignment(Kernel::HLERequestContext& ctx); 125 void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
126 void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx); 126 void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx);
127 void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx); 127 void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx);
128 void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx); 128 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
129 void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
130 void SendVibrationValue(Kernel::HLERequestContext& ctx); 129 void SendVibrationValue(Kernel::HLERequestContext& ctx);
131 void SendVibrationValues(Kernel::HLERequestContext& ctx);
132 void GetActualVibrationValue(Kernel::HLERequestContext& ctx); 130 void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
133 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
134 void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx); 131 void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
135 void PermitVibration(Kernel::HLERequestContext& ctx); 132 void PermitVibration(Kernel::HLERequestContext& ctx);
136 void IsVibrationPermitted(Kernel::HLERequestContext& ctx); 133 void IsVibrationPermitted(Kernel::HLERequestContext& ctx);
134 void SendVibrationValues(Kernel::HLERequestContext& ctx);
135 void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
136 void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
137 void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx);
137 void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); 138 void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
138 void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); 139 void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
139 void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); 140 void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
@@ -146,6 +147,22 @@ private:
146 void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); 147 void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
147 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); 148 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
148 149
150 enum class VibrationDeviceType : u32 {
151 LinearResonantActuator = 1,
152 };
153
154 enum class VibrationDevicePosition : u32 {
155 None = 0,
156 Left = 1,
157 Right = 2,
158 };
159
160 struct VibrationDeviceInfo {
161 VibrationDeviceType type{};
162 VibrationDevicePosition position{};
163 };
164 static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
165
149 std::shared_ptr<IAppletResource> applet_resource; 166 std::shared_ptr<IAppletResource> applet_resource;
150 Core::System& system; 167 Core::System& system;
151}; 168};
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index eeaca44b6..65c209725 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -9,6 +9,7 @@
9#include "common/alignment.h" 9#include "common/alignment.h"
10#include "common/hex_util.h" 10#include "common/hex_util.h"
11#include "common/scope_exit.h" 11#include "common/scope_exit.h"
12#include "core/core.h"
12#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/errors.h" 14#include "core/hle/kernel/errors.h"
14#include "core/hle/kernel/memory/page_table.h" 15#include "core/hle/kernel/memory/page_table.h"
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index dec96b771..49a42a9c9 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -7,6 +7,7 @@
7 7
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/scope_exit.h" 9#include "common/scope_exit.h"
10#include "core/core.h"
10#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
11#include "core/hle/service/lm/lm.h" 12#include "core/hle/service/lm/lm.h"
12#include "core/hle/service/lm/manager.h" 13#include "core/hle/service/lm/manager.h"
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 58ee1f712..2594e6839 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.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 "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/core.h"
6#include "core/file_sys/control_metadata.h" 7#include "core/file_sys/control_metadata.h"
7#include "core/file_sys/patch_manager.h" 8#include "core/file_sys/patch_manager.h"
8#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
@@ -29,8 +30,8 @@ IAccountProxyInterface::IAccountProxyInterface() : ServiceFramework{"IAccountPro
29 30
30IAccountProxyInterface::~IAccountProxyInterface() = default; 31IAccountProxyInterface::~IAccountProxyInterface() = default;
31 32
32IApplicationManagerInterface::IApplicationManagerInterface() 33IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_)
33 : ServiceFramework{"IApplicationManagerInterface"} { 34 : ServiceFramework{"IApplicationManagerInterface"}, system{system_} {
34 // clang-format off 35 // clang-format off
35 static const FunctionInfo functions[] = { 36 static const FunctionInfo functions[] = {
36 {0, nullptr, "ListApplicationRecord"}, 37 {0, nullptr, "ListApplicationRecord"},
@@ -298,7 +299,8 @@ void IApplicationManagerInterface::GetApplicationControlData(Kernel::HLERequestC
298 299
299 const auto size = ctx.GetWriteBufferSize(); 300 const auto size = ctx.GetWriteBufferSize();
300 301
301 const FileSys::PatchManager pm{title_id}; 302 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
303 system.GetContentProvider()};
302 const auto control = pm.GetControlMetadata(); 304 const auto control = pm.GetControlMetadata();
303 305
304 std::vector<u8> out; 306 std::vector<u8> out;
@@ -538,14 +540,14 @@ IFactoryResetInterface::IFactoryResetInterface::IFactoryResetInterface()
538 540
539IFactoryResetInterface::~IFactoryResetInterface() = default; 541IFactoryResetInterface::~IFactoryResetInterface() = default;
540 542
541NS::NS(const char* name) : ServiceFramework{name} { 543NS::NS(const char* name, Core::System& system_) : ServiceFramework{name}, system{system_} {
542 // clang-format off 544 // clang-format off
543 static const FunctionInfo functions[] = { 545 static const FunctionInfo functions[] = {
544 {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"}, 546 {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
545 {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"}, 547 {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
546 {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"}, 548 {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
547 {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"}, 549 {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
548 {7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"}, 550 {7996, &NS::PushIApplicationManagerInterface, "GetApplicationManagerInterface"},
549 {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"}, 551 {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
550 {7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"}, 552 {7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"},
551 {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"}, 553 {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
@@ -558,7 +560,7 @@ NS::NS(const char* name) : ServiceFramework{name} {
558NS::~NS() = default; 560NS::~NS() = default;
559 561
560std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const { 562std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const {
561 return GetInterface<IApplicationManagerInterface>(); 563 return GetInterface<IApplicationManagerInterface>(system);
562} 564}
563 565
564class NS_DEV final : public ServiceFramework<NS_DEV> { 566class NS_DEV final : public ServiceFramework<NS_DEV> {
@@ -678,11 +680,11 @@ public:
678 680
679void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 681void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
680 682
681 std::make_shared<NS>("ns:am2")->InstallAsService(service_manager); 683 std::make_shared<NS>("ns:am2", system)->InstallAsService(service_manager);
682 std::make_shared<NS>("ns:ec")->InstallAsService(service_manager); 684 std::make_shared<NS>("ns:ec", system)->InstallAsService(service_manager);
683 std::make_shared<NS>("ns:rid")->InstallAsService(service_manager); 685 std::make_shared<NS>("ns:rid", system)->InstallAsService(service_manager);
684 std::make_shared<NS>("ns:rt")->InstallAsService(service_manager); 686 std::make_shared<NS>("ns:rt", system)->InstallAsService(service_manager);
685 std::make_shared<NS>("ns:web")->InstallAsService(service_manager); 687 std::make_shared<NS>("ns:web", system)->InstallAsService(service_manager);
686 688
687 std::make_shared<NS_DEV>()->InstallAsService(service_manager); 689 std::make_shared<NS_DEV>()->InstallAsService(service_manager);
688 std::make_shared<NS_SU>()->InstallAsService(service_manager); 690 std::make_shared<NS_SU>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index c2554b878..c90ccd755 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -6,6 +6,10 @@
6 6
7#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8 8
9namespace Core {
10class System;
11}
12
9namespace Service { 13namespace Service {
10 14
11namespace FileSystem { 15namespace FileSystem {
@@ -22,7 +26,7 @@ public:
22 26
23class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> { 27class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
24public: 28public:
25 explicit IApplicationManagerInterface(); 29 explicit IApplicationManagerInterface(Core::System& system_);
26 ~IApplicationManagerInterface() override; 30 ~IApplicationManagerInterface() override;
27 31
28 ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages); 32 ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages);
@@ -32,6 +36,8 @@ private:
32 void GetApplicationControlData(Kernel::HLERequestContext& ctx); 36 void GetApplicationControlData(Kernel::HLERequestContext& ctx);
33 void GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx); 37 void GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx);
34 void ConvertApplicationLanguageToLanguageCode(Kernel::HLERequestContext& ctx); 38 void ConvertApplicationLanguageToLanguageCode(Kernel::HLERequestContext& ctx);
39
40 Core::System& system;
35}; 41};
36 42
37class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { 43class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
@@ -72,13 +78,13 @@ public:
72 78
73class NS final : public ServiceFramework<NS> { 79class NS final : public ServiceFramework<NS> {
74public: 80public:
75 explicit NS(const char* name); 81 explicit NS(const char* name, Core::System& system_);
76 ~NS() override; 82 ~NS() override;
77 83
78 std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const; 84 std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const;
79 85
80private: 86private:
81 template <typename T> 87 template <typename T, typename... Args>
82 void PushInterface(Kernel::HLERequestContext& ctx) { 88 void PushInterface(Kernel::HLERequestContext& ctx) {
83 LOG_DEBUG(Service_NS, "called"); 89 LOG_DEBUG(Service_NS, "called");
84 90
@@ -87,13 +93,23 @@ private:
87 rb.PushIpcInterface<T>(); 93 rb.PushIpcInterface<T>();
88 } 94 }
89 95
90 template <typename T> 96 void PushIApplicationManagerInterface(Kernel::HLERequestContext& ctx) {
91 std::shared_ptr<T> GetInterface() const { 97 LOG_DEBUG(Service_NS, "called");
98
99 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
100 rb.Push(RESULT_SUCCESS);
101 rb.PushIpcInterface<IApplicationManagerInterface>(system);
102 }
103
104 template <typename T, typename... Args>
105 std::shared_ptr<T> GetInterface(Args&&... args) const {
92 static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>, 106 static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>,
93 "Not a base of ServiceFrameworkBase"); 107 "Not a base of ServiceFrameworkBase");
94 108
95 return std::make_shared<T>(); 109 return std::make_shared<T>(std::forward<Args>(args)...);
96 } 110 }
111
112 Core::System& system;
97}; 113};
98 114
99/// Registers all NS services with the specified service manager. 115/// Registers all NS services with the specified service manager.
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 0240d6643..5681599ba 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -24,25 +24,37 @@ public:
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
27 union Ioctl { 27 /**
28 u32_le raw; 28 * Handles an ioctl1 request.
29 BitField<0, 8, u32> cmd; 29 * @param command The ioctl command id.
30 BitField<8, 8, u32> group; 30 * @param input A buffer containing the input data for the ioctl.
31 BitField<16, 14, u32> length; 31 * @param output A buffer where the output data will be written to.
32 BitField<30, 1, u32> is_in; 32 * @returns The result code of the ioctl.
33 BitField<31, 1, u32> is_out; 33 */
34 }; 34 virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
35 std::vector<u8>& output) = 0;
36
37 /**
38 * Handles an ioctl2 request.
39 * @param command The ioctl command id.
40 * @param input A buffer containing the input data for the ioctl.
41 * @param inline_input A buffer containing the input data for the ioctl which has been inlined.
42 * @param output A buffer where the output data will be written to.
43 * @returns The result code of the ioctl.
44 */
45 virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
46 const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
35 47
36 /** 48 /**
37 * Handles an ioctl request. 49 * Handles an ioctl3 request.
38 * @param command The ioctl command id. 50 * @param command The ioctl command id.
39 * @param input A buffer containing the input data for the ioctl. 51 * @param input A buffer containing the input data for the ioctl.
40 * @param output A buffer where the output data will be written to. 52 * @param output A buffer where the output data will be written to.
53 * @param inline_output A buffer where the inlined output data will be written to.
41 * @returns The result code of the ioctl. 54 * @returns The result code of the ioctl.
42 */ 55 */
43 virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 56 virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
44 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 57 std::vector<u8>& inline_output) = 0;
45 IoctlVersion version) = 0;
46 58
47protected: 59protected:
48 Core::System& system; 60 Core::System& system;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 3f7b8e670..ce615c758 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -18,11 +18,22 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de
18 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 18 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
19nvdisp_disp0 ::~nvdisp_disp0() = default; 19nvdisp_disp0 ::~nvdisp_disp0() = default;
20 20
21u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 21NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input,
22 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 22 std::vector<u8>& output) {
23 IoctlVersion version) { 23 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
24 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 24 return NvResult::NotImplemented;
25 return 0; 25}
26
27NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input,
28 const std::vector<u8>& inline_input, std::vector<u8>& output) {
29 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
30 return NvResult::NotImplemented;
31}
32
33NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
34 std::vector<u8>& inline_output) {
35 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
36 return NvResult::NotImplemented;
26} 37}
27 38
28void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, 39void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index 6fcdeee84..55a33b7e4 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -20,9 +20,11 @@ public:
20 explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 20 explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
21 ~nvdisp_disp0() override; 21 ~nvdisp_disp0() override;
22 22
23 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 23 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
24 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 24 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
25 IoctlVersion version) override; 25 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
26 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
27 std::vector<u8>& inline_output) override;
26 28
27 /// Performs a screen flip, drawing the buffer pointed to by the handle. 29 /// Performs a screen flip, drawing the buffer pointed to by the handle.
28 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, 30 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index f2529a12e..6b062e10e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -17,59 +17,77 @@
17 17
18namespace Service::Nvidia::Devices { 18namespace Service::Nvidia::Devices {
19 19
20namespace NvErrCodes {
21constexpr u32 Success{};
22constexpr u32 OutOfMemory{static_cast<u32>(-12)};
23constexpr u32 InvalidInput{static_cast<u32>(-22)};
24} // namespace NvErrCodes
25
26nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) 20nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
27 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 21 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
28nvhost_as_gpu::~nvhost_as_gpu() = default; 22nvhost_as_gpu::~nvhost_as_gpu() = default;
29 23
30u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 24NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
31 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 25 std::vector<u8>& output) {
32 IoctlVersion version) { 26 switch (command.group) {
33 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 27 case 'A':
34 command.raw, input.size(), output.size()); 28 switch (command.cmd) {
35 29 case 0x1:
36 switch (static_cast<IoctlCommand>(command.raw)) { 30 return BindChannel(input, output);
37 case IoctlCommand::IocInitalizeExCommand: 31 case 0x2:
38 return InitalizeEx(input, output); 32 return AllocateSpace(input, output);
39 case IoctlCommand::IocAllocateSpaceCommand: 33 case 0x3:
40 return AllocateSpace(input, output); 34 return FreeSpace(input, output);
41 case IoctlCommand::IocMapBufferExCommand: 35 case 0x5:
42 return MapBufferEx(input, output); 36 return UnmapBuffer(input, output);
43 case IoctlCommand::IocBindChannelCommand: 37 case 0x6:
44 return BindChannel(input, output); 38 return MapBufferEx(input, output);
45 case IoctlCommand::IocGetVaRegionsCommand: 39 case 0x8:
46 return GetVARegions(input, output); 40 return GetVARegions(input, output);
47 case IoctlCommand::IocUnmapBufferCommand: 41 case 0x9:
48 return UnmapBuffer(input, output); 42 return InitalizeEx(input, output);
49 case IoctlCommand::IocFreeSpaceCommand: 43 case 0x14:
50 return FreeSpace(input, output); 44 return Remap(input, output);
45 default:
46 break;
47 }
48 break;
51 default: 49 default:
52 break; 50 break;
53 } 51 }
54 52
55 if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) { 53 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
56 return Remap(input, output); 54 return NvResult::NotImplemented;
57 } 55}
56
57NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
58 const std::vector<u8>& inline_input, std::vector<u8>& output) {
59 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
60 return NvResult::NotImplemented;
61}
58 62
59 UNIMPLEMENTED_MSG("Unimplemented ioctl command"); 63NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
60 return 0; 64 std::vector<u8>& inline_output) {
65 switch (command.group) {
66 case 'A':
67 switch (command.cmd) {
68 case 0x8:
69 return GetVARegions(input, output, inline_output);
70 default:
71 break;
72 }
73 break;
74 default:
75 break;
76 }
77 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
78 return NvResult::NotImplemented;
61} 79}
62 80
63u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) { 81NvResult nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) {
64 IoctlInitalizeEx params{}; 82 IoctlInitalizeEx params{};
65 std::memcpy(&params, input.data(), input.size()); 83 std::memcpy(&params, input.data(), input.size());
66 84
67 LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); 85 LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size);
68 86
69 return 0; 87 return NvResult::Success;
70} 88}
71 89
72u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { 90NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
73 IoctlAllocSpace params{}; 91 IoctlAllocSpace params{};
74 std::memcpy(&params, input.data(), input.size()); 92 std::memcpy(&params, input.data(), input.size());
75 93
@@ -83,17 +101,17 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
83 params.offset = system.GPU().MemoryManager().Allocate(size, params.align); 101 params.offset = system.GPU().MemoryManager().Allocate(size, params.align);
84 } 102 }
85 103
86 auto result{NvErrCodes::Success}; 104 auto result = NvResult::Success;
87 if (!params.offset) { 105 if (!params.offset) {
88 LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size); 106 LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size);
89 result = NvErrCodes::OutOfMemory; 107 result = NvResult::InsufficientMemory;
90 } 108 }
91 109
92 std::memcpy(output.data(), &params, output.size()); 110 std::memcpy(output.data(), &params, output.size());
93 return result; 111 return result;
94} 112}
95 113
96u32 nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) { 114NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) {
97 IoctlFreeSpace params{}; 115 IoctlFreeSpace params{};
98 std::memcpy(&params, input.data(), input.size()); 116 std::memcpy(&params, input.data(), input.size());
99 117
@@ -104,15 +122,15 @@ u32 nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& outp
104 static_cast<std::size_t>(params.pages) * params.page_size); 122 static_cast<std::size_t>(params.pages) * params.page_size);
105 123
106 std::memcpy(output.data(), &params, output.size()); 124 std::memcpy(output.data(), &params, output.size());
107 return NvErrCodes::Success; 125 return NvResult::Success;
108} 126}
109 127
110u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { 128NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
111 const auto num_entries = input.size() / sizeof(IoctlRemapEntry); 129 const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
112 130
113 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); 131 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
114 132
115 auto result{NvErrCodes::Success}; 133 auto result = NvResult::Success;
116 std::vector<IoctlRemapEntry> entries(num_entries); 134 std::vector<IoctlRemapEntry> entries(num_entries);
117 std::memcpy(entries.data(), input.data(), input.size()); 135 std::memcpy(entries.data(), input.data(), input.size());
118 136
@@ -123,7 +141,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
123 const auto object{nvmap_dev->GetObject(entry.nvmap_handle)}; 141 const auto object{nvmap_dev->GetObject(entry.nvmap_handle)};
124 if (!object) { 142 if (!object) {
125 LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle); 143 LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle);
126 result = NvErrCodes::InvalidInput; 144 result = NvResult::InvalidState;
127 break; 145 break;
128 } 146 }
129 147
@@ -134,7 +152,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
134 152
135 if (!addr) { 153 if (!addr) {
136 LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!"); 154 LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!");
137 result = NvErrCodes::InvalidInput; 155 result = NvResult::InvalidState;
138 break; 156 break;
139 } 157 }
140 } 158 }
@@ -143,7 +161,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
143 return result; 161 return result;
144} 162}
145 163
146u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { 164NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
147 IoctlMapBufferEx params{}; 165 IoctlMapBufferEx params{};
148 std::memcpy(&params, input.data(), input.size()); 166 std::memcpy(&params, input.data(), input.size());
149 167
@@ -157,7 +175,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
157 if (!object) { 175 if (!object) {
158 LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle); 176 LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
159 std::memcpy(output.data(), &params, output.size()); 177 std::memcpy(output.data(), &params, output.size());
160 return NvErrCodes::InvalidInput; 178 return NvResult::InvalidState;
161 } 179 }
162 180
163 // The real nvservices doesn't make a distinction between handles and ids, and 181 // The real nvservices doesn't make a distinction between handles and ids, and
@@ -184,16 +202,16 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
184 params.mapping_size, params.offset); 202 params.mapping_size, params.offset);
185 203
186 std::memcpy(output.data(), &params, output.size()); 204 std::memcpy(output.data(), &params, output.size());
187 return NvErrCodes::InvalidInput; 205 return NvResult::InvalidState;
188 } 206 }
189 207
190 std::memcpy(output.data(), &params, output.size()); 208 std::memcpy(output.data(), &params, output.size());
191 return NvErrCodes::Success; 209 return NvResult::Success;
192 } else { 210 } else {
193 LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset); 211 LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset);
194 212
195 std::memcpy(output.data(), &params, output.size()); 213 std::memcpy(output.data(), &params, output.size());
196 return NvErrCodes::InvalidInput; 214 return NvResult::InvalidState;
197 } 215 }
198 } 216 }
199 217
@@ -213,10 +231,10 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
213 params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size); 231 params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size);
214 } 232 }
215 233
216 auto result{NvErrCodes::Success}; 234 auto result = NvResult::Success;
217 if (!params.offset) { 235 if (!params.offset) {
218 LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size); 236 LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size);
219 result = NvErrCodes::InvalidInput; 237 result = NvResult::InvalidState;
220 } else { 238 } else {
221 AddBufferMap(params.offset, size, physical_address, is_alloc); 239 AddBufferMap(params.offset, size, physical_address, is_alloc);
222 } 240 }
@@ -225,7 +243,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
225 return result; 243 return result;
226} 244}
227 245
228u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 246NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
229 IoctlUnmapBuffer params{}; 247 IoctlUnmapBuffer params{};
230 std::memcpy(&params, input.data(), input.size()); 248 std::memcpy(&params, input.data(), input.size());
231 249
@@ -238,20 +256,42 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
238 } 256 }
239 257
240 std::memcpy(output.data(), &params, output.size()); 258 std::memcpy(output.data(), &params, output.size());
241 return NvErrCodes::Success; 259 return NvResult::Success;
242} 260}
243 261
244u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { 262NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
245 IoctlBindChannel params{}; 263 IoctlBindChannel params{};
246 std::memcpy(&params, input.data(), input.size()); 264 std::memcpy(&params, input.data(), input.size());
247 265 LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd);
248 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
249 266
250 channel = params.fd; 267 channel = params.fd;
251 return 0; 268 return NvResult::Success;
269}
270
271NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
272 IoctlGetVaRegions params{};
273 std::memcpy(&params, input.data(), input.size());
274
275 LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
276 params.buf_size);
277
278 params.buf_size = 0x30;
279 params.regions[0].offset = 0x04000000;
280 params.regions[0].page_size = 0x1000;
281 params.regions[0].pages = 0x3fbfff;
282
283 params.regions[1].offset = 0x04000000;
284 params.regions[1].page_size = 0x10000;
285 params.regions[1].pages = 0x1bffff;
286
287 // TODO(ogniK): This probably can stay stubbed but should add support way way later
288
289 std::memcpy(output.data(), &params, output.size());
290 return NvResult::Success;
252} 291}
253 292
254u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { 293NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
294 std::vector<u8>& inline_output) {
255 IoctlGetVaRegions params{}; 295 IoctlGetVaRegions params{};
256 std::memcpy(&params, input.data(), input.size()); 296 std::memcpy(&params, input.data(), input.size());
257 297
@@ -270,7 +310,8 @@ u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& o
270 // TODO(ogniK): This probably can stay stubbed but should add support way way later 310 // TODO(ogniK): This probably can stay stubbed but should add support way way later
271 311
272 std::memcpy(output.data(), &params, output.size()); 312 std::memcpy(output.data(), &params, output.size());
273 return 0; 313 std::memcpy(inline_output.data(), &params.regions, inline_output.size());
314 return NvResult::Success;
274} 315}
275 316
276std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const { 317std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index fcdb40d93..08035fa0e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -30,9 +30,11 @@ public:
30 explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 30 explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
31 ~nvhost_as_gpu() override; 31 ~nvhost_as_gpu() override;
32 32
33 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 33 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
34 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 34 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
35 IoctlVersion version) override; 35 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
36 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
37 std::vector<u8>& inline_output) override;
36 38
37private: 39private:
38 class BufferMap final { 40 class BufferMap final {
@@ -74,32 +76,21 @@ private:
74 bool is_allocated{}; 76 bool is_allocated{};
75 }; 77 };
76 78
77 enum class IoctlCommand : u32_le {
78 IocInitalizeExCommand = 0x40284109,
79 IocAllocateSpaceCommand = 0xC0184102,
80 IocRemapCommand = 0x00000014,
81 IocMapBufferExCommand = 0xC0284106,
82 IocBindChannelCommand = 0x40044101,
83 IocGetVaRegionsCommand = 0xC0404108,
84 IocUnmapBufferCommand = 0xC0084105,
85 IocFreeSpaceCommand = 0xC0104103,
86 };
87
88 struct IoctlInitalizeEx { 79 struct IoctlInitalizeEx {
89 u32_le big_page_size; // depends on GPU's available_big_page_sizes; 0=default 80 u32_le big_page_size{}; // depends on GPU's available_big_page_sizes; 0=default
90 s32_le as_fd; // ignored; passes 0 81 s32_le as_fd{}; // ignored; passes 0
91 u32_le flags; // passes 0 82 u32_le flags{}; // passes 0
92 u32_le reserved; // ignored; passes 0 83 u32_le reserved{}; // ignored; passes 0
93 u64_le unk0; 84 u64_le unk0{};
94 u64_le unk1; 85 u64_le unk1{};
95 u64_le unk2; 86 u64_le unk2{};
96 }; 87 };
97 static_assert(sizeof(IoctlInitalizeEx) == 40, "IoctlInitalizeEx is incorrect size"); 88 static_assert(sizeof(IoctlInitalizeEx) == 40, "IoctlInitalizeEx is incorrect size");
98 89
99 struct IoctlAllocSpace { 90 struct IoctlAllocSpace {
100 u32_le pages; 91 u32_le pages{};
101 u32_le page_size; 92 u32_le page_size{};
102 AddressSpaceFlags flags; 93 AddressSpaceFlags flags{};
103 INSERT_PADDING_WORDS(1); 94 INSERT_PADDING_WORDS(1);
104 union { 95 union {
105 u64_le offset; 96 u64_le offset;
@@ -109,70 +100,73 @@ private:
109 static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size"); 100 static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size");
110 101
111 struct IoctlFreeSpace { 102 struct IoctlFreeSpace {
112 u64_le offset; 103 u64_le offset{};
113 u32_le pages; 104 u32_le pages{};
114 u32_le page_size; 105 u32_le page_size{};
115 }; 106 };
116 static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size"); 107 static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size");
117 108
118 struct IoctlRemapEntry { 109 struct IoctlRemapEntry {
119 u16_le flags; 110 u16_le flags{};
120 u16_le kind; 111 u16_le kind{};
121 u32_le nvmap_handle; 112 u32_le nvmap_handle{};
122 u32_le map_offset; 113 u32_le map_offset{};
123 u32_le offset; 114 u32_le offset{};
124 u32_le pages; 115 u32_le pages{};
125 }; 116 };
126 static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); 117 static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
127 118
128 struct IoctlMapBufferEx { 119 struct IoctlMapBufferEx {
129 AddressSpaceFlags flags; // bit0: fixed_offset, bit2: cacheable 120 AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable
130 u32_le kind; // -1 is default 121 u32_le kind{}; // -1 is default
131 u32_le nvmap_handle; 122 u32_le nvmap_handle{};
132 u32_le page_size; // 0 means don't care 123 u32_le page_size{}; // 0 means don't care
133 s64_le buffer_offset; 124 s64_le buffer_offset{};
134 u64_le mapping_size; 125 u64_le mapping_size{};
135 s64_le offset; 126 s64_le offset{};
136 }; 127 };
137 static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size"); 128 static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size");
138 129
139 struct IoctlUnmapBuffer { 130 struct IoctlUnmapBuffer {
140 s64_le offset; 131 s64_le offset{};
141 }; 132 };
142 static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size"); 133 static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size");
143 134
144 struct IoctlBindChannel { 135 struct IoctlBindChannel {
145 u32_le fd; 136 s32_le fd{};
146 }; 137 };
147 static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size"); 138 static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size");
148 139
149 struct IoctlVaRegion { 140 struct IoctlVaRegion {
150 u64_le offset; 141 u64_le offset{};
151 u32_le page_size; 142 u32_le page_size{};
152 INSERT_PADDING_WORDS(1); 143 INSERT_PADDING_WORDS(1);
153 u64_le pages; 144 u64_le pages{};
154 }; 145 };
155 static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size"); 146 static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size");
156 147
157 struct IoctlGetVaRegions { 148 struct IoctlGetVaRegions {
158 u64_le buf_addr; // (contained output user ptr on linux, ignored) 149 u64_le buf_addr{}; // (contained output user ptr on linux, ignored)
159 u32_le buf_size; // forced to 2*sizeof(struct va_region) 150 u32_le buf_size{}; // forced to 2*sizeof(struct va_region)
160 u32_le reserved; 151 u32_le reserved{};
161 IoctlVaRegion regions[2]; 152 IoctlVaRegion regions[2]{};
162 }; 153 };
163 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, 154 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2,
164 "IoctlGetVaRegions is incorrect size"); 155 "IoctlGetVaRegions is incorrect size");
165 156
166 u32 channel{}; 157 s32 channel{};
158
159 NvResult InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
160 NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
161 NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output);
162 NvResult MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
163 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
164 NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
165 NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
167 166
168 u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output); 167 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
169 u32 AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); 168 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
170 u32 Remap(const std::vector<u8>& input, std::vector<u8>& output); 169 std::vector<u8>& inline_output);
171 u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
172 u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
173 u32 FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
174 u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
175 u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
176 170
177 std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const; 171 std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
178 void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); 172 void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 8356a8139..d90cf90a8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -20,41 +20,54 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,
20 : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {} 20 : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
21nvhost_ctrl::~nvhost_ctrl() = default; 21nvhost_ctrl::~nvhost_ctrl() = default;
22 22
23u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 23NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
24 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 24 switch (command.group) {
25 IoctlVersion version) { 25 case 0x0:
26 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 26 switch (command.cmd) {
27 command.raw, input.size(), output.size()); 27 case 0x1b:
28 28 return NvOsGetConfigU32(input, output);
29 switch (static_cast<IoctlCommand>(command.raw)) { 29 case 0x1c:
30 case IoctlCommand::IocGetConfigCommand: 30 return IocCtrlClearEventWait(input, output);
31 return NvOsGetConfigU32(input, output); 31 case 0x1d:
32 case IoctlCommand::IocCtrlEventWaitCommand: 32 return IocCtrlEventWait(input, output, false);
33 return IocCtrlEventWait(input, output, false, ctrl); 33 case 0x1e:
34 case IoctlCommand::IocCtrlEventWaitAsyncCommand: 34 return IocCtrlEventWait(input, output, true);
35 return IocCtrlEventWait(input, output, true, ctrl); 35 case 0x1f:
36 case IoctlCommand::IocCtrlEventRegisterCommand: 36 return IocCtrlEventRegister(input, output);
37 return IocCtrlEventRegister(input, output); 37 case 0x20:
38 case IoctlCommand::IocCtrlEventUnregisterCommand: 38 return IocCtrlEventUnregister(input, output);
39 return IocCtrlEventUnregister(input, output); 39 }
40 case IoctlCommand::IocCtrlClearEventWaitCommand: 40 break;
41 return IocCtrlClearEventWait(input, output);
42 default: 41 default:
43 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 42 break;
44 return 0;
45 } 43 }
44
45 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
46 return NvResult::NotImplemented;
47}
48
49NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input,
50 const std::vector<u8>& inline_input, std::vector<u8>& output) {
51 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
52 return NvResult::NotImplemented;
53}
54
55NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
56 std::vector<u8>& inline_output) {
57 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
58 return NvResult::NotImplemented;
46} 59}
47 60
48u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { 61NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
49 IocGetConfigParams params{}; 62 IocGetConfigParams params{};
50 std::memcpy(&params, input.data(), sizeof(params)); 63 std::memcpy(&params, input.data(), sizeof(params));
51 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), 64 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
52 params.param_str.data()); 65 params.param_str.data());
53 return 0x30006; // Returns error on production mode 66 return NvResult::ConfigVarNotFound; // Returns error on production mode
54} 67}
55 68
56u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, 69NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
57 bool is_async, IoctlCtrl& ctrl) { 70 bool is_async) {
58 IocCtrlEventWaitParams params{}; 71 IocCtrlEventWaitParams params{};
59 std::memcpy(&params, input.data(), sizeof(params)); 72 std::memcpy(&params, input.data(), sizeof(params));
60 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", 73 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
@@ -126,10 +139,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
126 params.value |= event_id; 139 params.value |= event_id;
127 event.event.writable->Clear(); 140 event.event.writable->Clear();
128 gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); 141 gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
129 if (!is_async && ctrl.fresh_call) { 142 if (!is_async) {
130 ctrl.must_delay = true;
131 ctrl.timeout = params.timeout;
132 ctrl.event_id = event_id;
133 return NvResult::Timeout; 143 return NvResult::Timeout;
134 } 144 }
135 std::memcpy(output.data(), &params, sizeof(params)); 145 std::memcpy(output.data(), &params, sizeof(params));
@@ -139,7 +149,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
139 return NvResult::BadParameter; 149 return NvResult::BadParameter;
140} 150}
141 151
142u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { 152NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
143 IocCtrlEventRegisterParams params{}; 153 IocCtrlEventRegisterParams params{};
144 std::memcpy(&params, input.data(), sizeof(params)); 154 std::memcpy(&params, input.data(), sizeof(params));
145 const u32 event_id = params.user_event_id & 0x00FF; 155 const u32 event_id = params.user_event_id & 0x00FF;
@@ -154,7 +164,8 @@ u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<
154 return NvResult::Success; 164 return NvResult::Success;
155} 165}
156 166
157u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) { 167NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
168 std::vector<u8>& output) {
158 IocCtrlEventUnregisterParams params{}; 169 IocCtrlEventUnregisterParams params{};
159 std::memcpy(&params, input.data(), sizeof(params)); 170 std::memcpy(&params, input.data(), sizeof(params));
160 const u32 event_id = params.user_event_id & 0x00FF; 171 const u32 event_id = params.user_event_id & 0x00FF;
@@ -169,7 +180,7 @@ u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vecto
169 return NvResult::Success; 180 return NvResult::Success;
170} 181}
171 182
172u32 nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { 183NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
173 IocCtrlEventSignalParams params{}; 184 IocCtrlEventSignalParams params{};
174 std::memcpy(&params, input.data(), sizeof(params)); 185 std::memcpy(&params, input.data(), sizeof(params));
175 186
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 24ad96cb9..c5aa1362a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -18,132 +18,113 @@ public:
18 SyncpointManager& syncpoint_manager); 18 SyncpointManager& syncpoint_manager);
19 ~nvhost_ctrl() override; 19 ~nvhost_ctrl() override;
20 20
21 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 21 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
22 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 22 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
23 IoctlVersion version) override; 23 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
24 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
25 std::vector<u8>& inline_output) override;
24 26
25private: 27private:
26 enum class IoctlCommand : u32_le {
27 IocSyncptReadCommand = 0xC0080014,
28 IocSyncptIncrCommand = 0x40040015,
29 IocSyncptWaitCommand = 0xC00C0016,
30 IocModuleMutexCommand = 0x40080017,
31 IocModuleRegRDWRCommand = 0xC0180018,
32 IocSyncptWaitexCommand = 0xC0100019,
33 IocSyncptReadMaxCommand = 0xC008001A,
34 IocGetConfigCommand = 0xC183001B,
35 IocCtrlClearEventWaitCommand = 0xC004001C,
36 IocCtrlEventWaitCommand = 0xC010001D,
37 IocCtrlEventWaitAsyncCommand = 0xC010001E,
38 IocCtrlEventRegisterCommand = 0xC004001F,
39 IocCtrlEventUnregisterCommand = 0xC0040020,
40 IocCtrlEventKillCommand = 0x40080021,
41 };
42 struct IocSyncptReadParams { 28 struct IocSyncptReadParams {
43 u32_le id; 29 u32_le id{};
44 u32_le value; 30 u32_le value{};
45 }; 31 };
46 static_assert(sizeof(IocSyncptReadParams) == 8, "IocSyncptReadParams is incorrect size"); 32 static_assert(sizeof(IocSyncptReadParams) == 8, "IocSyncptReadParams is incorrect size");
47 33
48 struct IocSyncptIncrParams { 34 struct IocSyncptIncrParams {
49 u32_le id; 35 u32_le id{};
50 }; 36 };
51 static_assert(sizeof(IocSyncptIncrParams) == 4, "IocSyncptIncrParams is incorrect size"); 37 static_assert(sizeof(IocSyncptIncrParams) == 4, "IocSyncptIncrParams is incorrect size");
52 38
53 struct IocSyncptWaitParams { 39 struct IocSyncptWaitParams {
54 u32_le id; 40 u32_le id{};
55 u32_le thresh; 41 u32_le thresh{};
56 s32_le timeout; 42 s32_le timeout{};
57 }; 43 };
58 static_assert(sizeof(IocSyncptWaitParams) == 12, "IocSyncptWaitParams is incorrect size"); 44 static_assert(sizeof(IocSyncptWaitParams) == 12, "IocSyncptWaitParams is incorrect size");
59 45
60 struct IocModuleMutexParams { 46 struct IocModuleMutexParams {
61 u32_le id; 47 u32_le id{};
62 u32_le lock; // (0 = unlock and 1 = lock) 48 u32_le lock{}; // (0 = unlock and 1 = lock)
63 }; 49 };
64 static_assert(sizeof(IocModuleMutexParams) == 8, "IocModuleMutexParams is incorrect size"); 50 static_assert(sizeof(IocModuleMutexParams) == 8, "IocModuleMutexParams is incorrect size");
65 51
66 struct IocModuleRegRDWRParams { 52 struct IocModuleRegRDWRParams {
67 u32_le id; 53 u32_le id{};
68 u32_le num_offsets; 54 u32_le num_offsets{};
69 u32_le block_size; 55 u32_le block_size{};
70 u32_le offsets; 56 u32_le offsets{};
71 u32_le values; 57 u32_le values{};
72 u32_le write; 58 u32_le write{};
73 }; 59 };
74 static_assert(sizeof(IocModuleRegRDWRParams) == 24, "IocModuleRegRDWRParams is incorrect size"); 60 static_assert(sizeof(IocModuleRegRDWRParams) == 24, "IocModuleRegRDWRParams is incorrect size");
75 61
76 struct IocSyncptWaitexParams { 62 struct IocSyncptWaitexParams {
77 u32_le id; 63 u32_le id{};
78 u32_le thresh; 64 u32_le thresh{};
79 s32_le timeout; 65 s32_le timeout{};
80 u32_le value; 66 u32_le value{};
81 }; 67 };
82 static_assert(sizeof(IocSyncptWaitexParams) == 16, "IocSyncptWaitexParams is incorrect size"); 68 static_assert(sizeof(IocSyncptWaitexParams) == 16, "IocSyncptWaitexParams is incorrect size");
83 69
84 struct IocSyncptReadMaxParams { 70 struct IocSyncptReadMaxParams {
85 u32_le id; 71 u32_le id{};
86 u32_le value; 72 u32_le value{};
87 }; 73 };
88 static_assert(sizeof(IocSyncptReadMaxParams) == 8, "IocSyncptReadMaxParams is incorrect size"); 74 static_assert(sizeof(IocSyncptReadMaxParams) == 8, "IocSyncptReadMaxParams is incorrect size");
89 75
90 struct IocGetConfigParams { 76 struct IocGetConfigParams {
91 std::array<char, 0x41> domain_str; 77 std::array<char, 0x41> domain_str{};
92 std::array<char, 0x41> param_str; 78 std::array<char, 0x41> param_str{};
93 std::array<char, 0x101> config_str; 79 std::array<char, 0x101> config_str{};
94 }; 80 };
95 static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); 81 static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
96 82
97 struct IocCtrlEventSignalParams { 83 struct IocCtrlEventSignalParams {
98 u32_le event_id; 84 u32_le event_id{};
99 }; 85 };
100 static_assert(sizeof(IocCtrlEventSignalParams) == 4, 86 static_assert(sizeof(IocCtrlEventSignalParams) == 4,
101 "IocCtrlEventSignalParams is incorrect size"); 87 "IocCtrlEventSignalParams is incorrect size");
102 88
103 struct IocCtrlEventWaitParams { 89 struct IocCtrlEventWaitParams {
104 u32_le syncpt_id; 90 u32_le syncpt_id{};
105 u32_le threshold; 91 u32_le threshold{};
106 s32_le timeout; 92 s32_le timeout{};
107 u32_le value; 93 u32_le value{};
108 }; 94 };
109 static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size"); 95 static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
110 96
111 struct IocCtrlEventWaitAsyncParams { 97 struct IocCtrlEventWaitAsyncParams {
112 u32_le syncpt_id; 98 u32_le syncpt_id{};
113 u32_le threshold; 99 u32_le threshold{};
114 u32_le timeout; 100 u32_le timeout{};
115 u32_le value; 101 u32_le value{};
116 }; 102 };
117 static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16, 103 static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16,
118 "IocCtrlEventWaitAsyncParams is incorrect size"); 104 "IocCtrlEventWaitAsyncParams is incorrect size");
119 105
120 struct IocCtrlEventRegisterParams { 106 struct IocCtrlEventRegisterParams {
121 u32_le user_event_id; 107 u32_le user_event_id{};
122 }; 108 };
123 static_assert(sizeof(IocCtrlEventRegisterParams) == 4, 109 static_assert(sizeof(IocCtrlEventRegisterParams) == 4,
124 "IocCtrlEventRegisterParams is incorrect size"); 110 "IocCtrlEventRegisterParams is incorrect size");
125 111
126 struct IocCtrlEventUnregisterParams { 112 struct IocCtrlEventUnregisterParams {
127 u32_le user_event_id; 113 u32_le user_event_id{};
128 }; 114 };
129 static_assert(sizeof(IocCtrlEventUnregisterParams) == 4, 115 static_assert(sizeof(IocCtrlEventUnregisterParams) == 4,
130 "IocCtrlEventUnregisterParams is incorrect size"); 116 "IocCtrlEventUnregisterParams is incorrect size");
131 117
132 struct IocCtrlEventKill { 118 struct IocCtrlEventKill {
133 u64_le user_events; 119 u64_le user_events{};
134 }; 120 };
135 static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); 121 static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
136 122
137 u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); 123 NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
138 124 NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
139 u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async, 125 NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
140 IoctlCtrl& ctrl); 126 NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
141 127 NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
142 u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
143
144 u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
145
146 u32 IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
147 128
148 EventInterface& events_interface; 129 EventInterface& events_interface;
149 SyncpointManager& syncpoint_manager; 130 SyncpointManager& syncpoint_manager;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index fba89e7a6..2d7ea433c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -15,39 +15,66 @@ namespace Service::Nvidia::Devices {
15nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {} 15nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
16nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; 16nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
17 17
18u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, 18NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
19 const std::vector<u8>& input2, std::vector<u8>& output, 19 std::vector<u8>& output) {
20 std::vector<u8>& output2, IoctlCtrl& ctrl, IoctlVersion version) { 20 switch (command.group) {
21 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 21 case 'G':
22 command.raw, input.size(), output.size()); 22 switch (command.cmd) {
23 23 case 0x1:
24 switch (static_cast<IoctlCommand>(command.raw)) { 24 return ZCullGetCtxSize(input, output);
25 case IoctlCommand::IocGetCharacteristicsCommand: 25 case 0x2:
26 return GetCharacteristics(input, output, output2, version); 26 return ZCullGetInfo(input, output);
27 case IoctlCommand::IocGetTPCMasksCommand: 27 case 0x3:
28 return GetTPCMasks(input, output, output2, version); 28 return ZBCSetTable(input, output);
29 case IoctlCommand::IocGetActiveSlotMaskCommand: 29 case 0x4:
30 return GetActiveSlotMask(input, output); 30 return ZBCQueryTable(input, output);
31 case IoctlCommand::IocZcullGetCtxSizeCommand: 31 case 0x5:
32 return ZCullGetCtxSize(input, output); 32 return GetCharacteristics(input, output);
33 case IoctlCommand::IocZcullGetInfo: 33 case 0x6:
34 return ZCullGetInfo(input, output); 34 return GetTPCMasks(input, output);
35 case IoctlCommand::IocZbcSetTable: 35 case 0x7:
36 return ZBCSetTable(input, output); 36 return FlushL2(input, output);
37 case IoctlCommand::IocZbcQueryTable: 37 case 0x14:
38 return ZBCQueryTable(input, output); 38 return GetActiveSlotMask(input, output);
39 case IoctlCommand::IocFlushL2: 39 case 0x1c:
40 return FlushL2(input, output); 40 return GetGpuTime(input, output);
41 case IoctlCommand::IocGetGpuTime: 41 default:
42 return GetGpuTime(input, output); 42 break;
43 }
44 break;
45 }
46 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
47 return NvResult::NotImplemented;
48}
49
50NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
51 const std::vector<u8>& inline_input, std::vector<u8>& output) {
52 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
53 return NvResult::NotImplemented;
54}
55
56NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input,
57 std::vector<u8>& output, std::vector<u8>& inline_output) {
58 switch (command.group) {
59 case 'G':
60 switch (command.cmd) {
61 case 0x5:
62 return GetCharacteristics(input, output, inline_output);
63 case 0x6:
64 return GetTPCMasks(input, output, inline_output);
65 default:
66 break;
67 }
68 break;
43 default: 69 default:
44 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 70 break;
45 return 0;
46 } 71 }
72 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
73 return NvResult::NotImplemented;
47} 74}
48 75
49u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, 76NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input,
50 std::vector<u8>& output2, IoctlVersion version) { 77 std::vector<u8>& output) {
51 LOG_DEBUG(Service_NVDRV, "called"); 78 LOG_DEBUG(Service_NVDRV, "called");
52 IoctlCharacteristics params{}; 79 IoctlCharacteristics params{};
53 std::memcpy(&params, input.data(), input.size()); 80 std::memcpy(&params, input.data(), input.size());
@@ -88,36 +115,83 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto
88 params.gc.gr_compbit_store_base_hw = 0x0; 115 params.gc.gr_compbit_store_base_hw = 0x0;
89 params.gpu_characteristics_buf_size = 0xA0; 116 params.gpu_characteristics_buf_size = 0xA0;
90 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) 117 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
118 std::memcpy(output.data(), &params, output.size());
119 return NvResult::Success;
120}
91 121
92 if (version == IoctlVersion::Version3) { 122NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
93 std::memcpy(output.data(), input.data(), output.size()); 123 std::vector<u8>& inline_output) {
94 std::memcpy(output2.data(), &params.gc, output2.size()); 124 LOG_DEBUG(Service_NVDRV, "called");
95 } else { 125 IoctlCharacteristics params{};
96 std::memcpy(output.data(), &params, output.size()); 126 std::memcpy(&params, input.data(), input.size());
97 } 127 params.gc.arch = 0x120;
98 return 0; 128 params.gc.impl = 0xb;
129 params.gc.rev = 0xa1;
130 params.gc.num_gpc = 0x1;
131 params.gc.l2_cache_size = 0x40000;
132 params.gc.on_board_video_memory_size = 0x0;
133 params.gc.num_tpc_per_gpc = 0x2;
134 params.gc.bus_type = 0x20;
135 params.gc.big_page_size = 0x20000;
136 params.gc.compression_page_size = 0x20000;
137 params.gc.pde_coverage_bit_count = 0x1B;
138 params.gc.available_big_page_sizes = 0x30000;
139 params.gc.gpc_mask = 0x1;
140 params.gc.sm_arch_sm_version = 0x503;
141 params.gc.sm_arch_spa_version = 0x503;
142 params.gc.sm_arch_warp_count = 0x80;
143 params.gc.gpu_va_bit_count = 0x28;
144 params.gc.reserved = 0x0;
145 params.gc.flags = 0x55;
146 params.gc.twod_class = 0x902D;
147 params.gc.threed_class = 0xB197;
148 params.gc.compute_class = 0xB1C0;
149 params.gc.gpfifo_class = 0xB06F;
150 params.gc.inline_to_memory_class = 0xA140;
151 params.gc.dma_copy_class = 0xB0B5;
152 params.gc.max_fbps_count = 0x1;
153 params.gc.fbp_en_mask = 0x0;
154 params.gc.max_ltc_per_fbp = 0x2;
155 params.gc.max_lts_per_ltc = 0x1;
156 params.gc.max_tex_per_tpc = 0x0;
157 params.gc.max_gpc_count = 0x1;
158 params.gc.rop_l2_en_mask_0 = 0x21D70;
159 params.gc.rop_l2_en_mask_1 = 0x0;
160 params.gc.chipname = 0x6230326D67;
161 params.gc.gr_compbit_store_base_hw = 0x0;
162 params.gpu_characteristics_buf_size = 0xA0;
163 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
164
165 std::memcpy(output.data(), input.data(), output.size());
166 std::memcpy(inline_output.data(), &params.gc, inline_output.size());
167 return NvResult::Success;
99} 168}
100 169
101u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, 170NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) {
102 std::vector<u8>& output2, IoctlVersion version) {
103 IoctlGpuGetTpcMasksArgs params{}; 171 IoctlGpuGetTpcMasksArgs params{};
104 std::memcpy(&params, input.data(), input.size()); 172 std::memcpy(&params, input.data(), input.size());
105 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); 173 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
106 if (params.mask_buffer_size != 0) { 174 if (params.mask_buffer_size != 0) {
107 params.tcp_mask = 3; 175 params.tcp_mask = 3;
108 } 176 }
177 std::memcpy(output.data(), &params, output.size());
178 return NvResult::Success;
179}
109 180
110 if (version == IoctlVersion::Version3) { 181NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
111 std::memcpy(output.data(), input.data(), output.size()); 182 std::vector<u8>& inline_output) {
112 std::memcpy(output2.data(), &params.tcp_mask, output2.size()); 183 IoctlGpuGetTpcMasksArgs params{};
113 } else { 184 std::memcpy(&params, input.data(), input.size());
114 std::memcpy(output.data(), &params, output.size()); 185 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
186 if (params.mask_buffer_size != 0) {
187 params.tcp_mask = 3;
115 } 188 }
116 189 std::memcpy(output.data(), &params, output.size());
117 return 0; 190 std::memcpy(inline_output.data(), &params.tcp_mask, inline_output.size());
191 return NvResult::Success;
118} 192}
119 193
120u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { 194NvResult nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) {
121 LOG_DEBUG(Service_NVDRV, "called"); 195 LOG_DEBUG(Service_NVDRV, "called");
122 196
123 IoctlActiveSlotMask params{}; 197 IoctlActiveSlotMask params{};
@@ -127,10 +201,10 @@ u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector
127 params.slot = 0x07; 201 params.slot = 0x07;
128 params.mask = 0x01; 202 params.mask = 0x01;
129 std::memcpy(output.data(), &params, output.size()); 203 std::memcpy(output.data(), &params, output.size());
130 return 0; 204 return NvResult::Success;
131} 205}
132 206
133u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) { 207NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) {
134 LOG_DEBUG(Service_NVDRV, "called"); 208 LOG_DEBUG(Service_NVDRV, "called");
135 209
136 IoctlZcullGetCtxSize params{}; 210 IoctlZcullGetCtxSize params{};
@@ -139,10 +213,10 @@ u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u
139 } 213 }
140 params.size = 0x1; 214 params.size = 0x1;
141 std::memcpy(output.data(), &params, output.size()); 215 std::memcpy(output.data(), &params, output.size());
142 return 0; 216 return NvResult::Success;
143} 217}
144 218
145u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) { 219NvResult nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) {
146 LOG_DEBUG(Service_NVDRV, "called"); 220 LOG_DEBUG(Service_NVDRV, "called");
147 221
148 IoctlNvgpuGpuZcullGetInfoArgs params{}; 222 IoctlNvgpuGpuZcullGetInfoArgs params{};
@@ -162,47 +236,47 @@ u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>&
162 params.subregion_height_align_pixels = 0x40; 236 params.subregion_height_align_pixels = 0x40;
163 params.subregion_count = 0x10; 237 params.subregion_count = 0x10;
164 std::memcpy(output.data(), &params, output.size()); 238 std::memcpy(output.data(), &params, output.size());
165 return 0; 239 return NvResult::Success;
166} 240}
167 241
168u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) { 242NvResult nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
169 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 243 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
170 244
171 IoctlZbcSetTable params{}; 245 IoctlZbcSetTable params{};
172 std::memcpy(&params, input.data(), input.size()); 246 std::memcpy(&params, input.data(), input.size());
173 // TODO(ogniK): What does this even actually do? 247 // TODO(ogniK): What does this even actually do?
174 std::memcpy(output.data(), &params, output.size()); 248 std::memcpy(output.data(), &params, output.size());
175 return 0; 249 return NvResult::Success;
176} 250}
177 251
178u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) { 252NvResult nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
179 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 253 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
180 254
181 IoctlZbcQueryTable params{}; 255 IoctlZbcQueryTable params{};
182 std::memcpy(&params, input.data(), input.size()); 256 std::memcpy(&params, input.data(), input.size());
183 // TODO : To implement properly 257 // TODO : To implement properly
184 std::memcpy(output.data(), &params, output.size()); 258 std::memcpy(output.data(), &params, output.size());
185 return 0; 259 return NvResult::Success;
186} 260}
187 261
188u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) { 262NvResult nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
189 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 263 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
190 264
191 IoctlFlushL2 params{}; 265 IoctlFlushL2 params{};
192 std::memcpy(&params, input.data(), input.size()); 266 std::memcpy(&params, input.data(), input.size());
193 // TODO : To implement properly 267 // TODO : To implement properly
194 std::memcpy(output.data(), &params, output.size()); 268 std::memcpy(output.data(), &params, output.size());
195 return 0; 269 return NvResult::Success;
196} 270}
197 271
198u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) { 272NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
199 LOG_DEBUG(Service_NVDRV, "called"); 273 LOG_DEBUG(Service_NVDRV, "called");
200 274
201 IoctlGetGpuTime params{}; 275 IoctlGetGpuTime params{};
202 std::memcpy(&params, input.data(), input.size()); 276 std::memcpy(&params, input.data(), input.size());
203 params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count()); 277 params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count());
204 std::memcpy(output.data(), &params, output.size()); 278 std::memcpy(output.data(), &params, output.size());
205 return 0; 279 return NvResult::Success;
206} 280}
207 281
208} // namespace Service::Nvidia::Devices 282} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index ef60f72ce..137b88238 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -16,32 +16,13 @@ public:
16 explicit nvhost_ctrl_gpu(Core::System& system); 16 explicit nvhost_ctrl_gpu(Core::System& system);
17 ~nvhost_ctrl_gpu() override; 17 ~nvhost_ctrl_gpu() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 19 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
20 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 20 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
21 IoctlVersion version) override; 21 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
22 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
23 std::vector<u8>& inline_output) override;
22 24
23private: 25private:
24 enum class IoctlCommand : u32_le {
25 IocGetCharacteristicsCommand = 0xC0B04705,
26 IocGetTPCMasksCommand = 0xC0184706,
27 IocGetActiveSlotMaskCommand = 0x80084714,
28 IocZcullGetCtxSizeCommand = 0x80044701,
29 IocZcullGetInfo = 0x80284702,
30 IocZbcSetTable = 0x402C4703,
31 IocZbcQueryTable = 0xC0344704,
32 IocFlushL2 = 0x40084707,
33 IocInvalICache = 0x4008470D,
34 IocSetMmudebugMode = 0x4008470E,
35 IocSetSmDebugMode = 0x4010470F,
36 IocWaitForPause = 0xC0084710,
37 IocGetTcpExceptionEnStatus = 0x80084711,
38 IocNumVsms = 0x80084712,
39 IocVsmsMapping = 0xC0044713,
40 IocGetErrorChannelUserData = 0xC008471B,
41 IocGetGpuTime = 0xC010471C,
42 IocGetCpuTimeCorrelationInfo = 0xC108471D,
43 };
44
45 struct IoctlGpuCharacteristics { 26 struct IoctlGpuCharacteristics {
46 u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200) 27 u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200)
47 u32_le impl; // 0xB (NVGPU_GPU_IMPL_GM20B) 28 u32_le impl; // 0xB (NVGPU_GPU_IMPL_GM20B)
@@ -159,17 +140,21 @@ private:
159 }; 140 };
160 static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); 141 static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size");
161 142
162 u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, 143 NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
163 std::vector<u8>& output2, IoctlVersion version); 144 NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
164 u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, std::vector<u8>& output2, 145 std::vector<u8>& inline_output);
165 IoctlVersion version); 146
166 u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); 147 NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
167 u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); 148 NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
168 u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output); 149 std::vector<u8>& inline_output);
169 u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output); 150
170 u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); 151 NvResult GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
171 u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output); 152 NvResult ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
172 u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); 153 NvResult ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
154 NvResult ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
155 NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
156 NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
157 NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
173}; 158};
174 159
175} // namespace Service::Nvidia::Devices 160} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 152019548..af8b3d9f1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -23,107 +23,132 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
23 23
24nvhost_gpu::~nvhost_gpu() = default; 24nvhost_gpu::~nvhost_gpu() = default;
25 25
26u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 26NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
27 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 27 switch (command.group) {
28 IoctlVersion version) { 28 case 0x0:
29 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 29 switch (command.cmd) {
30 command.raw, input.size(), output.size()); 30 case 0x3:
31 31 return GetWaitbase(input, output);
32 switch (static_cast<IoctlCommand>(command.raw)) { 32 default:
33 case IoctlCommand::IocSetNVMAPfdCommand: 33 break;
34 return SetNVMAPfd(input, output); 34 }
35 case IoctlCommand::IocSetClientDataCommand: 35 break;
36 return SetClientData(input, output); 36 case 'H':
37 case IoctlCommand::IocGetClientDataCommand: 37 switch (command.cmd) {
38 return GetClientData(input, output); 38 case 0x1:
39 case IoctlCommand::IocZCullBind: 39 return SetNVMAPfd(input, output);
40 return ZCullBind(input, output); 40 case 0x3:
41 case IoctlCommand::IocSetErrorNotifierCommand: 41 return ChannelSetTimeout(input, output);
42 return SetErrorNotifier(input, output); 42 case 0x8:
43 case IoctlCommand::IocChannelSetPriorityCommand: 43 return SubmitGPFIFOBase(input, output, false);
44 return SetChannelPriority(input, output); 44 case 0x9:
45 case IoctlCommand::IocAllocGPFIFOEx2Command: 45 return AllocateObjectContext(input, output);
46 return AllocGPFIFOEx2(input, output); 46 case 0xb:
47 case IoctlCommand::IocAllocObjCtxCommand: 47 return ZCullBind(input, output);
48 return AllocateObjectContext(input, output); 48 case 0xc:
49 case IoctlCommand::IocChannelGetWaitbaseCommand: 49 return SetErrorNotifier(input, output);
50 return GetWaitbase(input, output); 50 case 0xd:
51 case IoctlCommand::IocChannelSetTimeoutCommand: 51 return SetChannelPriority(input, output);
52 return ChannelSetTimeout(input, output); 52 case 0x1a:
53 case IoctlCommand::IocChannelSetTimeslice: 53 return AllocGPFIFOEx2(input, output);
54 return ChannelSetTimeslice(input, output); 54 case 0x1b:
55 default: 55 return SubmitGPFIFOBase(input, output, true);
56 case 0x1d:
57 return ChannelSetTimeslice(input, output);
58 default:
59 break;
60 }
61 break;
62 case 'G':
63 switch (command.cmd) {
64 case 0x14:
65 return SetClientData(input, output);
66 case 0x15:
67 return GetClientData(input, output);
68 default:
69 break;
70 }
56 break; 71 break;
57 } 72 }
73 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
74 return NvResult::NotImplemented;
75};
58 76
59 if (command.group == NVGPU_IOCTL_MAGIC) { 77NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
60 if (command.cmd == NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO) { 78 const std::vector<u8>& inline_input, std::vector<u8>& output) {
61 return SubmitGPFIFO(input, output); 79 switch (command.group) {
62 } 80 case 'H':
63 if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) { 81 switch (command.cmd) {
64 return KickoffPB(input, output, input2, version); 82 case 0x1b:
83 return SubmitGPFIFOBase(input, inline_input, output);
65 } 84 }
85 break;
66 } 86 }
87 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
88 return NvResult::NotImplemented;
89}
67 90
68 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 91NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
69 return 0; 92 std::vector<u8>& inline_output) {
70}; 93 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
94 return NvResult::NotImplemented;
95}
71 96
72u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { 97NvResult nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
73 IoctlSetNvmapFD params{}; 98 IoctlSetNvmapFD params{};
74 std::memcpy(&params, input.data(), input.size()); 99 std::memcpy(&params, input.data(), input.size());
75 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 100 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
76 101
77 nvmap_fd = params.nvmap_fd; 102 nvmap_fd = params.nvmap_fd;
78 return 0; 103 return NvResult::Success;
79} 104}
80 105
81u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) { 106NvResult nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
82 LOG_DEBUG(Service_NVDRV, "called"); 107 LOG_DEBUG(Service_NVDRV, "called");
83 108
84 IoctlClientData params{}; 109 IoctlClientData params{};
85 std::memcpy(&params, input.data(), input.size()); 110 std::memcpy(&params, input.data(), input.size());
86 user_data = params.data; 111 user_data = params.data;
87 return 0; 112 return NvResult::Success;
88} 113}
89 114
90u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) { 115NvResult nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
91 LOG_DEBUG(Service_NVDRV, "called"); 116 LOG_DEBUG(Service_NVDRV, "called");
92 117
93 IoctlClientData params{}; 118 IoctlClientData params{};
94 std::memcpy(&params, input.data(), input.size()); 119 std::memcpy(&params, input.data(), input.size());
95 params.data = user_data; 120 params.data = user_data;
96 std::memcpy(output.data(), &params, output.size()); 121 std::memcpy(output.data(), &params, output.size());
97 return 0; 122 return NvResult::Success;
98} 123}
99 124
100u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) { 125NvResult nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) {
101 std::memcpy(&zcull_params, input.data(), input.size()); 126 std::memcpy(&zcull_params, input.data(), input.size());
102 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, 127 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
103 zcull_params.mode); 128 zcull_params.mode);
104 129
105 std::memcpy(output.data(), &zcull_params, output.size()); 130 std::memcpy(output.data(), &zcull_params, output.size());
106 return 0; 131 return NvResult::Success;
107} 132}
108 133
109u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) { 134NvResult nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) {
110 IoctlSetErrorNotifier params{}; 135 IoctlSetErrorNotifier params{};
111 std::memcpy(&params, input.data(), input.size()); 136 std::memcpy(&params, input.data(), input.size());
112 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, 137 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
113 params.size, params.mem); 138 params.size, params.mem);
114 139
115 std::memcpy(output.data(), &params, output.size()); 140 std::memcpy(output.data(), &params, output.size());
116 return 0; 141 return NvResult::Success;
117} 142}
118 143
119u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) { 144NvResult nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) {
120 std::memcpy(&channel_priority, input.data(), input.size()); 145 std::memcpy(&channel_priority, input.data(), input.size());
121 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); 146 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
122 147
123 return 0; 148 return NvResult::Success;
124} 149}
125 150
126u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) { 151NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) {
127 IoctlAllocGpfifoEx2 params{}; 152 IoctlAllocGpfifoEx2 params{};
128 std::memcpy(&params, input.data(), input.size()); 153 std::memcpy(&params, input.data(), input.size());
129 LOG_WARNING(Service_NVDRV, 154 LOG_WARNING(Service_NVDRV,
@@ -137,10 +162,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
137 params.fence_out = channel_fence; 162 params.fence_out = channel_fence;
138 163
139 std::memcpy(output.data(), &params, output.size()); 164 std::memcpy(output.data(), &params, output.size());
140 return 0; 165 return NvResult::Success;
141} 166}
142 167
143u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) { 168NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) {
144 IoctlAllocObjCtx params{}; 169 IoctlAllocObjCtx params{};
145 std::memcpy(&params, input.data(), input.size()); 170 std::memcpy(&params, input.data(), input.size());
146 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, 171 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
@@ -148,7 +173,7 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
148 173
149 params.obj_id = 0x0; 174 params.obj_id = 0x0;
150 std::memcpy(output.data(), &params, output.size()); 175 std::memcpy(output.data(), &params, output.size());
151 return 0; 176 return NvResult::Success;
152} 177}
153 178
154static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) { 179static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
@@ -192,8 +217,8 @@ static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence
192 return result; 217 return result;
193} 218}
194 219
195u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, 220NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
196 Tegra::CommandList&& entries) { 221 Tegra::CommandList&& entries) {
197 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, 222 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
198 params.num_entries, params.flags.raw); 223 params.num_entries, params.flags.raw);
199 224
@@ -214,7 +239,6 @@ u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& out
214 params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id); 239 params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id);
215 } 240 }
216 241
217 entries.RefreshIntegrityChecks(gpu);
218 gpu.PushGPUEntries(std::move(entries)); 242 gpu.PushGPUEntries(std::move(entries));
219 243
220 if (params.flags.add_increment.Value()) { 244 if (params.flags.add_increment.Value()) {
@@ -228,69 +252,70 @@ u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& out
228 } 252 }
229 253
230 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo)); 254 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
231 return 0; 255 return NvResult::Success;
232} 256}
233 257
234u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { 258NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output,
259 bool kickoff) {
235 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 260 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
236 UNIMPLEMENTED(); 261 UNIMPLEMENTED();
262 return NvResult::InvalidSize;
237 } 263 }
238 IoctlSubmitGpfifo params{}; 264 IoctlSubmitGpfifo params{};
239 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo)); 265 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
240
241 Tegra::CommandList entries(params.num_entries); 266 Tegra::CommandList entries(params.num_entries);
242 std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)], 267
243 params.num_entries * sizeof(Tegra::CommandListHeader)); 268 if (kickoff) {
269 system.Memory().ReadBlock(params.address, entries.command_lists.data(),
270 params.num_entries * sizeof(Tegra::CommandListHeader));
271 } else {
272 std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)],
273 params.num_entries * sizeof(Tegra::CommandListHeader));
274 }
244 275
245 return SubmitGPFIFOImpl(params, output, std::move(entries)); 276 return SubmitGPFIFOImpl(params, output, std::move(entries));
246} 277}
247 278
248u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, 279NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input,
249 const std::vector<u8>& input2, IoctlVersion version) { 280 const std::vector<u8>& input_inline,
281 std::vector<u8>& output) {
250 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 282 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
251 UNIMPLEMENTED(); 283 UNIMPLEMENTED();
284 return NvResult::InvalidSize;
252 } 285 }
253 IoctlSubmitGpfifo params{}; 286 IoctlSubmitGpfifo params{};
254 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo)); 287 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
255
256 Tegra::CommandList entries(params.num_entries); 288 Tegra::CommandList entries(params.num_entries);
257 if (version == IoctlVersion::Version2) { 289 std::memcpy(entries.command_lists.data(), input_inline.data(), input_inline.size());
258 std::memcpy(entries.command_lists.data(), input2.data(),
259 params.num_entries * sizeof(Tegra::CommandListHeader));
260 } else {
261 system.Memory().ReadBlock(params.address, entries.command_lists.data(),
262 params.num_entries * sizeof(Tegra::CommandListHeader));
263 }
264
265 return SubmitGPFIFOImpl(params, output, std::move(entries)); 290 return SubmitGPFIFOImpl(params, output, std::move(entries));
266} 291}
267 292
268u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { 293NvResult nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
269 IoctlGetWaitbase params{}; 294 IoctlGetWaitbase params{};
270 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 295 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
271 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); 296 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
272 297
273 params.value = 0; // Seems to be hard coded at 0 298 params.value = 0; // Seems to be hard coded at 0
274 std::memcpy(output.data(), &params, output.size()); 299 std::memcpy(output.data(), &params, output.size());
275 return 0; 300 return NvResult::Success;
276} 301}
277 302
278u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) { 303NvResult nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
279 IoctlChannelSetTimeout params{}; 304 IoctlChannelSetTimeout params{};
280 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout)); 305 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
281 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); 306 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
282 307
283 return 0; 308 return NvResult::Success;
284} 309}
285 310
286u32 nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) { 311NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) {
287 IoctlSetTimeslice params{}; 312 IoctlSetTimeslice params{};
288 std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice)); 313 std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice));
289 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); 314 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice);
290 315
291 channel_timeslice = params.timeslice; 316 channel_timeslice = params.timeslice;
292 317
293 return 0; 318 return NvResult::Success;
294} 319}
295 320
296} // namespace Service::Nvidia::Devices 321} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index a252fc06d..e0298b4fe 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -20,43 +20,19 @@ class SyncpointManager;
20namespace Service::Nvidia::Devices { 20namespace Service::Nvidia::Devices {
21 21
22class nvmap; 22class nvmap;
23constexpr u32 NVGPU_IOCTL_MAGIC('H');
24constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8);
25constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
26
27class nvhost_gpu final : public nvdevice { 23class nvhost_gpu final : public nvdevice {
28public: 24public:
29 explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev, 25 explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
30 SyncpointManager& syncpoint_manager); 26 SyncpointManager& syncpoint_manager);
31 ~nvhost_gpu() override; 27 ~nvhost_gpu() override;
32 28
33 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 29 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
34 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 30 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
35 IoctlVersion version) override; 31 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
32 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
33 std::vector<u8>& inline_output) override;
36 34
37private: 35private:
38 enum class IoctlCommand : u32_le {
39 IocSetNVMAPfdCommand = 0x40044801,
40 IocAllocGPFIFOCommand = 0x40084805,
41 IocSetClientDataCommand = 0x40084714,
42 IocGetClientDataCommand = 0x80084715,
43 IocZCullBind = 0xc010480b,
44 IocSetErrorNotifierCommand = 0xC018480C,
45 IocChannelSetPriorityCommand = 0x4004480D,
46 IocEnableCommand = 0x0000480E,
47 IocDisableCommand = 0x0000480F,
48 IocPreemptCommand = 0x00004810,
49 IocForceResetCommand = 0x00004811,
50 IocEventIdControlCommand = 0x40084812,
51 IocGetErrorNotificationCommand = 0xC0104817,
52 IocAllocGPFIFOExCommand = 0x40204818,
53 IocAllocGPFIFOEx2Command = 0xC020481A,
54 IocAllocObjCtxCommand = 0xC0104809,
55 IocChannelGetWaitbaseCommand = 0xC0080003,
56 IocChannelSetTimeoutCommand = 0x40044803,
57 IocChannelSetTimeslice = 0xC004481D,
58 };
59
60 enum class CtxObjects : u32_le { 36 enum class CtxObjects : u32_le {
61 Ctx2D = 0x902D, 37 Ctx2D = 0x902D,
62 Ctx3D = 0xB197, 38 Ctx3D = 0xB197,
@@ -67,63 +43,63 @@ private:
67 }; 43 };
68 44
69 struct IoctlSetNvmapFD { 45 struct IoctlSetNvmapFD {
70 u32_le nvmap_fd; 46 s32_le nvmap_fd{};
71 }; 47 };
72 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); 48 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
73 49
74 struct IoctlChannelSetTimeout { 50 struct IoctlChannelSetTimeout {
75 u32_le timeout; 51 u32_le timeout{};
76 }; 52 };
77 static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size"); 53 static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size");
78 54
79 struct IoctlAllocGPFIFO { 55 struct IoctlAllocGPFIFO {
80 u32_le num_entries; 56 u32_le num_entries{};
81 u32_le flags; 57 u32_le flags{};
82 }; 58 };
83 static_assert(sizeof(IoctlAllocGPFIFO) == 8, "IoctlAllocGPFIFO is incorrect size"); 59 static_assert(sizeof(IoctlAllocGPFIFO) == 8, "IoctlAllocGPFIFO is incorrect size");
84 60
85 struct IoctlClientData { 61 struct IoctlClientData {
86 u64_le data; 62 u64_le data{};
87 }; 63 };
88 static_assert(sizeof(IoctlClientData) == 8, "IoctlClientData is incorrect size"); 64 static_assert(sizeof(IoctlClientData) == 8, "IoctlClientData is incorrect size");
89 65
90 struct IoctlZCullBind { 66 struct IoctlZCullBind {
91 u64_le gpu_va; 67 u64_le gpu_va{};
92 u32_le mode; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf 68 u32_le mode{}; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf
93 INSERT_PADDING_WORDS(1); 69 INSERT_PADDING_WORDS(1);
94 }; 70 };
95 static_assert(sizeof(IoctlZCullBind) == 16, "IoctlZCullBind is incorrect size"); 71 static_assert(sizeof(IoctlZCullBind) == 16, "IoctlZCullBind is incorrect size");
96 72
97 struct IoctlSetErrorNotifier { 73 struct IoctlSetErrorNotifier {
98 u64_le offset; 74 u64_le offset{};
99 u64_le size; 75 u64_le size{};
100 u32_le mem; // nvmap object handle 76 u32_le mem{}; // nvmap object handle
101 INSERT_PADDING_WORDS(1); 77 INSERT_PADDING_WORDS(1);
102 }; 78 };
103 static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size"); 79 static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size");
104 80
105 struct IoctlChannelSetPriority { 81 struct IoctlChannelSetPriority {
106 u32_le priority; 82 u32_le priority{};
107 }; 83 };
108 static_assert(sizeof(IoctlChannelSetPriority) == 4, 84 static_assert(sizeof(IoctlChannelSetPriority) == 4,
109 "IoctlChannelSetPriority is incorrect size"); 85 "IoctlChannelSetPriority is incorrect size");
110 86
111 struct IoctlSetTimeslice { 87 struct IoctlSetTimeslice {
112 u32_le timeslice; 88 u32_le timeslice{};
113 }; 89 };
114 static_assert(sizeof(IoctlSetTimeslice) == 4, "IoctlSetTimeslice is incorrect size"); 90 static_assert(sizeof(IoctlSetTimeslice) == 4, "IoctlSetTimeslice is incorrect size");
115 91
116 struct IoctlEventIdControl { 92 struct IoctlEventIdControl {
117 u32_le cmd; // 0=disable, 1=enable, 2=clear 93 u32_le cmd{}; // 0=disable, 1=enable, 2=clear
118 u32_le id; 94 u32_le id{};
119 }; 95 };
120 static_assert(sizeof(IoctlEventIdControl) == 8, "IoctlEventIdControl is incorrect size"); 96 static_assert(sizeof(IoctlEventIdControl) == 8, "IoctlEventIdControl is incorrect size");
121 97
122 struct IoctlGetErrorNotification { 98 struct IoctlGetErrorNotification {
123 u64_le timestamp; 99 u64_le timestamp{};
124 u32_le info32; 100 u32_le info32{};
125 u16_le info16; 101 u16_le info16{};
126 u16_le status; // always 0xFFFF 102 u16_le status{}; // always 0xFFFF
127 }; 103 };
128 static_assert(sizeof(IoctlGetErrorNotification) == 16, 104 static_assert(sizeof(IoctlGetErrorNotification) == 16,
129 "IoctlGetErrorNotification is incorrect size"); 105 "IoctlGetErrorNotification is incorrect size");
@@ -131,39 +107,39 @@ private:
131 static_assert(sizeof(Fence) == 8, "Fence is incorrect size"); 107 static_assert(sizeof(Fence) == 8, "Fence is incorrect size");
132 108
133 struct IoctlAllocGpfifoEx { 109 struct IoctlAllocGpfifoEx {
134 u32_le num_entries; 110 u32_le num_entries{};
135 u32_le flags; 111 u32_le flags{};
136 u32_le unk0; 112 u32_le unk0{};
137 u32_le unk1; 113 u32_le unk1{};
138 u32_le unk2; 114 u32_le unk2{};
139 u32_le unk3; 115 u32_le unk3{};
140 u32_le unk4; 116 u32_le unk4{};
141 u32_le unk5; 117 u32_le unk5{};
142 }; 118 };
143 static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size"); 119 static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size");
144 120
145 struct IoctlAllocGpfifoEx2 { 121 struct IoctlAllocGpfifoEx2 {
146 u32_le num_entries; // in 122 u32_le num_entries{}; // in
147 u32_le flags; // in 123 u32_le flags{}; // in
148 u32_le unk0; // in (1 works) 124 u32_le unk0{}; // in (1 works)
149 Fence fence_out; // out 125 Fence fence_out{}; // out
150 u32_le unk1; // in 126 u32_le unk1{}; // in
151 u32_le unk2; // in 127 u32_le unk2{}; // in
152 u32_le unk3; // in 128 u32_le unk3{}; // in
153 }; 129 };
154 static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size"); 130 static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size");
155 131
156 struct IoctlAllocObjCtx { 132 struct IoctlAllocObjCtx {
157 u32_le class_num; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA, 133 u32_le class_num{}; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA,
158 // 0xB06F=channel_gpfifo 134 // 0xB06F=channel_gpfifo
159 u32_le flags; 135 u32_le flags{};
160 u64_le obj_id; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported 136 u64_le obj_id{}; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported
161 }; 137 };
162 static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size"); 138 static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size");
163 139
164 struct IoctlSubmitGpfifo { 140 struct IoctlSubmitGpfifo {
165 u64_le address; // pointer to gpfifo entry structs 141 u64_le address{}; // pointer to gpfifo entry structs
166 u32_le num_entries; // number of fence objects being submitted 142 u32_le num_entries{}; // number of fence objects being submitted
167 union { 143 union {
168 u32_le raw; 144 u32_le raw;
169 BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list 145 BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list
@@ -172,7 +148,7 @@ private:
172 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt 148 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
173 BitField<8, 1, u32_le> increment; // increment the returned fence 149 BitField<8, 1, u32_le> increment; // increment the returned fence
174 } flags; 150 } flags;
175 Fence fence_out; // returned new fence object for others to wait on 151 Fence fence_out{}; // returned new fence object for others to wait on
176 152
177 u32 AddIncrementValue() const { 153 u32 AddIncrementValue() const {
178 return flags.add_increment.Value() << 1; 154 return flags.add_increment.Value() << 1;
@@ -182,33 +158,34 @@ private:
182 "IoctlSubmitGpfifo is incorrect size"); 158 "IoctlSubmitGpfifo is incorrect size");
183 159
184 struct IoctlGetWaitbase { 160 struct IoctlGetWaitbase {
185 u32 unknown; // seems to be ignored? Nintendo added this 161 u32 unknown{}; // seems to be ignored? Nintendo added this
186 u32 value; 162 u32 value{};
187 }; 163 };
188 static_assert(sizeof(IoctlGetWaitbase) == 8, "IoctlGetWaitbase is incorrect size"); 164 static_assert(sizeof(IoctlGetWaitbase) == 8, "IoctlGetWaitbase is incorrect size");
189 165
190 u32_le nvmap_fd{}; 166 s32_le nvmap_fd{};
191 u64_le user_data{}; 167 u64_le user_data{};
192 IoctlZCullBind zcull_params{}; 168 IoctlZCullBind zcull_params{};
193 u32_le channel_priority{}; 169 u32_le channel_priority{};
194 u32_le channel_timeslice{}; 170 u32_le channel_timeslice{};
195 171
196 u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); 172 NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
197 u32 SetClientData(const std::vector<u8>& input, std::vector<u8>& output); 173 NvResult SetClientData(const std::vector<u8>& input, std::vector<u8>& output);
198 u32 GetClientData(const std::vector<u8>& input, std::vector<u8>& output); 174 NvResult GetClientData(const std::vector<u8>& input, std::vector<u8>& output);
199 u32 ZCullBind(const std::vector<u8>& input, std::vector<u8>& output); 175 NvResult ZCullBind(const std::vector<u8>& input, std::vector<u8>& output);
200 u32 SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output); 176 NvResult SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output);
201 u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output); 177 NvResult SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
202 u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); 178 NvResult AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
203 u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); 179 NvResult AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
204 u32 SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, 180 NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
205 Tegra::CommandList&& entries); 181 Tegra::CommandList&& entries);
206 u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output); 182 NvResult SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output,
207 u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, 183 bool kickoff = false);
208 const std::vector<u8>& input2, IoctlVersion version); 184 NvResult SubmitGPFIFOBase(const std::vector<u8>& input, const std::vector<u8>& input_inline,
209 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); 185 std::vector<u8>& output);
210 u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); 186 NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
211 u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); 187 NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
188 NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
212 189
213 std::shared_ptr<nvmap> nvmap_dev; 190 std::shared_ptr<nvmap> nvmap_dev;
214 SyncpointManager& syncpoint_manager; 191 SyncpointManager& syncpoint_manager;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index b6df48360..d8735491c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -15,46 +15,58 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_de
15 : nvhost_nvdec_common(system, std::move(nvmap_dev)) {} 15 : nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
16nvhost_nvdec::~nvhost_nvdec() = default; 16nvhost_nvdec::~nvhost_nvdec() = default;
17 17
18u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 18NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
19 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 19 std::vector<u8>& output) {
20 IoctlVersion version) { 20 switch (command.group) {
21 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 21 case 0x0:
22 command.raw, input.size(), output.size()); 22 switch (command.cmd) {
23 23 case 0x1:
24 switch (static_cast<IoctlCommand>(command.raw)) { 24 return Submit(input, output);
25 case IoctlCommand::IocSetNVMAPfdCommand: 25 case 0x2:
26 return SetNVMAPfd(input); 26 return GetSyncpoint(input, output);
27 case IoctlCommand::IocSubmit: 27 case 0x3:
28 return Submit(input, output); 28 return GetWaitbase(input, output);
29 case IoctlCommand::IocGetSyncpoint: 29 case 0x7:
30 return GetSyncpoint(input, output); 30 return SetSubmitTimeout(input, output);
31 case IoctlCommand::IocGetWaitbase: 31 case 0x9:
32 return GetWaitbase(input, output); 32 return MapBuffer(input, output);
33 case IoctlCommand::IocMapBuffer: 33 case 0xa: {
34 case IoctlCommand::IocMapBuffer2: 34 if (command.length == 0x1c) {
35 case IoctlCommand::IocMapBuffer3: 35 LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
36 case IoctlCommand::IocMapBufferEx: 36 Tegra::ChCommandHeaderList cmdlist(1);
37 return MapBuffer(input, output); 37 cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F};
38 case IoctlCommand::IocUnmapBufferEx: { 38 system.GPU().PushCommandBuffer(cmdlist);
39 // This command is sent when the video stream has ended, flush all video contexts 39 }
40 // This is usually sent in the folowing order: vic, nvdec, vic. 40 return UnmapBuffer(input, output);
41 // Inform the GPU to clear any remaining nvdec buffers when this is detected. 41 }
42 LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); 42 default:
43 Tegra::ChCommandHeaderList cmdlist(1); 43 break;
44 cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F}; 44 }
45 system.GPU().PushCommandBuffer(cmdlist); 45 break;
46 [[fallthrough]]; // fallthrough to unmap buffers 46 case 'H':
47 }; 47 switch (command.cmd) {
48 case IoctlCommand::IocUnmapBuffer: 48 case 0x1:
49 case IoctlCommand::IocUnmapBuffer2: 49 return SetNVMAPfd(input);
50 case IoctlCommand::IocUnmapBuffer3: 50 default:
51 return UnmapBuffer(input, output); 51 break;
52 case IoctlCommand::IocSetSubmitTimeout: 52 }
53 return SetSubmitTimeout(input, output); 53 break;
54 } 54 }
55 55
56 UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw); 56 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
57 return 0; 57 return NvResult::NotImplemented;
58}
59
60NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input,
61 const std::vector<u8>& inline_input, std::vector<u8>& output) {
62 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
63 return NvResult::NotImplemented;
64}
65
66NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
67 std::vector<u8>& inline_output) {
68 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
69 return NvResult::NotImplemented;
58} 70}
59 71
60} // namespace Service::Nvidia::Devices 72} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 102777ddd..79b8b6de1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -14,26 +14,11 @@ public:
14 explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 14 explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
15 ~nvhost_nvdec() override; 15 ~nvhost_nvdec() override;
16 16
17 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 17 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
18 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 18 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
19 IoctlVersion version) override; 19 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
20 20 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
21private: 21 std::vector<u8>& inline_output) override;
22 enum class IoctlCommand : u32_le {
23 IocSetNVMAPfdCommand = 0x40044801,
24 IocSubmit = 0xC0400001,
25 IocGetSyncpoint = 0xC0080002,
26 IocGetWaitbase = 0xC0080003,
27 IocMapBuffer = 0xC01C0009,
28 IocMapBuffer2 = 0xC16C0009,
29 IocMapBuffer3 = 0xC15C0009,
30 IocMapBufferEx = 0xC0A40009,
31 IocUnmapBuffer = 0xC0A4000A,
32 IocUnmapBuffer2 = 0xC16C000A,
33 IocUnmapBufferEx = 0xC01C000A,
34 IocUnmapBuffer3 = 0xC15C000A,
35 IocSetSubmitTimeout = 0x40040007,
36 };
37}; 22};
38 23
39} // namespace Service::Nvidia::Devices 24} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index 30f03f845..b49cecb42 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -36,26 +36,20 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s
36} 36}
37} // Anonymous namespace 37} // Anonymous namespace
38 38
39namespace NvErrCodes {
40constexpr u32 Success{};
41[[maybe_unused]] constexpr u32 OutOfMemory{static_cast<u32>(-12)};
42constexpr u32 InvalidInput{static_cast<u32>(-22)};
43} // namespace NvErrCodes
44
45nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) 39nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
46 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 40 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
47nvhost_nvdec_common::~nvhost_nvdec_common() = default; 41nvhost_nvdec_common::~nvhost_nvdec_common() = default;
48 42
49u32 nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { 43NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
50 IoctlSetNvmapFD params{}; 44 IoctlSetNvmapFD params{};
51 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD)); 45 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
52 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 46 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
53 47
54 nvmap_fd = params.nvmap_fd; 48 nvmap_fd = params.nvmap_fd;
55 return 0; 49 return NvResult::Success;
56} 50}
57 51
58u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) { 52NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
59 IoctlSubmit params{}; 53 IoctlSubmit params{};
60 std::memcpy(&params, input.data(), sizeof(IoctlSubmit)); 54 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
61 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); 55 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
@@ -83,12 +77,12 @@ u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& o
83 77
84 for (const auto& cmd_buffer : command_buffers) { 78 for (const auto& cmd_buffer : command_buffers) {
85 auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); 79 auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
86 ASSERT_OR_EXECUTE(object, return NvErrCodes::InvalidInput;); 80 ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
87 const auto map = FindBufferMap(object->dma_map_addr); 81 const auto map = FindBufferMap(object->dma_map_addr);
88 if (!map) { 82 if (!map) {
89 LOG_ERROR(Service_NVDRV, "Tried to submit an invalid offset 0x{:X} dma 0x{:X}", 83 LOG_ERROR(Service_NVDRV, "Tried to submit an invalid offset 0x{:X} dma 0x{:X}",
90 object->addr, object->dma_map_addr); 84 object->addr, object->dma_map_addr);
91 return 0; 85 return NvResult::Success;
92 } 86 }
93 Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); 87 Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
94 gpu.MemoryManager().ReadBlock(map->StartAddr() + cmd_buffer.offset, cmdlist.data(), 88 gpu.MemoryManager().ReadBlock(map->StartAddr() + cmd_buffer.offset, cmdlist.data(),
@@ -105,10 +99,10 @@ u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& o
105 offset = WriteVectors(output, syncpt_increments, offset); 99 offset = WriteVectors(output, syncpt_increments, offset);
106 offset = WriteVectors(output, wait_checks, offset); 100 offset = WriteVectors(output, wait_checks, offset);
107 101
108 return NvErrCodes::Success; 102 return NvResult::Success;
109} 103}
110 104
111u32 nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) { 105NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
112 IoctlGetSyncpoint params{}; 106 IoctlGetSyncpoint params{};
113 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint)); 107 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
114 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); 108 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
@@ -118,18 +112,18 @@ u32 nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<
118 params.value = 0; 112 params.value = 0;
119 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint)); 113 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
120 114
121 return NvErrCodes::Success; 115 return NvResult::Success;
122} 116}
123 117
124u32 nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { 118NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
125 IoctlGetWaitbase params{}; 119 IoctlGetWaitbase params{};
126 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 120 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
127 params.value = 0; // Seems to be hard coded at 0 121 params.value = 0; // Seems to be hard coded at 0
128 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase)); 122 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
129 return 0; 123 return NvResult::Success;
130} 124}
131 125
132u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 126NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
133 IoctlMapBuffer params{}; 127 IoctlMapBuffer params{};
134 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 128 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
135 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 129 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -143,7 +137,7 @@ u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>
143 if (!object) { 137 if (!object) {
144 LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle); 138 LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
145 std::memcpy(output.data(), &params, output.size()); 139 std::memcpy(output.data(), &params, output.size());
146 return NvErrCodes::InvalidInput; 140 return NvResult::InvalidState;
147 } 141 }
148 if (object->dma_map_addr == 0) { 142 if (object->dma_map_addr == 0) {
149 // NVDEC and VIC memory is in the 32-bit address space 143 // NVDEC and VIC memory is in the 32-bit address space
@@ -165,10 +159,10 @@ u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>
165 std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(), 159 std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
166 cmd_buffer_handles.size() * sizeof(MapBufferEntry)); 160 cmd_buffer_handles.size() * sizeof(MapBufferEntry));
167 161
168 return NvErrCodes::Success; 162 return NvResult::Success;
169} 163}
170 164
171u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 165NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
172 IoctlMapBuffer params{}; 166 IoctlMapBuffer params{};
173 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 167 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
174 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 168 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -181,7 +175,7 @@ u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u
181 if (!object) { 175 if (!object) {
182 LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle); 176 LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
183 std::memcpy(output.data(), &params, output.size()); 177 std::memcpy(output.data(), &params, output.size());
184 return NvErrCodes::InvalidInput; 178 return NvResult::InvalidState;
185 } 179 }
186 if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) { 180 if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
187 gpu.MemoryManager().Unmap(object->dma_map_addr, *size); 181 gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
@@ -193,13 +187,14 @@ u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u
193 object->dma_map_addr = 0; 187 object->dma_map_addr = 0;
194 } 188 }
195 std::memset(output.data(), 0, output.size()); 189 std::memset(output.data(), 0, output.size());
196 return NvErrCodes::Success; 190 return NvResult::Success;
197} 191}
198 192
199u32 nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output) { 193NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
194 std::vector<u8>& output) {
200 std::memcpy(&submit_timeout, input.data(), input.size()); 195 std::memcpy(&submit_timeout, input.data(), input.size());
201 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 196 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
202 return NvErrCodes::Success; 197 return NvResult::Success;
203} 198}
204 199
205std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap( 200std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap(
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index c249c5349..86ba3a4d1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -18,9 +18,37 @@ public:
18 explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 18 explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
19 ~nvhost_nvdec_common() override; 19 ~nvhost_nvdec_common() override;
20 20
21 virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 21 /**
22 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 22 * Handles an ioctl1 request.
23 IoctlVersion version) = 0; 23 * @param command The ioctl command id.
24 * @param input A buffer containing the input data for the ioctl.
25 * @param output A buffer where the output data will be written to.
26 * @returns The result code of the ioctl.
27 */
28 virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
29 std::vector<u8>& output) = 0;
30
31 /**
32 * Handles an ioctl2 request.
33 * @param command The ioctl command id.
34 * @param input A buffer containing the input data for the ioctl.
35 * @param inline_input A buffer containing the input data for the ioctl which has been inlined.
36 * @param output A buffer where the output data will be written to.
37 * @returns The result code of the ioctl.
38 */
39 virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
40 const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
41
42 /**
43 * Handles an ioctl3 request.
44 * @param command The ioctl command id.
45 * @param input A buffer containing the input data for the ioctl.
46 * @param output A buffer where the output data will be written to.
47 * @param inline_output A buffer where the inlined output data will be written to.
48 * @returns The result code of the ioctl.
49 */
50 virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
51 std::vector<u8>& inline_output) = 0;
24 52
25protected: 53protected:
26 class BufferMap final { 54 class BufferMap final {
@@ -63,102 +91,102 @@ protected:
63 }; 91 };
64 92
65 struct IoctlSetNvmapFD { 93 struct IoctlSetNvmapFD {
66 u32_le nvmap_fd; 94 s32_le nvmap_fd{};
67 }; 95 };
68 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); 96 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
69 97
70 struct IoctlSubmitCommandBuffer { 98 struct IoctlSubmitCommandBuffer {
71 u32_le id; 99 u32_le id{};
72 u32_le offset; 100 u32_le offset{};
73 u32_le count; 101 u32_le count{};
74 }; 102 };
75 static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC, 103 static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC,
76 "IoctlSubmitCommandBuffer is incorrect size"); 104 "IoctlSubmitCommandBuffer is incorrect size");
77 struct IoctlSubmit { 105 struct IoctlSubmit {
78 u32_le cmd_buffer_count; 106 u32_le cmd_buffer_count{};
79 u32_le relocation_count; 107 u32_le relocation_count{};
80 u32_le syncpoint_count; 108 u32_le syncpoint_count{};
81 u32_le fence_count; 109 u32_le fence_count{};
82 }; 110 };
83 static_assert(sizeof(IoctlSubmit) == 0x10, "IoctlSubmit has incorrect size"); 111 static_assert(sizeof(IoctlSubmit) == 0x10, "IoctlSubmit has incorrect size");
84 112
85 struct CommandBuffer { 113 struct CommandBuffer {
86 s32 memory_id; 114 s32 memory_id{};
87 u32 offset; 115 u32 offset{};
88 s32 word_count; 116 s32 word_count{};
89 }; 117 };
90 static_assert(sizeof(CommandBuffer) == 0xC, "CommandBuffer has incorrect size"); 118 static_assert(sizeof(CommandBuffer) == 0xC, "CommandBuffer has incorrect size");
91 119
92 struct Reloc { 120 struct Reloc {
93 s32 cmdbuffer_memory; 121 s32 cmdbuffer_memory{};
94 s32 cmdbuffer_offset; 122 s32 cmdbuffer_offset{};
95 s32 target; 123 s32 target{};
96 s32 target_offset; 124 s32 target_offset{};
97 }; 125 };
98 static_assert(sizeof(Reloc) == 0x10, "CommandBuffer has incorrect size"); 126 static_assert(sizeof(Reloc) == 0x10, "CommandBuffer has incorrect size");
99 127
100 struct SyncptIncr { 128 struct SyncptIncr {
101 u32 id; 129 u32 id{};
102 u32 increments; 130 u32 increments{};
103 }; 131 };
104 static_assert(sizeof(SyncptIncr) == 0x8, "CommandBuffer has incorrect size"); 132 static_assert(sizeof(SyncptIncr) == 0x8, "CommandBuffer has incorrect size");
105 133
106 struct Fence { 134 struct Fence {
107 u32 id; 135 u32 id{};
108 u32 value; 136 u32 value{};
109 }; 137 };
110 static_assert(sizeof(Fence) == 0x8, "CommandBuffer has incorrect size"); 138 static_assert(sizeof(Fence) == 0x8, "CommandBuffer has incorrect size");
111 139
112 struct IoctlGetSyncpoint { 140 struct IoctlGetSyncpoint {
113 // Input 141 // Input
114 u32_le param; 142 u32_le param{};
115 // Output 143 // Output
116 u32_le value; 144 u32_le value{};
117 }; 145 };
118 static_assert(sizeof(IoctlGetSyncpoint) == 8, "IocGetIdParams has wrong size"); 146 static_assert(sizeof(IoctlGetSyncpoint) == 8, "IocGetIdParams has wrong size");
119 147
120 struct IoctlGetWaitbase { 148 struct IoctlGetWaitbase {
121 u32_le unknown; // seems to be ignored? Nintendo added this 149 u32_le unknown{}; // seems to be ignored? Nintendo added this
122 u32_le value; 150 u32_le value{};
123 }; 151 };
124 static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size"); 152 static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size");
125 153
126 struct IoctlMapBuffer { 154 struct IoctlMapBuffer {
127 u32_le num_entries; 155 u32_le num_entries{};
128 u32_le data_address; // Ignored by the driver. 156 u32_le data_address{}; // Ignored by the driver.
129 u32_le attach_host_ch_das; 157 u32_le attach_host_ch_das{};
130 }; 158 };
131 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size"); 159 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
132 160
133 struct IocGetIdParams { 161 struct IocGetIdParams {
134 // Input 162 // Input
135 u32_le param; 163 u32_le param{};
136 // Output 164 // Output
137 u32_le value; 165 u32_le value{};
138 }; 166 };
139 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); 167 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
140 168
141 // Used for mapping and unmapping command buffers 169 // Used for mapping and unmapping command buffers
142 struct MapBufferEntry { 170 struct MapBufferEntry {
143 u32_le map_handle; 171 u32_le map_handle{};
144 u32_le map_address; 172 u32_le map_address{};
145 }; 173 };
146 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size"); 174 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
147 175
148 /// Ioctl command implementations 176 /// Ioctl command implementations
149 u32 SetNVMAPfd(const std::vector<u8>& input); 177 NvResult SetNVMAPfd(const std::vector<u8>& input);
150 u32 Submit(const std::vector<u8>& input, std::vector<u8>& output); 178 NvResult Submit(const std::vector<u8>& input, std::vector<u8>& output);
151 u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output); 179 NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
152 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); 180 NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
153 u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 181 NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
154 u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 182 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
155 u32 SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); 183 NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
156 184
157 std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const; 185 std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
158 void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); 186 void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
159 std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr); 187 std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
160 188
161 u32_le nvmap_fd{}; 189 s32_le nvmap_fd{};
162 u32_le submit_timeout{}; 190 u32_le submit_timeout{};
163 std::shared_ptr<nvmap> nvmap_dev; 191 std::shared_ptr<nvmap> nvmap_dev;
164 192
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index 96e7b7dab..2d06955c0 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -13,28 +13,44 @@ namespace Service::Nvidia::Devices {
13nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {} 13nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
14nvhost_nvjpg::~nvhost_nvjpg() = default; 14nvhost_nvjpg::~nvhost_nvjpg() = default;
15 15
16u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 16NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input,
17 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 17 std::vector<u8>& output) {
18 IoctlVersion version) { 18 switch (command.group) {
19 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 19 case 'H':
20 command.raw, input.size(), output.size()); 20 switch (command.cmd) {
21 21 case 0x1:
22 switch (static_cast<IoctlCommand>(command.raw)) { 22 return SetNVMAPfd(input, output);
23 case IoctlCommand::IocSetNVMAPfdCommand: 23 default:
24 return SetNVMAPfd(input, output); 24 break;
25 }
26 break;
27 default:
28 break;
25 } 29 }
26 30
27 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 31 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
28 return 0; 32 return NvResult::NotImplemented;
29} 33}
30 34
31u32 nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { 35NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input,
36 const std::vector<u8>& inline_input, std::vector<u8>& output) {
37 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
38 return NvResult::NotImplemented;
39}
40
41NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
42 std::vector<u8>& inline_output) {
43 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
44 return NvResult::NotImplemented;
45}
46
47NvResult nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
32 IoctlSetNvmapFD params{}; 48 IoctlSetNvmapFD params{};
33 std::memcpy(&params, input.data(), input.size()); 49 std::memcpy(&params, input.data(), input.size());
34 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 50 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
35 51
36 nvmap_fd = params.nvmap_fd; 52 nvmap_fd = params.nvmap_fd;
37 return 0; 53 return NvResult::Success;
38} 54}
39 55
40} // namespace Service::Nvidia::Devices 56} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 98dcac52f..43948d18d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -16,23 +16,21 @@ public:
16 explicit nvhost_nvjpg(Core::System& system); 16 explicit nvhost_nvjpg(Core::System& system);
17 ~nvhost_nvjpg() override; 17 ~nvhost_nvjpg() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 19 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
20 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 20 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
21 IoctlVersion version) override; 21 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
22 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
23 std::vector<u8>& inline_output) override;
22 24
23private: 25private:
24 enum class IoctlCommand : u32_le {
25 IocSetNVMAPfdCommand = 0x40044801,
26 };
27
28 struct IoctlSetNvmapFD { 26 struct IoctlSetNvmapFD {
29 u32_le nvmap_fd; 27 s32_le nvmap_fd{};
30 }; 28 };
31 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); 29 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
32 30
33 u32_le nvmap_fd{}; 31 s32_le nvmap_fd{};
34 32
35 u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); 33 NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
36}; 34};
37 35
38} // namespace Service::Nvidia::Devices 36} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 60db54d00..805fe86ae 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -15,36 +15,50 @@ nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
15 15
16nvhost_vic::~nvhost_vic() = default; 16nvhost_vic::~nvhost_vic() = default;
17 17
18u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 18NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
19 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 19 switch (command.group) {
20 IoctlVersion version) { 20 case 0x0:
21 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 21 switch (command.cmd) {
22 command.raw, input.size(), output.size()); 22 case 0x1:
23 23 return Submit(input, output);
24 switch (static_cast<IoctlCommand>(command.raw)) { 24 case 0x2:
25 case IoctlCommand::IocSetNVMAPfdCommand: 25 return GetSyncpoint(input, output);
26 return SetNVMAPfd(input); 26 case 0x3:
27 case IoctlCommand::IocSubmit: 27 return GetWaitbase(input, output);
28 return Submit(input, output); 28 case 0x9:
29 case IoctlCommand::IocGetSyncpoint: 29 return MapBuffer(input, output);
30 return GetSyncpoint(input, output); 30 case 0xa:
31 case IoctlCommand::IocGetWaitbase: 31 return UnmapBuffer(input, output);
32 return GetWaitbase(input, output); 32 default:
33 case IoctlCommand::IocMapBuffer: 33 break;
34 case IoctlCommand::IocMapBuffer2: 34 }
35 case IoctlCommand::IocMapBuffer3: 35 break;
36 case IoctlCommand::IocMapBuffer4: 36 case 'H':
37 case IoctlCommand::IocMapBufferEx: 37 switch (command.cmd) {
38 return MapBuffer(input, output); 38 case 0x1:
39 case IoctlCommand::IocUnmapBuffer: 39 return SetNVMAPfd(input);
40 case IoctlCommand::IocUnmapBuffer2: 40 default:
41 case IoctlCommand::IocUnmapBuffer3: 41 break;
42 case IoctlCommand::IocUnmapBufferEx: 42 }
43 return UnmapBuffer(input, output); 43 break;
44 default:
45 break;
44 } 46 }
45 47
46 UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw); 48 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
47 return 0; 49 return NvResult::NotImplemented;
50}
51
52NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input,
53 const std::vector<u8>& inline_input, std::vector<u8>& output) {
54 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
55 return NvResult::NotImplemented;
56}
57
58NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
59 std::vector<u8>& inline_output) {
60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
61 return NvResult::NotImplemented;
48} 62}
49 63
50} // namespace Service::Nvidia::Devices 64} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index f975b190c..b2e11f4d4 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -13,25 +13,11 @@ class nvhost_vic final : public nvhost_nvdec_common {
13public: 13public:
14 explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 14 explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
15 ~nvhost_vic(); 15 ~nvhost_vic();
16 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
17 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
18 IoctlVersion version) override;
19 16
20private: 17 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
21 enum class IoctlCommand : u32_le { 18 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
22 IocSetNVMAPfdCommand = 0x40044801, 19 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
23 IocSubmit = 0xC0400001, 20 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
24 IocGetSyncpoint = 0xC0080002, 21 std::vector<u8>& inline_output) override;
25 IocGetWaitbase = 0xC0080003,
26 IocMapBuffer = 0xC01C0009,
27 IocMapBuffer2 = 0xC0340009,
28 IocMapBuffer3 = 0xC0140009,
29 IocMapBuffer4 = 0xC00C0009,
30 IocMapBufferEx = 0xC03C0009,
31 IocUnmapBuffer = 0xC03C000A,
32 IocUnmapBuffer2 = 0xC034000A,
33 IocUnmapBuffer3 = 0xC00C000A,
34 IocUnmapBufferEx = 0xC01C000A,
35 };
36}; 22};
37} // namespace Service::Nvidia::Devices 23} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 9436e16ad..4015a2740 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -11,13 +11,6 @@
11 11
12namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
13 13
14namespace NvErrCodes {
15enum {
16 OperationNotPermitted = -1,
17 InvalidValue = -22,
18};
19}
20
21nvmap::nvmap(Core::System& system) : nvdevice(system) { 14nvmap::nvmap(Core::System& system) : nvdevice(system) {
22 // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to 15 // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to
23 // represent this. 16 // represent this.
@@ -26,6 +19,46 @@ nvmap::nvmap(Core::System& system) : nvdevice(system) {
26 19
27nvmap::~nvmap() = default; 20nvmap::~nvmap() = default;
28 21
22NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
23 switch (command.group) {
24 case 0x1:
25 switch (command.cmd) {
26 case 0x1:
27 return IocCreate(input, output);
28 case 0x3:
29 return IocFromId(input, output);
30 case 0x4:
31 return IocAlloc(input, output);
32 case 0x5:
33 return IocFree(input, output);
34 case 0x9:
35 return IocParam(input, output);
36 case 0xe:
37 return IocGetId(input, output);
38 default:
39 break;
40 }
41 break;
42 default:
43 break;
44 }
45
46 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
47 return NvResult::NotImplemented;
48}
49
50NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input,
51 const std::vector<u8>& inline_input, std::vector<u8>& output) {
52 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
53 return NvResult::NotImplemented;
54}
55
56NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
57 std::vector<u8>& inline_output) {
58 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
59 return NvResult::NotImplemented;
60}
61
29VAddr nvmap::GetObjectAddress(u32 handle) const { 62VAddr nvmap::GetObjectAddress(u32 handle) const {
30 auto object = GetObject(handle); 63 auto object = GetObject(handle);
31 ASSERT(object); 64 ASSERT(object);
@@ -33,28 +66,6 @@ VAddr nvmap::GetObjectAddress(u32 handle) const {
33 return object->addr; 66 return object->addr;
34} 67}
35 68
36u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
37 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
38 IoctlVersion version) {
39 switch (static_cast<IoctlCommand>(command.raw)) {
40 case IoctlCommand::Create:
41 return IocCreate(input, output);
42 case IoctlCommand::Alloc:
43 return IocAlloc(input, output);
44 case IoctlCommand::GetId:
45 return IocGetId(input, output);
46 case IoctlCommand::FromId:
47 return IocFromId(input, output);
48 case IoctlCommand::Param:
49 return IocParam(input, output);
50 case IoctlCommand::Free:
51 return IocFree(input, output);
52 }
53
54 UNIMPLEMENTED_MSG("Unimplemented ioctl");
55 return 0;
56}
57
58u32 nvmap::CreateObject(u32 size) { 69u32 nvmap::CreateObject(u32 size) {
59 // Create a new nvmap object and obtain a handle to it. 70 // Create a new nvmap object and obtain a handle to it.
60 auto object = std::make_shared<Object>(); 71 auto object = std::make_shared<Object>();
@@ -70,35 +81,35 @@ u32 nvmap::CreateObject(u32 size) {
70 return handle; 81 return handle;
71} 82}
72 83
73u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { 84NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
74 IocCreateParams params; 85 IocCreateParams params;
75 std::memcpy(&params, input.data(), sizeof(params)); 86 std::memcpy(&params, input.data(), sizeof(params));
76 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); 87 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
77 88
78 if (!params.size) { 89 if (!params.size) {
79 LOG_ERROR(Service_NVDRV, "Size is 0"); 90 LOG_ERROR(Service_NVDRV, "Size is 0");
80 return static_cast<u32>(NvErrCodes::InvalidValue); 91 return NvResult::BadValue;
81 } 92 }
82 93
83 params.handle = CreateObject(params.size); 94 params.handle = CreateObject(params.size);
84 95
85 std::memcpy(output.data(), &params, sizeof(params)); 96 std::memcpy(output.data(), &params, sizeof(params));
86 return 0; 97 return NvResult::Success;
87} 98}
88 99
89u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { 100NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
90 IocAllocParams params; 101 IocAllocParams params;
91 std::memcpy(&params, input.data(), sizeof(params)); 102 std::memcpy(&params, input.data(), sizeof(params));
92 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); 103 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
93 104
94 if (!params.handle) { 105 if (!params.handle) {
95 LOG_ERROR(Service_NVDRV, "Handle is 0"); 106 LOG_ERROR(Service_NVDRV, "Handle is 0");
96 return static_cast<u32>(NvErrCodes::InvalidValue); 107 return NvResult::BadValue;
97 } 108 }
98 109
99 if ((params.align - 1) & params.align) { 110 if ((params.align - 1) & params.align) {
100 LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); 111 LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
101 return static_cast<u32>(NvErrCodes::InvalidValue); 112 return NvResult::BadValue;
102 } 113 }
103 114
104 const u32 min_alignment = 0x1000; 115 const u32 min_alignment = 0x1000;
@@ -109,12 +120,12 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
109 auto object = GetObject(params.handle); 120 auto object = GetObject(params.handle);
110 if (!object) { 121 if (!object) {
111 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 122 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
112 return static_cast<u32>(NvErrCodes::InvalidValue); 123 return NvResult::BadValue;
113 } 124 }
114 125
115 if (object->status == Object::Status::Allocated) { 126 if (object->status == Object::Status::Allocated) {
116 LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); 127 LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
117 return static_cast<u32>(NvErrCodes::OperationNotPermitted); 128 return NvResult::InsufficientMemory;
118 } 129 }
119 130
120 object->flags = params.flags; 131 object->flags = params.flags;
@@ -124,10 +135,10 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
124 object->status = Object::Status::Allocated; 135 object->status = Object::Status::Allocated;
125 136
126 std::memcpy(output.data(), &params, sizeof(params)); 137 std::memcpy(output.data(), &params, sizeof(params));
127 return 0; 138 return NvResult::Success;
128} 139}
129 140
130u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { 141NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
131 IocGetIdParams params; 142 IocGetIdParams params;
132 std::memcpy(&params, input.data(), sizeof(params)); 143 std::memcpy(&params, input.data(), sizeof(params));
133 144
@@ -135,22 +146,22 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
135 146
136 if (!params.handle) { 147 if (!params.handle) {
137 LOG_ERROR(Service_NVDRV, "Handle is zero"); 148 LOG_ERROR(Service_NVDRV, "Handle is zero");
138 return static_cast<u32>(NvErrCodes::InvalidValue); 149 return NvResult::BadValue;
139 } 150 }
140 151
141 auto object = GetObject(params.handle); 152 auto object = GetObject(params.handle);
142 if (!object) { 153 if (!object) {
143 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 154 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
144 return static_cast<u32>(NvErrCodes::OperationNotPermitted); 155 return NvResult::BadValue;
145 } 156 }
146 157
147 params.id = object->id; 158 params.id = object->id;
148 159
149 std::memcpy(output.data(), &params, sizeof(params)); 160 std::memcpy(output.data(), &params, sizeof(params));
150 return 0; 161 return NvResult::Success;
151} 162}
152 163
153u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { 164NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
154 IocFromIdParams params; 165 IocFromIdParams params;
155 std::memcpy(&params, input.data(), sizeof(params)); 166 std::memcpy(&params, input.data(), sizeof(params));
156 167
@@ -160,13 +171,13 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
160 [&](const auto& entry) { return entry.second->id == params.id; }); 171 [&](const auto& entry) { return entry.second->id == params.id; });
161 if (itr == handles.end()) { 172 if (itr == handles.end()) {
162 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 173 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
163 return static_cast<u32>(NvErrCodes::InvalidValue); 174 return NvResult::BadValue;
164 } 175 }
165 176
166 auto& object = itr->second; 177 auto& object = itr->second;
167 if (object->status != Object::Status::Allocated) { 178 if (object->status != Object::Status::Allocated) {
168 LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); 179 LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
169 return static_cast<u32>(NvErrCodes::InvalidValue); 180 return NvResult::BadValue;
170 } 181 }
171 182
172 itr->second->refcount++; 183 itr->second->refcount++;
@@ -175,10 +186,10 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
175 params.handle = itr->first; 186 params.handle = itr->first;
176 187
177 std::memcpy(output.data(), &params, sizeof(params)); 188 std::memcpy(output.data(), &params, sizeof(params));
178 return 0; 189 return NvResult::Success;
179} 190}
180 191
181u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { 192NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
182 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; 193 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
183 194
184 IocParamParams params; 195 IocParamParams params;
@@ -189,12 +200,12 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
189 auto object = GetObject(params.handle); 200 auto object = GetObject(params.handle);
190 if (!object) { 201 if (!object) {
191 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 202 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
192 return static_cast<u32>(NvErrCodes::InvalidValue); 203 return NvResult::BadValue;
193 } 204 }
194 205
195 if (object->status != Object::Status::Allocated) { 206 if (object->status != Object::Status::Allocated) {
196 LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); 207 LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
197 return static_cast<u32>(NvErrCodes::OperationNotPermitted); 208 return NvResult::BadValue;
198 } 209 }
199 210
200 switch (static_cast<ParamTypes>(params.param)) { 211 switch (static_cast<ParamTypes>(params.param)) {
@@ -216,10 +227,10 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
216 } 227 }
217 228
218 std::memcpy(output.data(), &params, sizeof(params)); 229 std::memcpy(output.data(), &params, sizeof(params));
219 return 0; 230 return NvResult::Success;
220} 231}
221 232
222u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { 233NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
223 // TODO(Subv): These flags are unconfirmed. 234 // TODO(Subv): These flags are unconfirmed.
224 enum FreeFlags { 235 enum FreeFlags {
225 Freed = 0, 236 Freed = 0,
@@ -234,14 +245,14 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
234 auto itr = handles.find(params.handle); 245 auto itr = handles.find(params.handle);
235 if (itr == handles.end()) { 246 if (itr == handles.end()) {
236 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 247 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
237 return static_cast<u32>(NvErrCodes::InvalidValue); 248 return NvResult::BadValue;
238 } 249 }
239 if (!itr->second->refcount) { 250 if (!itr->second->refcount) {
240 LOG_ERROR( 251 LOG_ERROR(
241 Service_NVDRV, 252 Service_NVDRV,
242 "There is no references to this object. The object is already freed. handle={:08X}", 253 "There is no references to this object. The object is already freed. handle={:08X}",
243 params.handle); 254 params.handle);
244 return static_cast<u32>(NvErrCodes::InvalidValue); 255 return NvResult::BadValue;
245 } 256 }
246 257
247 itr->second->refcount--; 258 itr->second->refcount--;
@@ -261,7 +272,7 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
261 handles.erase(params.handle); 272 handles.erase(params.handle);
262 273
263 std::memcpy(output.data(), &params, sizeof(params)); 274 std::memcpy(output.data(), &params, sizeof(params));
264 return 0; 275 return NvResult::Success;
265} 276}
266 277
267} // namespace Service::Nvidia::Devices 278} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 04b9ef540..4484bd79f 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -19,13 +19,15 @@ public:
19 explicit nvmap(Core::System& system); 19 explicit nvmap(Core::System& system);
20 ~nvmap() override; 20 ~nvmap() override;
21 21
22 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
23 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
24 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
25 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
26 std::vector<u8>& inline_output) override;
27
22 /// Returns the allocated address of an nvmap object given its handle. 28 /// Returns the allocated address of an nvmap object given its handle.
23 VAddr GetObjectAddress(u32 handle) const; 29 VAddr GetObjectAddress(u32 handle) const;
24 30
25 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
26 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
27 IoctlVersion version) override;
28
29 /// Represents an nvmap object. 31 /// Represents an nvmap object.
30 struct Object { 32 struct Object {
31 enum class Status { Created, Allocated }; 33 enum class Status { Created, Allocated };
@@ -58,76 +60,68 @@ private:
58 /// Mapping of currently allocated handles to the objects they represent. 60 /// Mapping of currently allocated handles to the objects they represent.
59 std::unordered_map<u32, std::shared_ptr<Object>> handles; 61 std::unordered_map<u32, std::shared_ptr<Object>> handles;
60 62
61 enum class IoctlCommand : u32 {
62 Create = 0xC0080101,
63 FromId = 0xC0080103,
64 Alloc = 0xC0200104,
65 Free = 0xC0180105,
66 Param = 0xC00C0109,
67 GetId = 0xC008010E,
68 };
69 struct IocCreateParams { 63 struct IocCreateParams {
70 // Input 64 // Input
71 u32_le size; 65 u32_le size{};
72 // Output 66 // Output
73 u32_le handle; 67 u32_le handle{};
74 }; 68 };
75 static_assert(sizeof(IocCreateParams) == 8, "IocCreateParams has wrong size"); 69 static_assert(sizeof(IocCreateParams) == 8, "IocCreateParams has wrong size");
76 70
77 struct IocFromIdParams { 71 struct IocFromIdParams {
78 // Input 72 // Input
79 u32_le id; 73 u32_le id{};
80 // Output 74 // Output
81 u32_le handle; 75 u32_le handle{};
82 }; 76 };
83 static_assert(sizeof(IocFromIdParams) == 8, "IocFromIdParams has wrong size"); 77 static_assert(sizeof(IocFromIdParams) == 8, "IocFromIdParams has wrong size");
84 78
85 struct IocAllocParams { 79 struct IocAllocParams {
86 // Input 80 // Input
87 u32_le handle; 81 u32_le handle{};
88 u32_le heap_mask; 82 u32_le heap_mask{};
89 u32_le flags; 83 u32_le flags{};
90 u32_le align; 84 u32_le align{};
91 u8 kind; 85 u8 kind{};
92 INSERT_PADDING_BYTES(7); 86 INSERT_PADDING_BYTES(7);
93 u64_le addr; 87 u64_le addr{};
94 }; 88 };
95 static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size"); 89 static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size");
96 90
97 struct IocFreeParams { 91 struct IocFreeParams {
98 u32_le handle; 92 u32_le handle{};
99 INSERT_PADDING_BYTES(4); 93 INSERT_PADDING_BYTES(4);
100 u64_le address; 94 u64_le address{};
101 u32_le size; 95 u32_le size{};
102 u32_le flags; 96 u32_le flags{};
103 }; 97 };
104 static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size"); 98 static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size");
105 99
106 struct IocParamParams { 100 struct IocParamParams {
107 // Input 101 // Input
108 u32_le handle; 102 u32_le handle{};
109 u32_le param; 103 u32_le param{};
110 // Output 104 // Output
111 u32_le result; 105 u32_le result{};
112 }; 106 };
113 static_assert(sizeof(IocParamParams) == 12, "IocParamParams has wrong size"); 107 static_assert(sizeof(IocParamParams) == 12, "IocParamParams has wrong size");
114 108
115 struct IocGetIdParams { 109 struct IocGetIdParams {
116 // Output 110 // Output
117 u32_le id; 111 u32_le id{};
118 // Input 112 // Input
119 u32_le handle; 113 u32_le handle{};
120 }; 114 };
121 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); 115 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
122 116
123 u32 CreateObject(u32 size); 117 u32 CreateObject(u32 size);
124 118
125 u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output); 119 NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
126 u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); 120 NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
127 u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output); 121 NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
128 u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output); 122 NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
129 u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output); 123 NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output);
130 u32 IocFree(const std::vector<u8>& input, std::vector<u8>& output); 124 NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output);
131}; 125};
132 126
133} // namespace Service::Nvidia::Devices 127} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index 88fbfa9b0..f6c38e853 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -23,124 +23,170 @@ void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
23void NVDRV::Open(Kernel::HLERequestContext& ctx) { 23void NVDRV::Open(Kernel::HLERequestContext& ctx) {
24 LOG_DEBUG(Service_NVDRV, "called"); 24 LOG_DEBUG(Service_NVDRV, "called");
25 25
26 if (!is_initialized) {
27 ServiceError(ctx, NvResult::NotInitialized);
28 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
29 return;
30 }
31
26 const auto& buffer = ctx.ReadBuffer(); 32 const auto& buffer = ctx.ReadBuffer();
27 std::string device_name(buffer.begin(), buffer.end()); 33 const std::string device_name(buffer.begin(), buffer.end());
34 DeviceFD fd = nvdrv->Open(device_name);
28 35
29 u32 fd = nvdrv->Open(device_name);
30 IPC::ResponseBuilder rb{ctx, 4}; 36 IPC::ResponseBuilder rb{ctx, 4};
31 rb.Push(RESULT_SUCCESS); 37 rb.Push(RESULT_SUCCESS);
32 rb.Push<u32>(fd); 38 rb.Push<DeviceFD>(fd);
33 rb.Push<u32>(0); 39 rb.PushEnum(fd != INVALID_NVDRV_FD ? NvResult::Success : NvResult::FileOperationFailed);
40}
41
42void NVDRV::ServiceError(Kernel::HLERequestContext& ctx, NvResult result) {
43 IPC::ResponseBuilder rb{ctx, 3};
44 rb.Push(RESULT_SUCCESS);
45 rb.PushEnum(result);
34} 46}
35 47
36void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) { 48void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
37 IPC::RequestParser rp{ctx}; 49 IPC::RequestParser rp{ctx};
38 u32 fd = rp.Pop<u32>(); 50 const auto fd = rp.Pop<DeviceFD>();
39 u32 command = rp.Pop<u32>(); 51 const auto command = rp.PopRaw<Ioctl>();
40 52 LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
41 /// Ioctl 3 has 2 outputs, first in the input params, second is the result 53
42 std::vector<u8> output(ctx.GetWriteBufferSize(0)); 54 if (!is_initialized) {
43 std::vector<u8> output2; 55 ServiceError(ctx, NvResult::NotInitialized);
44 if (version == IoctlVersion::Version3) { 56 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
45 output2.resize((ctx.GetWriteBufferSize(1))); 57 return;
46 } 58 }
47 59
48 /// Ioctl2 has 2 inputs. It's used to pass data directly instead of providing a pointer. 60 // Check device
49 /// KickOfPB uses this 61 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
50 auto input = ctx.ReadBuffer(0); 62 const auto input_buffer = ctx.ReadBuffer(0);
51 63
52 std::vector<u8> input2; 64 const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer);
53 if (version == IoctlVersion::Version2) {
54 input2 = ctx.ReadBuffer(1);
55 }
56 65
57 IoctlCtrl ctrl{}; 66 if (command.is_out != 0) {
58 67 ctx.WriteBuffer(output_buffer);
59 u32 result = nvdrv->Ioctl(fd, command, input, input2, output, output2, ctrl, version);
60
61 if (ctrl.must_delay) {
62 ctrl.fresh_call = false;
63 ctx.SleepClientThread(
64 "NVServices::DelayedResponse", ctrl.timeout,
65 [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
66 Kernel::ThreadWakeupReason reason) {
67 IoctlCtrl ctrl2{ctrl};
68 std::vector<u8> tmp_output = output;
69 std::vector<u8> tmp_output2 = output2;
70 const u32 ioctl_result = nvdrv->Ioctl(fd, command, input, input2, tmp_output,
71 tmp_output2, ctrl2, version);
72 ctx_.WriteBuffer(tmp_output, 0);
73 if (version == IoctlVersion::Version3) {
74 ctx_.WriteBuffer(tmp_output2, 1);
75 }
76 IPC::ResponseBuilder rb{ctx_, 3};
77 rb.Push(RESULT_SUCCESS);
78 rb.Push(ioctl_result);
79 },
80 nvdrv->GetEventWriteable(ctrl.event_id));
81 } else {
82 ctx.WriteBuffer(output);
83 if (version == IoctlVersion::Version3) {
84 ctx.WriteBuffer(output2, 1);
85 }
86 } 68 }
69
87 IPC::ResponseBuilder rb{ctx, 3}; 70 IPC::ResponseBuilder rb{ctx, 3};
88 rb.Push(RESULT_SUCCESS); 71 rb.Push(RESULT_SUCCESS);
89 rb.Push(result); 72 rb.PushEnum(nv_result);
90}
91
92void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
93 LOG_DEBUG(Service_NVDRV, "called");
94 IoctlBase(ctx, IoctlVersion::Version1);
95} 73}
96 74
97void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) { 75void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
98 LOG_DEBUG(Service_NVDRV, "called"); 76 IPC::RequestParser rp{ctx};
99 IoctlBase(ctx, IoctlVersion::Version2); 77 const auto fd = rp.Pop<DeviceFD>();
78 const auto command = rp.PopRaw<Ioctl>();
79 LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
80
81 if (!is_initialized) {
82 ServiceError(ctx, NvResult::NotInitialized);
83 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
84 return;
85 }
86
87 const auto input_buffer = ctx.ReadBuffer(0);
88 const auto input_inlined_buffer = ctx.ReadBuffer(1);
89 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
90
91 const auto nv_result =
92 nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer);
93
94 if (command.is_out != 0) {
95 ctx.WriteBuffer(output_buffer);
96 }
97
98 IPC::ResponseBuilder rb{ctx, 3};
99 rb.Push(RESULT_SUCCESS);
100 rb.PushEnum(nv_result);
100} 101}
101 102
102void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) { 103void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
103 LOG_DEBUG(Service_NVDRV, "called"); 104 IPC::RequestParser rp{ctx};
104 IoctlBase(ctx, IoctlVersion::Version3); 105 const auto fd = rp.Pop<DeviceFD>();
106 const auto command = rp.PopRaw<Ioctl>();
107 LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
108
109 if (!is_initialized) {
110 ServiceError(ctx, NvResult::NotInitialized);
111 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
112 return;
113 }
114
115 const auto input_buffer = ctx.ReadBuffer(0);
116 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
117 std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1));
118
119 const auto nv_result =
120 nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline);
121
122 if (command.is_out != 0) {
123 ctx.WriteBuffer(output_buffer, 0);
124 ctx.WriteBuffer(output_buffer_inline, 1);
125 }
126
127 IPC::ResponseBuilder rb{ctx, 3};
128 rb.Push(RESULT_SUCCESS);
129 rb.PushEnum(nv_result);
105} 130}
106 131
107void NVDRV::Close(Kernel::HLERequestContext& ctx) { 132void NVDRV::Close(Kernel::HLERequestContext& ctx) {
108 LOG_DEBUG(Service_NVDRV, "called"); 133 LOG_DEBUG(Service_NVDRV, "called");
109 134
110 IPC::RequestParser rp{ctx}; 135 if (!is_initialized) {
111 u32 fd = rp.Pop<u32>(); 136 ServiceError(ctx, NvResult::NotInitialized);
137 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
138 return;
139 }
112 140
113 auto result = nvdrv->Close(fd); 141 IPC::RequestParser rp{ctx};
142 const auto fd = rp.Pop<DeviceFD>();
143 const auto result = nvdrv->Close(fd);
114 144
115 IPC::ResponseBuilder rb{ctx, 2}; 145 IPC::ResponseBuilder rb{ctx, 3};
116 rb.Push(result); 146 rb.Push(RESULT_SUCCESS);
147 rb.PushEnum(result);
117} 148}
118 149
119void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { 150void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
120 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 151 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
121 152
153 is_initialized = true;
154
122 IPC::ResponseBuilder rb{ctx, 3}; 155 IPC::ResponseBuilder rb{ctx, 3};
123 rb.Push(RESULT_SUCCESS); 156 rb.Push(RESULT_SUCCESS);
124 rb.Push<u32>(0); 157 rb.PushEnum(NvResult::Success);
125} 158}
126 159
127void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { 160void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
128 IPC::RequestParser rp{ctx}; 161 IPC::RequestParser rp{ctx};
129 u32 fd = rp.Pop<u32>(); 162 const auto fd = rp.Pop<DeviceFD>();
130 // TODO(Blinkhawk): Figure the meaning of the flag at bit 16 163 const auto event_id = rp.Pop<u32>() & 0x00FF;
131 u32 event_id = rp.Pop<u32>() & 0x000000FF;
132 LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); 164 LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
133 165
134 IPC::ResponseBuilder rb{ctx, 3, 1}; 166 if (!is_initialized) {
135 rb.Push(RESULT_SUCCESS); 167 ServiceError(ctx, NvResult::NotInitialized);
168 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
169 return;
170 }
171
172 const auto nv_result = nvdrv->VerifyFD(fd);
173 if (nv_result != NvResult::Success) {
174 LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd);
175 ServiceError(ctx, nv_result);
176 return;
177 }
178
136 if (event_id < MaxNvEvents) { 179 if (event_id < MaxNvEvents) {
180 IPC::ResponseBuilder rb{ctx, 3, 1};
181 rb.Push(RESULT_SUCCESS);
137 auto event = nvdrv->GetEvent(event_id); 182 auto event = nvdrv->GetEvent(event_id);
138 event->Clear(); 183 event->Clear();
139 rb.PushCopyObjects(event); 184 rb.PushCopyObjects(event);
140 rb.Push<u32>(NvResult::Success); 185 rb.PushEnum(NvResult::Success);
141 } else { 186 } else {
142 rb.Push<u32>(0); 187 IPC::ResponseBuilder rb{ctx, 3};
143 rb.Push<u32>(NvResult::BadParameter); 188 rb.Push(RESULT_SUCCESS);
189 rb.PushEnum(NvResult::BadParameter);
144 } 190 }
145} 191}
146 192
@@ -151,7 +197,7 @@ void NVDRV::SetAruid(Kernel::HLERequestContext& ctx) {
151 197
152 IPC::ResponseBuilder rb{ctx, 3}; 198 IPC::ResponseBuilder rb{ctx, 3};
153 rb.Push(RESULT_SUCCESS); 199 rb.Push(RESULT_SUCCESS);
154 rb.Push<u32>(0); 200 rb.PushEnum(NvResult::Success);
155} 201}
156 202
157void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) { 203void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) {
@@ -164,8 +210,9 @@ void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ct
164void NVDRV::GetStatus(Kernel::HLERequestContext& ctx) { 210void NVDRV::GetStatus(Kernel::HLERequestContext& ctx) {
165 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 211 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
166 212
167 IPC::ResponseBuilder rb{ctx, 2}; 213 IPC::ResponseBuilder rb{ctx, 3};
168 rb.Push(RESULT_SUCCESS); 214 rb.Push(RESULT_SUCCESS);
215 rb.PushEnum(NvResult::Success);
169} 216}
170 217
171void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) { 218void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
@@ -181,7 +228,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
181 : ServiceFramework(name), nvdrv(std::move(nvdrv)) { 228 : ServiceFramework(name), nvdrv(std::move(nvdrv)) {
182 static const FunctionInfo functions[] = { 229 static const FunctionInfo functions[] = {
183 {0, &NVDRV::Open, "Open"}, 230 {0, &NVDRV::Open, "Open"},
184 {1, &NVDRV::Ioctl, "Ioctl"}, 231 {1, &NVDRV::Ioctl1, "Ioctl"},
185 {2, &NVDRV::Close, "Close"}, 232 {2, &NVDRV::Close, "Close"},
186 {3, &NVDRV::Initialize, "Initialize"}, 233 {3, &NVDRV::Initialize, "Initialize"},
187 {4, &NVDRV::QueryEvent, "QueryEvent"}, 234 {4, &NVDRV::QueryEvent, "QueryEvent"},
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 72e17a728..e05f905ae 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -23,7 +23,7 @@ public:
23 23
24private: 24private:
25 void Open(Kernel::HLERequestContext& ctx); 25 void Open(Kernel::HLERequestContext& ctx);
26 void Ioctl(Kernel::HLERequestContext& ctx); 26 void Ioctl1(Kernel::HLERequestContext& ctx);
27 void Ioctl2(Kernel::HLERequestContext& ctx); 27 void Ioctl2(Kernel::HLERequestContext& ctx);
28 void Ioctl3(Kernel::HLERequestContext& ctx); 28 void Ioctl3(Kernel::HLERequestContext& ctx);
29 void Close(Kernel::HLERequestContext& ctx); 29 void Close(Kernel::HLERequestContext& ctx);
@@ -33,11 +33,13 @@ private:
33 void SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx); 33 void SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx);
34 void GetStatus(Kernel::HLERequestContext& ctx); 34 void GetStatus(Kernel::HLERequestContext& ctx);
35 void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx); 35 void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
36 void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version); 36
37 void ServiceError(Kernel::HLERequestContext& ctx, NvResult result);
37 38
38 std::shared_ptr<Module> nvdrv; 39 std::shared_ptr<Module> nvdrv;
39 40
40 u64 pid{}; 41 u64 pid{};
42 bool is_initialized{};
41}; 43};
42 44
43} // namespace Service::Nvidia 45} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 529b03471..3294bc0e7 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -1,12 +1,16 @@
1#pragma once 1#pragma once
2 2
3#include <array> 3#include <array>
4#include "common/bit_field.h"
4#include "common/common_types.h" 5#include "common/common_types.h"
5 6
6namespace Service::Nvidia { 7namespace Service::Nvidia {
7 8
8constexpr u32 MaxSyncPoints = 192; 9constexpr u32 MaxSyncPoints = 192;
9constexpr u32 MaxNvEvents = 64; 10constexpr u32 MaxNvEvents = 64;
11using DeviceFD = s32;
12
13constexpr DeviceFD INVALID_NVDRV_FD = -1;
10 14
11struct Fence { 15struct Fence {
12 s32 id; 16 s32 id;
@@ -20,11 +24,61 @@ struct MultiFence {
20 std::array<Fence, 4> fences; 24 std::array<Fence, 4> fences;
21}; 25};
22 26
23enum NvResult : u32 { 27enum class NvResult : u32 {
24 Success = 0, 28 Success = 0x0,
25 BadParameter = 4, 29 NotImplemented = 0x1,
26 Timeout = 5, 30 NotSupported = 0x2,
27 ResourceError = 15, 31 NotInitialized = 0x3,
32 BadParameter = 0x4,
33 Timeout = 0x5,
34 InsufficientMemory = 0x6,
35 ReadOnlyAttribute = 0x7,
36 InvalidState = 0x8,
37 InvalidAddress = 0x9,
38 InvalidSize = 0xA,
39 BadValue = 0xB,
40 AlreadyAllocated = 0xD,
41 Busy = 0xE,
42 ResourceError = 0xF,
43 CountMismatch = 0x10,
44 OverFlow = 0x11,
45 InsufficientTransferMemory = 0x1000,
46 InsufficientVideoMemory = 0x10000,
47 BadSurfaceColorScheme = 0x10001,
48 InvalidSurface = 0x10002,
49 SurfaceNotSupported = 0x10003,
50 DispInitFailed = 0x20000,
51 DispAlreadyAttached = 0x20001,
52 DispTooManyDisplays = 0x20002,
53 DispNoDisplaysAttached = 0x20003,
54 DispModeNotSupported = 0x20004,
55 DispNotFound = 0x20005,
56 DispAttachDissallowed = 0x20006,
57 DispTypeNotSupported = 0x20007,
58 DispAuthenticationFailed = 0x20008,
59 DispNotAttached = 0x20009,
60 DispSamePwrState = 0x2000A,
61 DispEdidFailure = 0x2000B,
62 DispDsiReadAckError = 0x2000C,
63 DispDsiReadInvalidResp = 0x2000D,
64 FileWriteFailed = 0x30000,
65 FileReadFailed = 0x30001,
66 EndOfFile = 0x30002,
67 FileOperationFailed = 0x30003,
68 DirOperationFailed = 0x30004,
69 EndOfDirList = 0x30005,
70 ConfigVarNotFound = 0x30006,
71 InvalidConfigVar = 0x30007,
72 LibraryNotFound = 0x30008,
73 SymbolNotFound = 0x30009,
74 MemoryMapFailed = 0x3000A,
75 IoctlFailed = 0x3000F,
76 AccessDenied = 0x30010,
77 DeviceNotFound = 0x30011,
78 KernelDriverNotFound = 0x30012,
79 FileNotFound = 0x30013,
80 PathAlreadyExists = 0x30014,
81 ModuleNotPresent = 0xA000E,
28}; 82};
29 83
30enum class EventState { 84enum class EventState {
@@ -34,21 +88,13 @@ enum class EventState {
34 Busy = 3, 88 Busy = 3,
35}; 89};
36 90
37enum class IoctlVersion : u32 { 91union Ioctl {
38 Version1, 92 u32_le raw;
39 Version2, 93 BitField<0, 8, u32> cmd;
40 Version3, 94 BitField<8, 8, u32> group;
41}; 95 BitField<16, 14, u32> length;
42 96 BitField<30, 1, u32> is_in;
43struct IoctlCtrl { 97 BitField<31, 1, u32> is_out;
44 // First call done to the servioce for services that call itself again after a call.
45 bool fresh_call{true};
46 // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep
47 bool must_delay{};
48 // Timeout for the delay
49 s64 timeout{};
50 // NV Event Id
51 s32 event_id{-1};
52}; 98};
53 99
54} // namespace Service::Nvidia 100} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index a46755cdc..bdbbedd0d 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -5,6 +5,7 @@
5#include <utility> 5#include <utility>
6 6
7#include <fmt/format.h> 7#include <fmt/format.h>
8#include "core/core.h"
8#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/readable_event.h" 10#include "core/hle/kernel/readable_event.h"
10#include "core/hle/kernel/writable_event.h" 11#include "core/hle/kernel/writable_event.h"
@@ -61,36 +62,101 @@ Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
61 62
62Module::~Module() = default; 63Module::~Module() = default;
63 64
64u32 Module::Open(const std::string& device_name) { 65NvResult Module::VerifyFD(DeviceFD fd) const {
65 ASSERT_MSG(devices.find(device_name) != devices.end(), "Trying to open unknown device {}", 66 if (fd < 0) {
66 device_name); 67 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
68 return NvResult::InvalidState;
69 }
70
71 if (open_files.find(fd) == open_files.end()) {
72 LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
73 return NvResult::NotImplemented;
74 }
75
76 return NvResult::Success;
77}
78
79DeviceFD Module::Open(const std::string& device_name) {
80 if (devices.find(device_name) == devices.end()) {
81 LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name);
82 return INVALID_NVDRV_FD;
83 }
67 84
68 auto device = devices[device_name]; 85 auto device = devices[device_name];
69 const u32 fd = next_fd++; 86 const DeviceFD fd = next_fd++;
70 87
71 open_files[fd] = std::move(device); 88 open_files[fd] = std::move(device);
72 89
73 return fd; 90 return fd;
74} 91}
75 92
76u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2, 93NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
77 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 94 std::vector<u8>& output) {
78 IoctlVersion version) { 95 if (fd < 0) {
79 auto itr = open_files.find(fd); 96 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
80 ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); 97 return NvResult::InvalidState;
98 }
81 99
82 auto& device = itr->second; 100 const auto itr = open_files.find(fd);
83 return device->ioctl({command}, input, input2, output, output2, ctrl, version); 101
102 if (itr == open_files.end()) {
103 LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
104 return NvResult::NotImplemented;
105 }
106
107 return itr->second->Ioctl1(command, input, output);
84} 108}
85 109
86ResultCode Module::Close(u32 fd) { 110NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
87 auto itr = open_files.find(fd); 111 const std::vector<u8>& inline_input, std::vector<u8>& output) {
88 ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); 112 if (fd < 0) {
113 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
114 return NvResult::InvalidState;
115 }
116
117 const auto itr = open_files.find(fd);
118
119 if (itr == open_files.end()) {
120 LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
121 return NvResult::NotImplemented;
122 }
123
124 return itr->second->Ioctl2(command, input, inline_input, output);
125}
126
127NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
128 std::vector<u8>& output, std::vector<u8>& inline_output) {
129 if (fd < 0) {
130 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
131 return NvResult::InvalidState;
132 }
133
134 const auto itr = open_files.find(fd);
135
136 if (itr == open_files.end()) {
137 LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
138 return NvResult::NotImplemented;
139 }
140
141 return itr->second->Ioctl3(command, input, output, inline_output);
142}
143
144NvResult Module::Close(DeviceFD fd) {
145 if (fd < 0) {
146 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
147 return NvResult::InvalidState;
148 }
149
150 const auto itr = open_files.find(fd);
151
152 if (itr == open_files.end()) {
153 LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
154 return NvResult::NotImplemented;
155 }
89 156
90 open_files.erase(itr); 157 open_files.erase(itr);
91 158
92 // TODO(flerovium): return correct result code if operation failed. 159 return NvResult::Success;
93 return RESULT_SUCCESS;
94} 160}
95 161
96void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { 162void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index f3d863dac..7654bb026 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -112,14 +112,23 @@ public:
112 return std::static_pointer_cast<T>(itr->second); 112 return std::static_pointer_cast<T>(itr->second);
113 } 113 }
114 114
115 NvResult VerifyFD(DeviceFD fd) const;
116
115 /// Opens a device node and returns a file descriptor to it. 117 /// Opens a device node and returns a file descriptor to it.
116 u32 Open(const std::string& device_name); 118 DeviceFD Open(const std::string& device_name);
119
117 /// Sends an ioctl command to the specified file descriptor. 120 /// Sends an ioctl command to the specified file descriptor.
118 u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2, 121 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
119 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 122 std::vector<u8>& output);
120 IoctlVersion version); 123
124 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
125 const std::vector<u8>& inline_input, std::vector<u8>& output);
126
127 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
128 std::vector<u8>& output, std::vector<u8>& inline_output);
129
121 /// Closes a device file descriptor and returns operation success. 130 /// Closes a device file descriptor and returns operation success.
122 ResultCode Close(u32 fd); 131 NvResult Close(DeviceFD fd);
123 132
124 void SignalSyncpt(const u32 syncpoint_id, const u32 value); 133 void SignalSyncpt(const u32 syncpoint_id, const u32 value);
125 134
@@ -132,10 +141,10 @@ private:
132 SyncpointManager syncpoint_manager; 141 SyncpointManager syncpoint_manager;
133 142
134 /// Id to use for the next open file descriptor. 143 /// Id to use for the next open file descriptor.
135 u32 next_fd = 1; 144 DeviceFD next_fd = 1;
136 145
137 /// Mapping of file descriptors to the devices they reference. 146 /// Mapping of file descriptors to the devices they reference.
138 std::unordered_map<u32, std::shared_ptr<Devices::nvdevice>> open_files; 147 std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>> open_files;
139 148
140 /// Mapping of device node names to their implementation. 149 /// Mapping of device node names to their implementation.
141 std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices; 150 std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;
diff --git a/src/core/hle/service/olsc/olsc.cpp b/src/core/hle/service/olsc/olsc.cpp
new file mode 100644
index 000000000..aad4ca706
--- /dev/null
+++ b/src/core/hle/service/olsc/olsc.cpp
@@ -0,0 +1,69 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/ipc_helpers.h"
6#include "core/hle/kernel/hle_ipc.h"
7#include "core/hle/service/olsc/olsc.h"
8#include "core/hle/service/service.h"
9#include "core/hle/service/sm/sm.h"
10
11namespace Service::OLSC {
12
13class OLSC final : public ServiceFramework<OLSC> {
14public:
15 explicit OLSC() : ServiceFramework{"olsc:u"} {
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {0, &OLSC::Initialize, "Initialize"},
19 {10, nullptr, "VerifySaveDataBackupLicenseAsync"},
20 {13, nullptr, "GetSaveDataBackupSetting"},
21 {14, &OLSC::SetSaveDataBackupSettingEnabled, "SetSaveDataBackupSettingEnabled"},
22 {15, nullptr, "SetCustomData"},
23 {16, nullptr, "DeleteSaveDataBackupSetting"},
24 {18, nullptr, "GetSaveDataBackupInfoCache"},
25 {19, nullptr, "UpdateSaveDataBackupInfoCacheAsync"},
26 {22, nullptr, "DeleteSaveDataBackupAsync"},
27 {25, nullptr, "ListDownloadableSaveDataBackupInfoAsync"},
28 {26, nullptr, "DownloadSaveDataBackupAsync"},
29 {9010, nullptr, "VerifySaveDataBackupLicenseAsyncForDebug"},
30 {9013, nullptr, "GetSaveDataBackupSettingForDebug"},
31 {9014, nullptr, "SetSaveDataBackupSettingEnabledForDebug"},
32 {9015, nullptr, "SetCustomDataForDebug"},
33 {9016, nullptr, "DeleteSaveDataBackupSettingForDebug"},
34 {9018, nullptr, "GetSaveDataBackupInfoCacheForDebug"},
35 {9019, nullptr, "UpdateSaveDataBackupInfoCacheAsyncForDebug"},
36 {9022, nullptr, "DeleteSaveDataBackupAsyncForDebug"},
37 {9025, nullptr, "ListDownloadableSaveDataBackupInfoAsyncForDebug"},
38 {9026, nullptr, "DownloadSaveDataBackupAsyncForDebug"},
39 };
40 // clang-format on
41
42 RegisterHandlers(functions);
43 }
44
45private:
46 void Initialize(Kernel::HLERequestContext& ctx) {
47 LOG_WARNING(Service_OLSC, "(STUBBED) called");
48
49 initialized = true;
50
51 IPC::ResponseBuilder rb{ctx, 2};
52 rb.Push(RESULT_SUCCESS);
53 }
54
55 void SetSaveDataBackupSettingEnabled(Kernel::HLERequestContext& ctx) {
56 LOG_WARNING(Service_OLSC, "(STUBBED) called");
57
58 IPC::ResponseBuilder rb{ctx, 2};
59 rb.Push(RESULT_SUCCESS);
60 }
61
62 bool initialized{};
63};
64
65void InstallInterfaces(SM::ServiceManager& service_manager) {
66 std::make_shared<OLSC>()->InstallAsService(service_manager);
67}
68
69} // namespace Service::OLSC
diff --git a/src/core/hle/service/olsc/olsc.h b/src/core/hle/service/olsc/olsc.h
new file mode 100644
index 000000000..edee4376b
--- /dev/null
+++ b/src/core/hle/service/olsc/olsc.h
@@ -0,0 +1,16 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service::SM {
8class ServiceManager;
9}
10
11namespace Service::OLSC {
12
13/// Registers all SSL services with the specified service manager.
14void InstallInterfaces(SM::ServiceManager& service_manager);
15
16} // namespace Service::OLSC
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index f43122ad2..a771a51b4 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.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 "core/core.h"
5#include "core/hle/ipc_helpers.h" 6#include "core/hle/ipc_helpers.h"
6#include "core/hle/kernel/kernel.h" 7#include "core/hle/kernel/kernel.h"
7#include "core/hle/kernel/process.h" 8#include "core/hle/kernel/process.h"
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index cde3312da..b9ef86b72 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/hex_util.h" 5#include "common/hex_util.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h"
7#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/process.h" 9#include "core/hle/kernel/process.h"
9#include "core/hle/service/acc/profile_manager.h" 10#include "core/hle/service/acc/profile_manager.h"
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index ba9159ee0..fb4979af2 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -51,6 +51,7 @@
51#include "core/hle/service/ns/ns.h" 51#include "core/hle/service/ns/ns.h"
52#include "core/hle/service/nvdrv/nvdrv.h" 52#include "core/hle/service/nvdrv/nvdrv.h"
53#include "core/hle/service/nvflinger/nvflinger.h" 53#include "core/hle/service/nvflinger/nvflinger.h"
54#include "core/hle/service/olsc/olsc.h"
54#include "core/hle/service/pcie/pcie.h" 55#include "core/hle/service/pcie/pcie.h"
55#include "core/hle/service/pctl/module.h" 56#include "core/hle/service/pctl/module.h"
56#include "core/hle/service/pcv/pcv.h" 57#include "core/hle/service/pcv/pcv.h"
@@ -187,17 +188,19 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
187 return RESULT_SUCCESS; 188 return RESULT_SUCCESS;
188} 189}
189 190
190/// Initialize ServiceManager 191/// Initialize Services
191void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { 192Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system)
193 : nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system)} {
194
192 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 195 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
193 // here and pass it into the respective InstallInterfaces functions. 196 // here and pass it into the respective InstallInterfaces functions.
194 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system); 197
195 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); 198 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
196 199
197 SM::ServiceManager::InstallInterfaces(sm, system.Kernel()); 200 SM::ServiceManager::InstallInterfaces(sm, system.Kernel());
198 201
199 Account::InstallInterfaces(system); 202 Account::InstallInterfaces(system);
200 AM::InstallInterfaces(*sm, nv_flinger, system); 203 AM::InstallInterfaces(*sm, *nv_flinger, system);
201 AOC::InstallInterfaces(*sm, system); 204 AOC::InstallInterfaces(*sm, system);
202 APM::InstallInterfaces(system); 205 APM::InstallInterfaces(system);
203 Audio::InstallInterfaces(*sm, system); 206 Audio::InstallInterfaces(*sm, system);
@@ -231,6 +234,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
231 NPNS::InstallInterfaces(*sm); 234 NPNS::InstallInterfaces(*sm);
232 NS::InstallInterfaces(*sm, system); 235 NS::InstallInterfaces(*sm, system);
233 Nvidia::InstallInterfaces(*sm, *nv_flinger, system); 236 Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
237 OLSC::InstallInterfaces(*sm);
234 PCIe::InstallInterfaces(*sm); 238 PCIe::InstallInterfaces(*sm);
235 PCTL::InstallInterfaces(*sm); 239 PCTL::InstallInterfaces(*sm);
236 PCV::InstallInterfaces(*sm); 240 PCV::InstallInterfaces(*sm);
@@ -244,14 +248,10 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
244 SSL::InstallInterfaces(*sm); 248 SSL::InstallInterfaces(*sm);
245 Time::InstallInterfaces(system); 249 Time::InstallInterfaces(system);
246 USB::InstallInterfaces(*sm); 250 USB::InstallInterfaces(*sm);
247 VI::InstallInterfaces(*sm, nv_flinger); 251 VI::InstallInterfaces(*sm, *nv_flinger);
248 WLAN::InstallInterfaces(*sm); 252 WLAN::InstallInterfaces(*sm);
249
250 LOG_DEBUG(Service, "initialized OK");
251} 253}
252 254
253/// Shutdown ServiceManager 255Services::~Services() = default;
254void Shutdown() { 256
255 LOG_DEBUG(Service, "shutdown OK");
256}
257} // namespace Service 257} // namespace Service
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index a01ef3353..ed4792289 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -29,7 +29,11 @@ namespace Service {
29 29
30namespace FileSystem { 30namespace FileSystem {
31class FileSystemController; 31class FileSystemController;
32} // namespace FileSystem 32}
33
34namespace NVFlinger {
35class NVFlinger;
36}
33 37
34namespace SM { 38namespace SM {
35class ServiceManager; 39class ServiceManager;
@@ -181,10 +185,17 @@ private:
181 } 185 }
182}; 186};
183 187
184/// Initialize ServiceManager 188/**
185void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system); 189 * The purpose of this class is to own any objects that need to be shared across the other service
190 * implementations. Will be torn down when the global system instance is shutdown.
191 */
192class Services final {
193public:
194 explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system);
195 ~Services();
186 196
187/// Shutdown ServiceManager 197private:
188void Shutdown(); 198 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger;
199};
189 200
190} // namespace Service 201} // namespace Service
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 5b0e371fe..86bd604f4 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -492,8 +492,8 @@ private:
492 492
493class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { 493class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
494public: 494public:
495 explicit IHOSBinderDriver(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 495 explicit IHOSBinderDriver(NVFlinger::NVFlinger& nv_flinger)
496 : ServiceFramework("IHOSBinderDriver"), nv_flinger(std::move(nv_flinger)) { 496 : ServiceFramework("IHOSBinderDriver"), nv_flinger(nv_flinger) {
497 static const FunctionInfo functions[] = { 497 static const FunctionInfo functions[] = {
498 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, 498 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
499 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, 499 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
@@ -530,8 +530,8 @@ private:
530 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, 530 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
531 static_cast<u32>(transaction), flags); 531 static_cast<u32>(transaction), flags);
532 532
533 const auto guard = nv_flinger->Lock(); 533 const auto guard = nv_flinger.Lock();
534 auto& buffer_queue = nv_flinger->FindBufferQueue(id); 534 auto& buffer_queue = nv_flinger.FindBufferQueue(id);
535 535
536 switch (transaction) { 536 switch (transaction) {
537 case TransactionId::Connect: { 537 case TransactionId::Connect: {
@@ -570,8 +570,8 @@ private:
570 [=, this](std::shared_ptr<Kernel::Thread> thread, 570 [=, this](std::shared_ptr<Kernel::Thread> thread,
571 Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { 571 Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) {
572 // Repeat TransactParcel DequeueBuffer when a buffer is available 572 // Repeat TransactParcel DequeueBuffer when a buffer is available
573 const auto guard = nv_flinger->Lock(); 573 const auto guard = nv_flinger.Lock();
574 auto& buffer_queue = nv_flinger->FindBufferQueue(id); 574 auto& buffer_queue = nv_flinger.FindBufferQueue(id);
575 auto result = buffer_queue.DequeueBuffer(width, height); 575 auto result = buffer_queue.DequeueBuffer(width, height);
576 ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); 576 ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
577 577
@@ -676,7 +676,7 @@ private:
676 676
677 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); 677 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
678 678
679 const auto& buffer_queue = nv_flinger->FindBufferQueue(id); 679 const auto& buffer_queue = nv_flinger.FindBufferQueue(id);
680 680
681 // TODO(Subv): Find out what this actually is. 681 // TODO(Subv): Find out what this actually is.
682 IPC::ResponseBuilder rb{ctx, 2, 1}; 682 IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -684,8 +684,8 @@ private:
684 rb.PushCopyObjects(buffer_queue.GetBufferWaitEvent()); 684 rb.PushCopyObjects(buffer_queue.GetBufferWaitEvent());
685 } 685 }
686 686
687 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 687 NVFlinger::NVFlinger& nv_flinger;
688}; // namespace VI 688};
689 689
690class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { 690class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
691public: 691public:
@@ -771,7 +771,7 @@ private:
771 IPC::ResponseBuilder rb{ctx, 6}; 771 IPC::ResponseBuilder rb{ctx, 6};
772 rb.Push(RESULT_SUCCESS); 772 rb.Push(RESULT_SUCCESS);
773 773
774 if (Settings::values.use_docked_mode) { 774 if (Settings::values.use_docked_mode.GetValue()) {
775 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * 775 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
776 static_cast<u32>(Settings::values.resolution_factor.GetValue())); 776 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
777 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * 777 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
@@ -790,8 +790,8 @@ private:
790 790
791class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { 791class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
792public: 792public:
793 explicit IManagerDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 793 explicit IManagerDisplayService(NVFlinger::NVFlinger& nv_flinger)
794 : ServiceFramework("IManagerDisplayService"), nv_flinger(std::move(nv_flinger)) { 794 : ServiceFramework("IManagerDisplayService"), nv_flinger(nv_flinger) {
795 // clang-format off 795 // clang-format off
796 static const FunctionInfo functions[] = { 796 static const FunctionInfo functions[] = {
797 {200, nullptr, "AllocateProcessHeapBlock"}, 797 {200, nullptr, "AllocateProcessHeapBlock"},
@@ -893,7 +893,7 @@ private:
893 "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}", 893 "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}",
894 unknown, display, aruid); 894 unknown, display, aruid);
895 895
896 const auto layer_id = nv_flinger->CreateLayer(display); 896 const auto layer_id = nv_flinger.CreateLayer(display);
897 if (!layer_id) { 897 if (!layer_id) {
898 LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); 898 LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display);
899 IPC::ResponseBuilder rb{ctx, 2}; 899 IPC::ResponseBuilder rb{ctx, 2};
@@ -930,12 +930,12 @@ private:
930 rb.Push(RESULT_SUCCESS); 930 rb.Push(RESULT_SUCCESS);
931 } 931 }
932 932
933 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 933 NVFlinger::NVFlinger& nv_flinger;
934}; 934};
935 935
936class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { 936class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
937public: 937public:
938 explicit IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 938 explicit IApplicationDisplayService(NVFlinger::NVFlinger& nv_flinger);
939 939
940private: 940private:
941 enum class ConvertedScaleMode : u64 { 941 enum class ConvertedScaleMode : u64 {
@@ -1010,7 +1010,7 @@ private:
1010 1010
1011 ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); 1011 ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
1012 1012
1013 const auto display_id = nv_flinger->OpenDisplay(name); 1013 const auto display_id = nv_flinger.OpenDisplay(name);
1014 if (!display_id) { 1014 if (!display_id) {
1015 LOG_ERROR(Service_VI, "Display not found! display_name={}", name); 1015 LOG_ERROR(Service_VI, "Display not found! display_name={}", name);
1016 IPC::ResponseBuilder rb{ctx, 2}; 1016 IPC::ResponseBuilder rb{ctx, 2};
@@ -1110,7 +1110,7 @@ private:
1110 1110
1111 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid); 1111 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid);
1112 1112
1113 const auto display_id = nv_flinger->OpenDisplay(display_name); 1113 const auto display_id = nv_flinger.OpenDisplay(display_name);
1114 if (!display_id) { 1114 if (!display_id) {
1115 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); 1115 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id);
1116 IPC::ResponseBuilder rb{ctx, 2}; 1116 IPC::ResponseBuilder rb{ctx, 2};
@@ -1118,7 +1118,7 @@ private:
1118 return; 1118 return;
1119 } 1119 }
1120 1120
1121 const auto buffer_queue_id = nv_flinger->FindBufferQueueId(*display_id, layer_id); 1121 const auto buffer_queue_id = nv_flinger.FindBufferQueueId(*display_id, layer_id);
1122 if (!buffer_queue_id) { 1122 if (!buffer_queue_id) {
1123 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); 1123 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id);
1124 IPC::ResponseBuilder rb{ctx, 2}; 1124 IPC::ResponseBuilder rb{ctx, 2};
@@ -1138,7 +1138,7 @@ private:
1138 1138
1139 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id); 1139 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id);
1140 1140
1141 nv_flinger->CloseLayer(layer_id); 1141 nv_flinger.CloseLayer(layer_id);
1142 1142
1143 IPC::ResponseBuilder rb{ctx, 2}; 1143 IPC::ResponseBuilder rb{ctx, 2};
1144 rb.Push(RESULT_SUCCESS); 1144 rb.Push(RESULT_SUCCESS);
@@ -1154,7 +1154,7 @@ private:
1154 1154
1155 // TODO(Subv): What's the difference between a Stray and a Managed layer? 1155 // TODO(Subv): What's the difference between a Stray and a Managed layer?
1156 1156
1157 const auto layer_id = nv_flinger->CreateLayer(display_id); 1157 const auto layer_id = nv_flinger.CreateLayer(display_id);
1158 if (!layer_id) { 1158 if (!layer_id) {
1159 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id); 1159 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id);
1160 IPC::ResponseBuilder rb{ctx, 2}; 1160 IPC::ResponseBuilder rb{ctx, 2};
@@ -1162,7 +1162,7 @@ private:
1162 return; 1162 return;
1163 } 1163 }
1164 1164
1165 const auto buffer_queue_id = nv_flinger->FindBufferQueueId(display_id, *layer_id); 1165 const auto buffer_queue_id = nv_flinger.FindBufferQueueId(display_id, *layer_id);
1166 if (!buffer_queue_id) { 1166 if (!buffer_queue_id) {
1167 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); 1167 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id);
1168 IPC::ResponseBuilder rb{ctx, 2}; 1168 IPC::ResponseBuilder rb{ctx, 2};
@@ -1193,7 +1193,7 @@ private:
1193 1193
1194 LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); 1194 LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
1195 1195
1196 const auto vsync_event = nv_flinger->FindVsyncEvent(display_id); 1196 const auto vsync_event = nv_flinger.FindVsyncEvent(display_id);
1197 if (!vsync_event) { 1197 if (!vsync_event) {
1198 LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); 1198 LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
1199 IPC::ResponseBuilder rb{ctx, 2}; 1199 IPC::ResponseBuilder rb{ctx, 2};
@@ -1258,12 +1258,11 @@ private:
1258 } 1258 }
1259 } 1259 }
1260 1260
1261 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 1261 NVFlinger::NVFlinger& nv_flinger;
1262}; 1262};
1263 1263
1264IApplicationDisplayService::IApplicationDisplayService( 1264IApplicationDisplayService::IApplicationDisplayService(NVFlinger::NVFlinger& nv_flinger)
1265 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 1265 : ServiceFramework("IApplicationDisplayService"), nv_flinger(nv_flinger) {
1266 : ServiceFramework("IApplicationDisplayService"), nv_flinger(std::move(nv_flinger)) {
1267 static const FunctionInfo functions[] = { 1266 static const FunctionInfo functions[] = {
1268 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"}, 1267 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
1269 {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"}, 1268 {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
@@ -1304,8 +1303,7 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) {
1304 return false; 1303 return false;
1305} 1304}
1306 1305
1307void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, 1306void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, NVFlinger::NVFlinger& nv_flinger,
1308 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger,
1309 Permission permission) { 1307 Permission permission) {
1310 IPC::RequestParser rp{ctx}; 1308 IPC::RequestParser rp{ctx};
1311 const auto policy = rp.PopEnum<Policy>(); 1309 const auto policy = rp.PopEnum<Policy>();
@@ -1319,11 +1317,10 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
1319 1317
1320 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1318 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1321 rb.Push(RESULT_SUCCESS); 1319 rb.Push(RESULT_SUCCESS);
1322 rb.PushIpcInterface<IApplicationDisplayService>(std::move(nv_flinger)); 1320 rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
1323} 1321}
1324 1322
1325void InstallInterfaces(SM::ServiceManager& service_manager, 1323void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nv_flinger) {
1326 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
1327 std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager); 1324 std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager);
1328 std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager); 1325 std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager);
1329 std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager); 1326 std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index 6b66f8b81..5229fa753 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -43,12 +43,11 @@ enum class Policy {
43}; 43};
44 44
45namespace detail { 45namespace detail {
46void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, 46void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, NVFlinger::NVFlinger& nv_flinger,
47 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger, Permission permission); 47 Permission permission);
48} // namespace detail 48} // namespace detail
49 49
50/// Registers all VI services with the specified service manager. 50/// Registers all VI services with the specified service manager.
51void InstallInterfaces(SM::ServiceManager& service_manager, 51void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nv_flinger);
52 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
53 52
54} // namespace Service::VI 53} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp
index 06070087f..41da3ee93 100644
--- a/src/core/hle/service/vi/vi_m.cpp
+++ b/src/core/hle/service/vi/vi_m.cpp
@@ -8,8 +8,7 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_M::VI_M(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:m"}, nv_flinger{nv_flinger} {
12 : ServiceFramework{"vi:m"}, nv_flinger{std::move(nv_flinger)} {
13 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
14 {2, &VI_M::GetDisplayService, "GetDisplayService"}, 13 {2, &VI_M::GetDisplayService, "GetDisplayService"},
15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 14 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h
index 290e06689..ee2489874 100644
--- a/src/core/hle/service/vi/vi_m.h
+++ b/src/core/hle/service/vi/vi_m.h
@@ -18,13 +18,13 @@ namespace Service::VI {
18 18
19class VI_M final : public ServiceFramework<VI_M> { 19class VI_M final : public ServiceFramework<VI_M> {
20public: 20public:
21 explicit VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_M(NVFlinger::NVFlinger& nv_flinger);
22 ~VI_M() override; 22 ~VI_M() override;
23 23
24private: 24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx); 25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26 26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 27 NVFlinger::NVFlinger& nv_flinger;
28}; 28};
29 29
30} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp
index 57c596cc4..6acb51e2a 100644
--- a/src/core/hle/service/vi/vi_s.cpp
+++ b/src/core/hle/service/vi/vi_s.cpp
@@ -8,8 +8,7 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_S::VI_S(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:s"}, nv_flinger{nv_flinger} {
12 : ServiceFramework{"vi:s"}, nv_flinger{std::move(nv_flinger)} {
13 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
14 {1, &VI_S::GetDisplayService, "GetDisplayService"}, 13 {1, &VI_S::GetDisplayService, "GetDisplayService"},
15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 14 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h
index 47804dc0b..6790673ab 100644
--- a/src/core/hle/service/vi/vi_s.h
+++ b/src/core/hle/service/vi/vi_s.h
@@ -18,13 +18,13 @@ namespace Service::VI {
18 18
19class VI_S final : public ServiceFramework<VI_S> { 19class VI_S final : public ServiceFramework<VI_S> {
20public: 20public:
21 explicit VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_S(NVFlinger::NVFlinger& nv_flinger);
22 ~VI_S() override; 22 ~VI_S() override;
23 23
24private: 24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx); 25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26 26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 27 NVFlinger::NVFlinger& nv_flinger;
28}; 28};
29 29
30} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp
index 6b7329345..44e00a4f6 100644
--- a/src/core/hle/service/vi/vi_u.cpp
+++ b/src/core/hle/service/vi/vi_u.cpp
@@ -8,8 +8,7 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_U::VI_U(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:u"}, nv_flinger{nv_flinger} {
12 : ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
13 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
14 {0, &VI_U::GetDisplayService, "GetDisplayService"}, 13 {0, &VI_U::GetDisplayService, "GetDisplayService"},
15 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 14 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h
index 19bdb73b0..b59f986f0 100644
--- a/src/core/hle/service/vi/vi_u.h
+++ b/src/core/hle/service/vi/vi_u.h
@@ -18,13 +18,13 @@ namespace Service::VI {
18 18
19class VI_U final : public ServiceFramework<VI_U> { 19class VI_U final : public ServiceFramework<VI_U> {
20public: 20public:
21 explicit VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_U(NVFlinger::NVFlinger& nv_flinger);
22 ~VI_U() override; 22 ~VI_U() override;
23 23
24private: 24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx); 25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26 26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 27 NVFlinger::NVFlinger& nv_flinger;
28}; 28};
29 29
30} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 394a1bf26..2002dc4f2 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -114,7 +114,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
114 } 114 }
115 115
116 if (override_update) { 116 if (override_update) {
117 const FileSys::PatchManager patch_manager(metadata.GetTitleID()); 117 const FileSys::PatchManager patch_manager(
118 metadata.GetTitleID(), system.GetFileSystemController(), system.GetContentProvider());
118 dir = patch_manager.PatchExeFS(dir); 119 dir = patch_manager.PatchExeFS(dir);
119 } 120 }
120 121
@@ -160,7 +161,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
160 modules.clear(); 161 modules.clear();
161 const VAddr base_address{process.PageTable().GetCodeRegionStart()}; 162 const VAddr base_address{process.PageTable().GetCodeRegionStart()};
162 VAddr next_load_addr{base_address}; 163 VAddr next_load_addr{base_address};
163 const FileSys::PatchManager pm{metadata.GetTitleID()}; 164 const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(),
165 system.GetContentProvider()};
164 for (const auto& module : static_modules) { 166 for (const auto& module : static_modules) {
165 const FileSys::VirtualFile module_file{dir->GetFile(module)}; 167 const FileSys::VirtualFile module_file{dir->GetFile(module)};
166 if (!module_file) { 168 if (!module_file) {
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 9bc3a8840..d91c15561 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -10,6 +10,7 @@
10#include "common/file_util.h" 10#include "common/file_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/string_util.h" 12#include "common/string_util.h"
13#include "core/core.h"
13#include "core/hle/kernel/process.h" 14#include "core/hle/kernel/process.h"
14#include "core/loader/deconstructed_rom_directory.h" 15#include "core/loader/deconstructed_rom_directory.h"
15#include "core/loader/elf.h" 16#include "core/loader/elf.h"
@@ -194,15 +195,15 @@ AppLoader::~AppLoader() = default;
194 195
195/** 196/**
196 * Get a loader for a file with a specific type 197 * Get a loader for a file with a specific type
197 * @param file The file to load 198 * @param system The system context to use.
198 * @param type The type of the file 199 * @param file The file to retrieve the loader for
199 * @param file the file to retrieve the loader for 200 * @param type The file type
200 * @param type the file type 201 * @param program_index Specifies the index within the container of the program to launch.
201 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type 202 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
202 */ 203 */
203static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileType type) { 204static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file,
205 FileType type, std::size_t program_index) {
204 switch (type) { 206 switch (type) {
205
206 // Standard ELF file format. 207 // Standard ELF file format.
207 case FileType::ELF: 208 case FileType::ELF:
208 return std::make_unique<AppLoader_ELF>(std::move(file)); 209 return std::make_unique<AppLoader_ELF>(std::move(file));
@@ -221,7 +222,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
221 222
222 // NX XCI (nX Card Image) file format. 223 // NX XCI (nX Card Image) file format.
223 case FileType::XCI: 224 case FileType::XCI:
224 return std::make_unique<AppLoader_XCI>(std::move(file)); 225 return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(),
226 system.GetContentProvider(), program_index);
225 227
226 // NX NAX (NintendoAesXts) file format. 228 // NX NAX (NintendoAesXts) file format.
227 case FileType::NAX: 229 case FileType::NAX:
@@ -229,7 +231,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
229 231
230 // NX NSP (Nintendo Submission Package) file format 232 // NX NSP (Nintendo Submission Package) file format
231 case FileType::NSP: 233 case FileType::NSP:
232 return std::make_unique<AppLoader_NSP>(std::move(file)); 234 return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(),
235 system.GetContentProvider(), program_index);
233 236
234 // NX KIP (Kernel Internal Process) file format 237 // NX KIP (Kernel Internal Process) file format
235 case FileType::KIP: 238 case FileType::KIP:
@@ -244,20 +247,22 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
244 } 247 }
245} 248}
246 249
247std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file) { 250std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
251 std::size_t program_index) {
248 FileType type = IdentifyFile(file); 252 FileType type = IdentifyFile(file);
249 FileType filename_type = GuessFromFilename(file->GetName()); 253 const FileType filename_type = GuessFromFilename(file->GetName());
250 254
251 // Special case: 00 is either a NCA or NAX. 255 // Special case: 00 is either a NCA or NAX.
252 if (type != filename_type && !(file->GetName() == "00" && type == FileType::NAX)) { 256 if (type != filename_type && !(file->GetName() == "00" && type == FileType::NAX)) {
253 LOG_WARNING(Loader, "File {} has a different type than its extension.", file->GetName()); 257 LOG_WARNING(Loader, "File {} has a different type than its extension.", file->GetName());
254 if (FileType::Unknown == type) 258 if (FileType::Unknown == type) {
255 type = filename_type; 259 type = filename_type;
260 }
256 } 261 }
257 262
258 LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type)); 263 LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
259 264
260 return GetFileLoader(std::move(file), type); 265 return GetFileLoader(system, std::move(file), type, program_index);
261} 266}
262 267
263} // namespace Loader 268} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index ac60b097a..36e79e71d 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -290,9 +290,14 @@ protected:
290 290
291/** 291/**
292 * Identifies a bootable file and return a suitable loader 292 * Identifies a bootable file and return a suitable loader
293 * @param file The bootable file 293 *
294 * @return the best loader for this file 294 * @param system The system context.
295 * @param file The bootable file.
296 * @param program_index Specifies the index within the container of the program to launch.
297 *
298 * @return the best loader for this file.
295 */ 299 */
296std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file); 300std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
301 std::size_t program_index = 0);
297 302
298} // namespace Loader 303} // namespace Loader
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 497f438a1..aa85c1a29 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -149,7 +149,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::S
149 // Apply cheats if they exist and the program has a valid title ID 149 // Apply cheats if they exist and the program has a valid title ID
150 if (pm) { 150 if (pm) {
151 system.SetCurrentProcessBuildID(nso_header.build_id); 151 system.SetCurrentProcessBuildID(nso_header.build_id);
152 const auto cheats = pm->CreateCheatList(system, nso_header.build_id); 152 const auto cheats = pm->CreateCheatList(nso_header.build_id);
153 if (!cheats.empty()) { 153 if (!cheats.empty()) {
154 system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size); 154 system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size);
155 } 155 }
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 15e528fa8..928f64c8c 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -21,26 +21,34 @@
21 21
22namespace Loader { 22namespace Loader {
23 23
24AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file) 24AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file,
25 : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file)), 25 const Service::FileSystem::FileSystemController& fsc,
26 const FileSys::ContentProvider& content_provider,
27 std::size_t program_index)
28 : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file, program_index)),
26 title_id(nsp->GetProgramTitleID()) { 29 title_id(nsp->GetProgramTitleID()) {
27 30
28 if (nsp->GetStatus() != ResultStatus::Success) 31 if (nsp->GetStatus() != ResultStatus::Success) {
29 return; 32 return;
33 }
30 34
31 if (nsp->IsExtractedType()) { 35 if (nsp->IsExtractedType()) {
32 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); 36 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
33 } else { 37 } else {
34 const auto control_nca = 38 const auto control_nca =
35 nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control); 39 nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
36 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) 40 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) {
37 return; 41 return;
42 }
38 43
39 std::tie(nacp_file, icon_file) = 44 std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] {
40 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca); 45 const FileSys::PatchManager pm{nsp->GetProgramTitleID(), fsc, content_provider};
46 return pm.ParseControlNCA(*control_nca);
47 }();
41 48
42 if (title_id == 0) 49 if (title_id == 0) {
43 return; 50 return;
51 }
44 52
45 secondary_loader = std::make_unique<AppLoader_NCA>( 53 secondary_loader = std::make_unique<AppLoader_NCA>(
46 nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program)); 54 nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index b27deb686..f0518ac47 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -9,15 +9,16 @@
9#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
10#include "core/loader/loader.h" 10#include "core/loader/loader.h"
11 11
12namespace Core {
13class System;
14}
15
16namespace FileSys { 12namespace FileSys {
13class ContentProvider;
17class NACP; 14class NACP;
18class NSP; 15class NSP;
19} // namespace FileSys 16} // namespace FileSys
20 17
18namespace Service::FileSystem {
19class FileSystemController;
20}
21
21namespace Loader { 22namespace Loader {
22 23
23class AppLoader_NCA; 24class AppLoader_NCA;
@@ -25,7 +26,10 @@ class AppLoader_NCA;
25/// Loads an XCI file 26/// Loads an XCI file
26class AppLoader_NSP final : public AppLoader { 27class AppLoader_NSP final : public AppLoader {
27public: 28public:
28 explicit AppLoader_NSP(FileSys::VirtualFile file); 29 explicit AppLoader_NSP(FileSys::VirtualFile file,
30 const Service::FileSystem::FileSystemController& fsc,
31 const FileSys::ContentProvider& content_provider,
32 std::size_t program_index);
29 ~AppLoader_NSP() override; 33 ~AppLoader_NSP() override;
30 34
31 /** 35 /**
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 25e83af0f..aaa250cea 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -20,18 +20,25 @@
20 20
21namespace Loader { 21namespace Loader {
22 22
23AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) 23AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file,
24 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), 24 const Service::FileSystem::FileSystemController& fsc,
25 const FileSys::ContentProvider& content_provider,
26 std::size_t program_index)
27 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file, program_index)),
25 nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) { 28 nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
26 if (xci->GetStatus() != ResultStatus::Success) 29 if (xci->GetStatus() != ResultStatus::Success) {
27 return; 30 return;
31 }
28 32
29 const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control); 33 const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
30 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) 34 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) {
31 return; 35 return;
36 }
32 37
33 std::tie(nacp_file, icon_file) = 38 std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] {
34 FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(*control_nca); 39 const FileSys::PatchManager pm{xci->GetProgramTitleID(), fsc, content_provider};
40 return pm.ParseControlNCA(*control_nca);
41 }();
35} 42}
36 43
37AppLoader_XCI::~AppLoader_XCI() = default; 44AppLoader_XCI::~AppLoader_XCI() = default;
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 04aea286f..764dc8328 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -9,15 +9,16 @@
9#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
10#include "core/loader/loader.h" 10#include "core/loader/loader.h"
11 11
12namespace Core {
13class System;
14}
15
16namespace FileSys { 12namespace FileSys {
13class ContentProvider;
17class NACP; 14class NACP;
18class XCI; 15class XCI;
19} // namespace FileSys 16} // namespace FileSys
20 17
18namespace Service::FileSystem {
19class FileSystemController;
20}
21
21namespace Loader { 22namespace Loader {
22 23
23class AppLoader_NCA; 24class AppLoader_NCA;
@@ -25,7 +26,10 @@ class AppLoader_NCA;
25/// Loads an XCI file 26/// Loads an XCI file
26class AppLoader_XCI final : public AppLoader { 27class AppLoader_XCI final : public AppLoader {
27public: 28public:
28 explicit AppLoader_XCI(FileSys::VirtualFile file); 29 explicit AppLoader_XCI(FileSys::VirtualFile file,
30 const Service::FileSystem::FileSystemController& fsc,
31 const FileSys::ContentProvider& content_provider,
32 std::size_t program_index);
29 ~AppLoader_XCI() override; 33 ~AppLoader_XCI() override;
30 34
31 /** 35 /**
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index a99d3cf5a..aadbc3932 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -49,13 +49,14 @@ void LogSettings() {
49 }; 49 };
50 50
51 LOG_INFO(Config, "yuzu Configuration:"); 51 LOG_INFO(Config, "yuzu Configuration:");
52 log_setting("Controls_UseDockedMode", values.use_docked_mode); 52 log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue());
53 log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); 53 log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0));
54 log_setting("System_CurrentUser", values.current_user); 54 log_setting("System_CurrentUser", values.current_user);
55 log_setting("System_LanguageIndex", values.language_index.GetValue()); 55 log_setting("System_LanguageIndex", values.language_index.GetValue());
56 log_setting("System_RegionIndex", values.region_index.GetValue()); 56 log_setting("System_RegionIndex", values.region_index.GetValue());
57 log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue()); 57 log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue());
58 log_setting("Core_UseMultiCore", values.use_multi_core.GetValue()); 58 log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
59 log_setting("CPU_Accuracy", values.cpu_accuracy);
59 log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue()); 60 log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue());
60 log_setting("Renderer_UseFrameLimit", values.use_frame_limit.GetValue()); 61 log_setting("Renderer_UseFrameLimit", values.use_frame_limit.GetValue());
61 log_setting("Renderer_FrameLimit", values.frame_limit.GetValue()); 62 log_setting("Renderer_FrameLimit", values.frame_limit.GetValue());
@@ -144,6 +145,12 @@ void RestoreGlobalState() {
144 values.rng_seed.SetGlobal(true); 145 values.rng_seed.SetGlobal(true);
145 values.custom_rtc.SetGlobal(true); 146 values.custom_rtc.SetGlobal(true);
146 values.sound_index.SetGlobal(true); 147 values.sound_index.SetGlobal(true);
148
149 // Controls
150 values.players.SetGlobal(true);
151 values.use_docked_mode.SetGlobal(true);
152 values.vibration_enabled.SetGlobal(true);
153 values.motion_enabled.SetGlobal(true);
147} 154}
148 155
149void Sanitize() { 156void Sanitize() {
diff --git a/src/core/settings.h b/src/core/settings.h
index 28616a574..1143aba5d 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -65,6 +65,38 @@ private:
65 Type local{}; 65 Type local{};
66}; 66};
67 67
68/**
69 * The InputSetting class allows for getting a reference to either the global or local members.
70 * This is required as we cannot easily modify the values of user-defined types within containers
71 * using the SetValue() member function found in the Setting class. The primary purpose of this
72 * class is to store an array of 10 PlayerInput structs for both the global and local (per-game)
73 * setting and allows for easily accessing and modifying both settings.
74 */
75template <typename Type>
76class InputSetting final {
77public:
78 InputSetting() = default;
79 explicit InputSetting(Type val) : global{val} {}
80 ~InputSetting() = default;
81 void SetGlobal(bool to_global) {
82 use_global = to_global;
83 }
84 bool UsingGlobal() const {
85 return use_global;
86 }
87 Type& GetValue(bool need_global = false) {
88 if (use_global || need_global) {
89 return global;
90 }
91 return local;
92 }
93
94private:
95 bool use_global = true;
96 Type global{};
97 Type local{};
98};
99
68struct TouchFromButtonMap { 100struct TouchFromButtonMap {
69 std::string name; 101 std::string name;
70 std::vector<std::string> buttons; 102 std::vector<std::string> buttons;
@@ -133,9 +165,18 @@ struct Values {
133 Setting<s32> sound_index; 165 Setting<s32> sound_index;
134 166
135 // Controls 167 // Controls
136 std::array<PlayerInput, 10> players; 168 InputSetting<std::array<PlayerInput, 10>> players;
169
170 Setting<bool> use_docked_mode;
137 171
138 bool use_docked_mode; 172 Setting<bool> vibration_enabled;
173 Setting<bool> enable_accurate_vibrations;
174
175 Setting<bool> motion_enabled;
176 std::string motion_device;
177 std::string udp_input_address;
178 u16 udp_input_port;
179 u8 udp_pad_index;
139 180
140 bool mouse_enabled; 181 bool mouse_enabled;
141 std::string mouse_device; 182 std::string mouse_device;
@@ -149,20 +190,15 @@ struct Values {
149 ButtonsRaw debug_pad_buttons; 190 ButtonsRaw debug_pad_buttons;
150 AnalogsRaw debug_pad_analogs; 191 AnalogsRaw debug_pad_analogs;
151 192
152 bool vibration_enabled;
153
154 bool motion_enabled;
155 std::string motion_device;
156 std::string touch_device;
157 TouchscreenInput touchscreen; 193 TouchscreenInput touchscreen;
158 std::atomic_bool is_device_reload_pending{true}; 194
159 bool use_touch_from_button; 195 bool use_touch_from_button;
196 std::string touch_device;
160 int touch_from_button_map_index; 197 int touch_from_button_map_index;
161 std::string udp_input_address;
162 u16 udp_input_port;
163 u8 udp_pad_index;
164 std::vector<TouchFromButtonMap> touch_from_button_maps; 198 std::vector<TouchFromButtonMap> touch_from_button_maps;
165 199
200 std::atomic_bool is_device_reload_pending{true};
201
166 // Data Storage 202 // Data Storage
167 bool use_virtual_sd; 203 bool use_virtual_sd;
168 bool gamecard_inserted; 204 bool gamecard_inserted;
@@ -179,6 +215,7 @@ struct Values {
179 bool reporting_services; 215 bool reporting_services;
180 bool quest_flag; 216 bool quest_flag;
181 bool disable_macro_jit; 217 bool disable_macro_jit;
218 bool extended_logging;
182 219
183 // Misceallaneous 220 // Misceallaneous
184 std::string log_filter; 221 std::string log_filter;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index ebc19e18a..d11b15f38 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -147,7 +147,9 @@ TelemetrySession::~TelemetrySession() {
147 } 147 }
148} 148}
149 149
150void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { 150void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
151 const Service::FileSystem::FileSystemController& fsc,
152 const FileSys::ContentProvider& content_provider) {
151 // Log one-time top-level information 153 // Log one-time top-level information
152 AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId()); 154 AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId());
153 155
@@ -167,7 +169,10 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
167 app_loader.ReadTitle(name); 169 app_loader.ReadTitle(name);
168 170
169 if (name.empty()) { 171 if (name.empty()) {
170 const auto metadata = FileSys::PatchManager(program_id).GetControlMetadata(); 172 const auto metadata = [&content_provider, &fsc, program_id] {
173 const FileSys::PatchManager pm{program_id, fsc, content_provider};
174 return pm.GetControlMetadata();
175 }();
171 if (metadata.first != nullptr) { 176 if (metadata.first != nullptr) {
172 name = metadata.first->GetApplicationName(); 177 name = metadata.first->GetApplicationName();
173 } 178 }
@@ -213,7 +218,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
213 Settings::values.use_assembly_shaders.GetValue()); 218 Settings::values.use_assembly_shaders.GetValue());
214 AddField(field_type, "Renderer_UseAsynchronousShaders", 219 AddField(field_type, "Renderer_UseAsynchronousShaders",
215 Settings::values.use_asynchronous_shaders.GetValue()); 220 Settings::values.use_asynchronous_shaders.GetValue());
216 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); 221 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode.GetValue());
217} 222}
218 223
219bool TelemetrySession::SubmitTestcase() { 224bool TelemetrySession::SubmitTestcase() {
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index 66789d4bd..6f3d45bea 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -7,10 +7,18 @@
7#include <string> 7#include <string>
8#include "common/telemetry.h" 8#include "common/telemetry.h"
9 9
10namespace FileSys {
11class ContentProvider;
12}
13
10namespace Loader { 14namespace Loader {
11class AppLoader; 15class AppLoader;
12} 16}
13 17
18namespace Service::FileSystem {
19class FileSystemController;
20}
21
14namespace Core { 22namespace Core {
15 23
16/** 24/**
@@ -40,10 +48,14 @@ public:
40 * - Title file format 48 * - Title file format
41 * - Miscellaneous settings values. 49 * - Miscellaneous settings values.
42 * 50 *
43 * @param app_loader The application loader to use to retrieve 51 * @param app_loader The application loader to use to retrieve
44 * title-specific information. 52 * title-specific information.
53 * @param fsc Filesystem controller to use to retrieve info.
54 * @param content_provider Content provider to use to retrieve info.
45 */ 55 */
46 void AddInitialInfo(Loader::AppLoader& app_loader); 56 void AddInitialInfo(Loader::AppLoader& app_loader,
57 const Service::FileSystem::FileSystemController& fsc,
58 const FileSys::ContentProvider& content_provider);
47 59
48 /** 60 /**
49 * Wrapper around the Telemetry::FieldCollection::AddField method. 61 * Wrapper around the Telemetry::FieldCollection::AddField method.
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 7b39a38c1..1d1b2e08a 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -31,6 +31,9 @@ add_library(input_common STATIC
31 31
32if (MSVC) 32if (MSVC)
33 target_compile_options(input_common PRIVATE 33 target_compile_options(input_common PRIVATE
34 /W4
35 /WX
36
34 # 'expression' : signed/unsigned mismatch 37 # 'expression' : signed/unsigned mismatch
35 /we4018 38 /we4018
36 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point) 39 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point)
@@ -46,6 +49,7 @@ if (MSVC)
46 ) 49 )
47else() 50else()
48 target_compile_options(input_common PRIVATE 51 target_compile_options(input_common PRIVATE
52 -Werror
49 -Werror=conversion 53 -Werror=conversion
50 -Werror=ignored-qualifiers 54 -Werror=ignored-qualifiers
51 -Werror=implicit-fallthrough 55 -Werror=implicit-fallthrough
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index b912188b6..d80195c82 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -230,10 +230,8 @@ void Adapter::SendVibrations() {
230 vibration_changed = false; 230 vibration_changed = false;
231} 231}
232 232
233bool Adapter::RumblePlay(std::size_t port, f32 amplitude) { 233bool Adapter::RumblePlay(std::size_t port, u8 amplitude) {
234 amplitude = std::clamp(amplitude, 0.0f, 1.0f); 234 pads[port].rumble_amplitude = amplitude;
235 const auto raw_amp = static_cast<u8>(amplitude * 0x8);
236 pads[port].rumble_amplitude = raw_amp;
237 235
238 return rumble_enabled; 236 return rumble_enabled;
239} 237}
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index d28dcfad3..f1256c9da 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -77,8 +77,8 @@ public:
77 Adapter(); 77 Adapter();
78 ~Adapter(); 78 ~Adapter();
79 79
80 /// Request a vibration for a controlelr 80 /// Request a vibration for a controller
81 bool RumblePlay(std::size_t port, f32 amplitude); 81 bool RumblePlay(std::size_t port, u8 amplitude);
82 82
83 /// Used for polling 83 /// Used for polling
84 void BeginConfiguration(); 84 void BeginConfiguration();
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index 6bd6f57fc..4d1052414 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -15,7 +15,7 @@ namespace InputCommon {
15 15
16class GCButton final : public Input::ButtonDevice { 16class GCButton final : public Input::ButtonDevice {
17public: 17public:
18 explicit GCButton(u32 port_, s32 button_, GCAdapter::Adapter* adapter) 18 explicit GCButton(u32 port_, s32 button_, const GCAdapter::Adapter* adapter)
19 : port(port_), button(button_), gcadapter(adapter) {} 19 : port(port_), button(button_), gcadapter(adapter) {}
20 20
21 ~GCButton() override; 21 ~GCButton() override;
@@ -27,18 +27,10 @@ public:
27 return false; 27 return false;
28 } 28 }
29 29
30 bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
31 const float amplitude = amp_high + amp_low > 2.0f ? 1.0f : (amp_high + amp_low) * 0.5f;
32 const auto new_amp =
33 static_cast<f32>(pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f)));
34
35 return gcadapter->RumblePlay(port, new_amp);
36 }
37
38private: 30private:
39 const u32 port; 31 const u32 port;
40 const s32 button; 32 const s32 button;
41 GCAdapter::Adapter* gcadapter; 33 const GCAdapter::Adapter* gcadapter;
42}; 34};
43 35
44class GCAxisButton final : public Input::ButtonDevice { 36class GCAxisButton final : public Input::ButtonDevice {
@@ -104,7 +96,6 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param
104 adapter.get()); 96 adapter.get());
105 } 97 }
106 98
107 UNREACHABLE();
108 return nullptr; 99 return nullptr;
109} 100}
110 101
@@ -299,4 +290,43 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
299 return params; 290 return params;
300} 291}
301 292
293class GCVibration final : public Input::VibrationDevice {
294public:
295 explicit GCVibration(u32 port_, GCAdapter::Adapter* adapter)
296 : port(port_), gcadapter(adapter) {}
297
298 u8 GetStatus() const override {
299 return gcadapter->RumblePlay(port, 0);
300 }
301
302 bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
303 [[maybe_unused]] f32 freq_high) const override {
304 const auto mean_amplitude = (amp_low + amp_high) * 0.5f;
305 const auto processed_amplitude =
306 static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8);
307
308 return gcadapter->RumblePlay(port, processed_amplitude);
309 }
310
311private:
312 const u32 port;
313 GCAdapter::Adapter* gcadapter;
314};
315
316/// An vibration device factory that creates vibration devices from GC Adapter
317GCVibrationFactory::GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
318 : adapter(std::move(adapter_)) {}
319
320/**
321 * Creates a vibration device from a joystick
322 * @param params contains parameters for creating the device:
323 * - "port": the nth gcpad on the adapter
324 */
325std::unique_ptr<Input::VibrationDevice> GCVibrationFactory::Create(
326 const Common::ParamPackage& params) {
327 const auto port = static_cast<u32>(params.Get("port", 0));
328
329 return std::make_unique<GCVibration>(port, adapter.get());
330}
331
302} // namespace InputCommon 332} // namespace InputCommon
diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h
index 0527f328f..d1271e3ea 100644
--- a/src/input_common/gcadapter/gc_poller.h
+++ b/src/input_common/gcadapter/gc_poller.h
@@ -64,4 +64,15 @@ private:
64 bool polling = false; 64 bool polling = false;
65}; 65};
66 66
67/// A vibration device factory creates vibration devices from GC Adapter
68class GCVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
69public:
70 explicit GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
71
72 std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override;
73
74private:
75 std::shared_ptr<GCAdapter::Adapter> adapter;
76};
77
67} // namespace InputCommon 78} // namespace InputCommon
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index d32fd8b81..e59ad4ff5 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -28,6 +28,8 @@ struct InputSubsystem::Impl {
28 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons); 28 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
29 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter); 29 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
30 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog); 30 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
31 gcvibration = std::make_shared<GCVibrationFactory>(gcadapter);
32 Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration);
31 33
32 keyboard = std::make_shared<Keyboard>(); 34 keyboard = std::make_shared<Keyboard>();
33 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); 35 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
@@ -64,9 +66,11 @@ struct InputSubsystem::Impl {
64#endif 66#endif
65 Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); 67 Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
66 Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); 68 Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
69 Input::UnregisterFactory<Input::VibrationDevice>("gcpad");
67 70
68 gcbuttons.reset(); 71 gcbuttons.reset();
69 gcanalog.reset(); 72 gcanalog.reset();
73 gcvibration.reset();
70 74
71 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); 75 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
72 Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); 76 Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
@@ -78,7 +82,7 @@ struct InputSubsystem::Impl {
78 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { 82 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
79 std::vector<Common::ParamPackage> devices = { 83 std::vector<Common::ParamPackage> devices = {
80 Common::ParamPackage{{"display", "Any"}, {"class", "any"}}, 84 Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
81 Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "key"}}, 85 Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}},
82 }; 86 };
83#ifdef HAVE_SDL2 87#ifdef HAVE_SDL2
84 auto sdl_devices = sdl->GetInputDevices(); 88 auto sdl_devices = sdl->GetInputDevices();
@@ -96,10 +100,6 @@ struct InputSubsystem::Impl {
96 if (!params.Has("class") || params.Get("class", "") == "any") { 100 if (!params.Has("class") || params.Get("class", "") == "any") {
97 return {}; 101 return {};
98 } 102 }
99 if (params.Get("class", "") == "key") {
100 // TODO consider returning the SDL key codes for the default keybindings
101 return {};
102 }
103 if (params.Get("class", "") == "gcpad") { 103 if (params.Get("class", "") == "gcpad") {
104 return gcadapter->GetAnalogMappingForDevice(params); 104 return gcadapter->GetAnalogMappingForDevice(params);
105 } 105 }
@@ -116,10 +116,6 @@ struct InputSubsystem::Impl {
116 if (!params.Has("class") || params.Get("class", "") == "any") { 116 if (!params.Has("class") || params.Get("class", "") == "any") {
117 return {}; 117 return {};
118 } 118 }
119 if (params.Get("class", "") == "key") {
120 // TODO consider returning the SDL key codes for the default keybindings
121 return {};
122 }
123 if (params.Get("class", "") == "gcpad") { 119 if (params.Get("class", "") == "gcpad") {
124 return gcadapter->GetButtonMappingForDevice(params); 120 return gcadapter->GetButtonMappingForDevice(params);
125 } 121 }
@@ -150,6 +146,7 @@ struct InputSubsystem::Impl {
150#endif 146#endif
151 std::shared_ptr<GCButtonFactory> gcbuttons; 147 std::shared_ptr<GCButtonFactory> gcbuttons;
152 std::shared_ptr<GCAnalogFactory> gcanalog; 148 std::shared_ptr<GCAnalogFactory> gcanalog;
149 std::shared_ptr<GCVibrationFactory> gcvibration;
153 std::shared_ptr<UDPMotionFactory> udpmotion; 150 std::shared_ptr<UDPMotionFactory> udpmotion;
154 std::shared_ptr<UDPTouchFactory> udptouch; 151 std::shared_ptr<UDPTouchFactory> udptouch;
155 std::shared_ptr<CemuhookUDP::Client> udp; 152 std::shared_ptr<CemuhookUDP::Client> udp;
diff --git a/src/input_common/motion_input.h b/src/input_common/motion_input.h
index abb957f04..efe74cf19 100644
--- a/src/input_common/motion_input.h
+++ b/src/input_common/motion_input.h
@@ -13,7 +13,7 @@ namespace InputCommon {
13 13
14class MotionInput { 14class MotionInput {
15public: 15public:
16 MotionInput(f32 new_kp, f32 new_ki, f32 new_kd); 16 explicit MotionInput(f32 new_kp, f32 new_ki, f32 new_kd);
17 17
18 MotionInput(const MotionInput&) = default; 18 MotionInput(const MotionInput&) = default;
19 MotionInput& operator=(const MotionInput&) = default; 19 MotionInput& operator=(const MotionInput&) = default;
@@ -33,16 +33,17 @@ public:
33 void UpdateRotation(u64 elapsed_time); 33 void UpdateRotation(u64 elapsed_time);
34 void UpdateOrientation(u64 elapsed_time); 34 void UpdateOrientation(u64 elapsed_time);
35 35
36 std::array<Common::Vec3f, 3> GetOrientation() const; 36 [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
37 Common::Vec3f GetAcceleration() const; 37 [[nodiscard]] Common::Vec3f GetAcceleration() const;
38 Common::Vec3f GetGyroscope() const; 38 [[nodiscard]] Common::Vec3f GetGyroscope() const;
39 Common::Vec3f GetRotations() const; 39 [[nodiscard]] Common::Vec3f GetRotations() const;
40 Common::Quaternion<f32> GetQuaternion() const; 40 [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
41 Input::MotionStatus GetMotion() const; 41 [[nodiscard]] Input::MotionStatus GetMotion() const;
42 Input::MotionStatus GetRandomMotion(int accel_magnitude, int gyro_magnitude) const; 42 [[nodiscard]] Input::MotionStatus GetRandomMotion(int accel_magnitude,
43 43 int gyro_magnitude) const;
44 bool IsMoving(f32 sensitivity) const; 44
45 bool IsCalibrated(f32 sensitivity) const; 45 [[nodiscard]] bool IsMoving(f32 sensitivity) const;
46 [[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
46 47
47private: 48private:
48 void ResetOrientation(); 49 void ResetOrientation();
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index f3554be9a..42bbf14d4 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -23,7 +23,7 @@ public:
23 /// Unregisters SDL device factories and shut them down. 23 /// Unregisters SDL device factories and shut them down.
24 virtual ~State() = default; 24 virtual ~State() = default;
25 25
26 virtual Pollers GetPollers(Polling::DeviceType type) { 26 virtual Pollers GetPollers(Polling::DeviceType) {
27 return {}; 27 return {};
28 } 28 }
29 29
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 10883e2d9..7827e324c 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -80,30 +80,13 @@ public:
80 return static_cast<float>(state.axes.at(axis)) / (32767.0f * range); 80 return static_cast<float>(state.axes.at(axis)) / (32767.0f * range);
81 } 81 }
82 82
83 bool RumblePlay(f32 amp_low, f32 amp_high, u32 time) { 83 bool RumblePlay(u16 amp_low, u16 amp_high) {
84 const u16 raw_amp_low = static_cast<u16>(amp_low * 0xFFFF); 84 if (sdl_controller) {
85 const u16 raw_amp_high = static_cast<u16>(amp_high * 0xFFFF); 85 return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0;
86 // Lower drastically the number of state changes 86 } else if (sdl_joystick) {
87 if (raw_amp_low >> 11 == last_state_rumble_low >> 11 && 87 return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0;
88 raw_amp_high >> 11 == last_state_rumble_high >> 11) {
89 if (raw_amp_low + raw_amp_high != 0 ||
90 last_state_rumble_low + last_state_rumble_high == 0) {
91 return false;
92 }
93 }
94 // Don't change state if last vibration was < 20ms
95 const auto now = std::chrono::system_clock::now();
96 if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_vibration) <
97 std::chrono::milliseconds(20)) {
98 return raw_amp_low + raw_amp_high == 0;
99 } 88 }
100 89
101 last_vibration = now;
102 last_state_rumble_low = raw_amp_low;
103 last_state_rumble_high = raw_amp_high;
104 if (sdl_joystick) {
105 SDL_JoystickRumble(sdl_joystick.get(), raw_amp_low, raw_amp_high, time);
106 }
107 return false; 90 return false;
108 } 91 }
109 92
@@ -172,9 +155,6 @@ private:
172 } state; 155 } state;
173 std::string guid; 156 std::string guid;
174 int port; 157 int port;
175 u16 last_state_rumble_high = 0;
176 u16 last_state_rumble_low = 0;
177 std::chrono::time_point<std::chrono::system_clock> last_vibration;
178 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; 158 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
179 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; 159 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
180 mutable std::mutex mutex; 160 mutable std::mutex mutex;
@@ -327,12 +307,6 @@ public:
327 return joystick->GetButton(button); 307 return joystick->GetButton(button);
328 } 308 }
329 309
330 bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
331 const f32 new_amp_low = pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f));
332 const f32 new_amp_high = pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f));
333 return joystick->RumblePlay(new_amp_low, new_amp_high, 250);
334 }
335
336private: 310private:
337 std::shared_ptr<SDLJoystick> joystick; 311 std::shared_ptr<SDLJoystick> joystick;
338 int button; 312 int button;
@@ -416,6 +390,32 @@ private:
416 const float range; 390 const float range;
417}; 391};
418 392
393class SDLVibration final : public Input::VibrationDevice {
394public:
395 explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_)
396 : joystick(std::move(joystick_)) {}
397
398 u8 GetStatus() const override {
399 joystick->RumblePlay(1, 1);
400 return joystick->RumblePlay(0, 0);
401 }
402
403 bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
404 [[maybe_unused]] f32 freq_high) const override {
405 const auto process_amplitude = [](f32 amplitude) {
406 return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF);
407 };
408
409 const auto processed_amp_low = process_amplitude(amp_low);
410 const auto processed_amp_high = process_amplitude(amp_high);
411
412 return joystick->RumblePlay(processed_amp_low, processed_amp_high);
413 }
414
415private:
416 std::shared_ptr<SDLJoystick> joystick;
417};
418
419class SDLDirectionMotion final : public Input::MotionDevice { 419class SDLDirectionMotion final : public Input::MotionDevice {
420public: 420public:
421 explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_) 421 explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
@@ -558,7 +558,7 @@ class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
558public: 558public:
559 explicit SDLAnalogFactory(SDLState& state_) : state(state_) {} 559 explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
560 /** 560 /**
561 * Creates analog device from joystick axes 561 * Creates an analog device from joystick axes
562 * @param params contains parameters for creating the device: 562 * @param params contains parameters for creating the device:
563 * - "guid": the guid of the joystick to bind 563 * - "guid": the guid of the joystick to bind
564 * - "port": the nth joystick of the same type 564 * - "port": the nth joystick of the same type
@@ -584,6 +584,26 @@ private:
584 SDLState& state; 584 SDLState& state;
585}; 585};
586 586
587/// An vibration device factory that creates vibration devices from SDL joystick
588class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
589public:
590 explicit SDLVibrationFactory(SDLState& state_) : state(state_) {}
591 /**
592 * Creates a vibration device from a joystick
593 * @param params contains parameters for creating the device:
594 * - "guid": the guid of the joystick to bind
595 * - "port": the nth joystick of the same type
596 */
597 std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override {
598 const std::string guid = params.Get("guid", "0");
599 const int port = params.Get("port", 0);
600 return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port));
601 }
602
603private:
604 SDLState& state;
605};
606
587/// A motion device factory that creates motion devices from SDL joystick 607/// A motion device factory that creates motion devices from SDL joystick
588class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> { 608class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
589public: 609public:
@@ -650,11 +670,13 @@ private:
650 670
651SDLState::SDLState() { 671SDLState::SDLState() {
652 using namespace Input; 672 using namespace Input;
653 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
654 button_factory = std::make_shared<SDLButtonFactory>(*this); 673 button_factory = std::make_shared<SDLButtonFactory>(*this);
674 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
675 vibration_factory = std::make_shared<SDLVibrationFactory>(*this);
655 motion_factory = std::make_shared<SDLMotionFactory>(*this); 676 motion_factory = std::make_shared<SDLMotionFactory>(*this);
656 RegisterFactory<AnalogDevice>("sdl", analog_factory);
657 RegisterFactory<ButtonDevice>("sdl", button_factory); 677 RegisterFactory<ButtonDevice>("sdl", button_factory);
678 RegisterFactory<AnalogDevice>("sdl", analog_factory);
679 RegisterFactory<VibrationDevice>("sdl", vibration_factory);
658 RegisterFactory<MotionDevice>("sdl", motion_factory); 680 RegisterFactory<MotionDevice>("sdl", motion_factory);
659 681
660 // If the frontend is going to manage the event loop, then we don't start one here 682 // If the frontend is going to manage the event loop, then we don't start one here
@@ -676,7 +698,7 @@ SDLState::SDLState() {
676 using namespace std::chrono_literals; 698 using namespace std::chrono_literals;
677 while (initialized) { 699 while (initialized) {
678 SDL_PumpEvents(); 700 SDL_PumpEvents();
679 std::this_thread::sleep_for(5ms); 701 std::this_thread::sleep_for(1ms);
680 } 702 }
681 }); 703 });
682 } 704 }
@@ -691,6 +713,7 @@ SDLState::~SDLState() {
691 using namespace Input; 713 using namespace Input;
692 UnregisterFactory<ButtonDevice>("sdl"); 714 UnregisterFactory<ButtonDevice>("sdl");
693 UnregisterFactory<AnalogDevice>("sdl"); 715 UnregisterFactory<AnalogDevice>("sdl");
716 UnregisterFactory<VibrationDevice>("sdl");
694 UnregisterFactory<MotionDevice>("sdl"); 717 UnregisterFactory<MotionDevice>("sdl");
695 718
696 CloseJoysticks(); 719 CloseJoysticks();
@@ -842,6 +865,8 @@ Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Eve
842Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid, 865Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
843 const SDL_GameControllerButtonBind& binding) { 866 const SDL_GameControllerButtonBind& binding) {
844 switch (binding.bindType) { 867 switch (binding.bindType) {
868 case SDL_CONTROLLER_BINDTYPE_NONE:
869 break;
845 case SDL_CONTROLLER_BINDTYPE_AXIS: 870 case SDL_CONTROLLER_BINDTYPE_AXIS:
846 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis); 871 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
847 case SDL_CONTROLLER_BINDTYPE_BUTTON: 872 case SDL_CONTROLLER_BINDTYPE_BUTTON:
@@ -962,7 +987,7 @@ class SDLPoller : public InputCommon::Polling::DevicePoller {
962public: 987public:
963 explicit SDLPoller(SDLState& state_) : state(state_) {} 988 explicit SDLPoller(SDLState& state_) : state(state_) {}
964 989
965 void Start(const std::string& device_id) override { 990 void Start([[maybe_unused]] const std::string& device_id) override {
966 state.event_queue.Clear(); 991 state.event_queue.Clear();
967 state.polling = true; 992 state.polling = true;
968 } 993 }
@@ -1045,7 +1070,6 @@ public:
1045 1070
1046 void Start(const std::string& device_id) override { 1071 void Start(const std::string& device_id) override {
1047 SDLPoller::Start(device_id); 1072 SDLPoller::Start(device_id);
1048 // Load the game controller
1049 // Reset stored axes 1073 // Reset stored axes
1050 analog_x_axis = -1; 1074 analog_x_axis = -1;
1051 analog_y_axis = -1; 1075 analog_y_axis = -1;
@@ -1058,40 +1082,21 @@ public:
1058 if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) { 1082 if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
1059 continue; 1083 continue;
1060 } 1084 }
1061 // Simplify controller config by testing if game controller support is enabled.
1062 if (event.type == SDL_JOYAXISMOTION) { 1085 if (event.type == SDL_JOYAXISMOTION) {
1063 const auto axis = event.jaxis.axis; 1086 const auto axis = event.jaxis.axis;
1064 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 1087 // In order to return a complete analog param, we need inputs for both axes.
1065 auto* const controller = joystick->GetSDLGameController()) { 1088 // First we take the x-axis (horizontal) input, then the y-axis (vertical) input.
1066 const auto axis_left_x = 1089 if (analog_x_axis == -1) {
1067 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) 1090 analog_x_axis = axis;
1068 .value.axis; 1091 } else if (analog_y_axis == -1 && analog_x_axis != axis) {
1069 const auto axis_left_y = 1092 analog_y_axis = axis;
1070 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) 1093 }
1071 .value.axis; 1094 } else {
1072 const auto axis_right_x = 1095 // If the press wasn't accepted as a joy axis, check for a button press
1073 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) 1096 auto button_press = button_poller.FromEvent(event);
1074 .value.axis; 1097 if (button_press) {
1075 const auto axis_right_y = 1098 return *button_press;
1076 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY)
1077 .value.axis;
1078
1079 if (axis == axis_left_x || axis == axis_left_y) {
1080 analog_x_axis = axis_left_x;
1081 analog_y_axis = axis_left_y;
1082 break;
1083 } else if (axis == axis_right_x || axis == axis_right_y) {
1084 analog_x_axis = axis_right_x;
1085 analog_y_axis = axis_right_y;
1086 break;
1087 }
1088 } 1099 }
1089 }
1090
1091 // If the press wasn't accepted as a joy axis, check for a button press
1092 auto button_press = button_poller.FromEvent(event);
1093 if (button_press) {
1094 return *button_press;
1095 } 1100 }
1096 } 1101 }
1097 1102
@@ -1104,6 +1109,7 @@ public:
1104 return params; 1109 return params;
1105 } 1110 }
1106 } 1111 }
1112
1107 return {}; 1113 return {};
1108 } 1114 }
1109 1115
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index b9bb4dc56..08044b00d 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -22,6 +22,7 @@ namespace InputCommon::SDL {
22class SDLAnalogFactory; 22class SDLAnalogFactory;
23class SDLButtonFactory; 23class SDLButtonFactory;
24class SDLMotionFactory; 24class SDLMotionFactory;
25class SDLVibrationFactory;
25class SDLJoystick; 26class SDLJoystick;
26 27
27class SDLState : public State { 28class SDLState : public State {
@@ -72,6 +73,7 @@ private:
72 73
73 std::shared_ptr<SDLButtonFactory> button_factory; 74 std::shared_ptr<SDLButtonFactory> button_factory;
74 std::shared_ptr<SDLAnalogFactory> analog_factory; 75 std::shared_ptr<SDLAnalogFactory> analog_factory;
76 std::shared_ptr<SDLVibrationFactory> vibration_factory;
75 std::shared_ptr<SDLMotionFactory> motion_factory; 77 std::shared_ptr<SDLMotionFactory> motion_factory;
76 78
77 bool start_thread = false; 79 bool start_thread = false;
diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp
index b66c05856..557e7a9a0 100644
--- a/src/input_common/settings.cpp
+++ b/src/input_common/settings.cpp
@@ -14,13 +14,6 @@ const std::array<const char*, NumButtons> mapping = {{
14}}; 14}};
15} 15}
16 16
17namespace NativeMotion {
18const std::array<const char*, NumMotions> mapping = {{
19 "motionleft",
20 "motionright",
21}};
22}
23
24namespace NativeAnalog { 17namespace NativeAnalog {
25const std::array<const char*, NumAnalogs> mapping = {{ 18const std::array<const char*, NumAnalogs> mapping = {{
26 "lstick", 19 "lstick",
@@ -28,6 +21,20 @@ const std::array<const char*, NumAnalogs> mapping = {{
28}}; 21}};
29} 22}
30 23
24namespace NativeVibration {
25const std::array<const char*, NumVibrations> mapping = {{
26 "left_vibration_device",
27 "right_vibration_device",
28}};
29}
30
31namespace NativeMotion {
32const std::array<const char*, NumMotions> mapping = {{
33 "motionleft",
34 "motionright",
35}};
36}
37
31namespace NativeMouseButton { 38namespace NativeMouseButton {
32const std::array<const char*, NumMouseButtons> mapping = {{ 39const std::array<const char*, NumMouseButtons> mapping = {{
33 "left", 40 "left",
diff --git a/src/input_common/settings.h b/src/input_common/settings.h
index f52d28540..75486554b 100644
--- a/src/input_common/settings.h
+++ b/src/input_common/settings.h
@@ -66,17 +66,32 @@ constexpr int NUM_STICKS_HID = NumAnalogs;
66extern const std::array<const char*, NumAnalogs> mapping; 66extern const std::array<const char*, NumAnalogs> mapping;
67} // namespace NativeAnalog 67} // namespace NativeAnalog
68 68
69namespace NativeVibration {
70enum Values : int {
71 LeftVibrationDevice,
72 RightVibrationDevice,
73
74 NumVibrations,
75};
76
77constexpr int VIBRATION_HID_BEGIN = LeftVibrationDevice;
78constexpr int VIBRATION_HID_END = NumVibrations;
79constexpr int NUM_VIBRATIONS_HID = NumVibrations;
80
81extern const std::array<const char*, NumVibrations> mapping;
82}; // namespace NativeVibration
83
69namespace NativeMotion { 84namespace NativeMotion {
70enum Values : int { 85enum Values : int {
71 MOTIONLEFT, 86 MotionLeft,
72 MOTIONRIGHT, 87 MotionRight,
73 88
74 NumMotions, 89 NumMotions,
75}; 90};
76 91
77constexpr int MOTION_HID_BEGIN = MOTIONLEFT; 92constexpr int MOTION_HID_BEGIN = MotionLeft;
78constexpr int MOTION_HID_END = NumMotions; 93constexpr int MOTION_HID_END = NumMotions;
79constexpr int NUM_MOTION_HID = NumMotions; 94constexpr int NUM_MOTIONS_HID = NumMotions;
80 95
81extern const std::array<const char*, NumMotions> mapping; 96extern const std::array<const char*, NumMotions> mapping;
82} // namespace NativeMotion 97} // namespace NativeMotion
@@ -305,9 +320,11 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
305 320
306} // namespace NativeKeyboard 321} // namespace NativeKeyboard
307 322
308using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
309using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; 323using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
310using MotionRaw = std::array<std::string, NativeMotion::NumMotions>; 324using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
325using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
326using VibrationsRaw = std::array<std::string, NativeVibration::NumVibrations>;
327
311using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>; 328using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
312using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; 329using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
313using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; 330using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
@@ -330,7 +347,11 @@ struct PlayerInput {
330 ControllerType controller_type; 347 ControllerType controller_type;
331 ButtonsRaw buttons; 348 ButtonsRaw buttons;
332 AnalogsRaw analogs; 349 AnalogsRaw analogs;
333 MotionRaw motions; 350 VibrationsRaw vibrations;
351 MotionsRaw motions;
352
353 bool vibration_enabled;
354 int vibration_strength;
334 355
335 u32 body_color_left; 356 u32 body_color_left;
336 u32 body_color_right; 357 u32 body_color_right;
diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp
index c37716aae..a07124a86 100644
--- a/src/input_common/touch_from_button.cpp
+++ b/src/input_common/touch_from_button.cpp
@@ -44,8 +44,7 @@ private:
44 std::vector<std::tuple<std::unique_ptr<Input::ButtonDevice>, int, int>> map; 44 std::vector<std::tuple<std::unique_ptr<Input::ButtonDevice>, int, int>> map;
45}; 45};
46 46
47std::unique_ptr<Input::TouchDevice> TouchFromButtonFactory::Create( 47std::unique_ptr<Input::TouchDevice> TouchFromButtonFactory::Create(const Common::ParamPackage&) {
48 const Common::ParamPackage& params) {
49 return std::make_unique<TouchFromButtonDevice>(); 48 return std::make_unique<TouchFromButtonDevice>();
50} 49}
51 50
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index 7039d6fc3..c0bb90048 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -63,7 +63,7 @@ public:
63 } 63 }
64 64
65private: 65private:
66 void HandleReceive(const boost::system::error_code& error, std::size_t bytes_transferred) { 66 void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {
67 if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) { 67 if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {
68 switch (*type) { 68 switch (*type) {
69 case Type::Version: { 69 case Type::Version: {
@@ -90,7 +90,7 @@ private:
90 StartReceive(); 90 StartReceive();
91 } 91 }
92 92
93 void HandleSend(const boost::system::error_code& error) { 93 void HandleSend(const boost::system::error_code&) {
94 boost::system::error_code _ignored{}; 94 boost::system::error_code _ignored{};
95 // Send a request for getting port info for the pad 95 // Send a request for getting port info for the pad
96 const Request::PortInfo port_info{1, {static_cast<u8>(pad_index), 0, 0, 0}}; 96 const Request::PortInfo port_info{1, {static_cast<u8>(pad_index), 0, 0, 0}};
@@ -189,11 +189,11 @@ void Client::ReloadSocket(const std::string& host, u16 port, std::size_t pad_ind
189 StartCommunication(client, host, port, pad_index, client_id); 189 StartCommunication(client, host, port, pad_index, client_id);
190} 190}
191 191
192void Client::OnVersion(Response::Version data) { 192void Client::OnVersion([[maybe_unused]] Response::Version data) {
193 LOG_TRACE(Input, "Version packet received: {}", data.version); 193 LOG_TRACE(Input, "Version packet received: {}", data.version);
194} 194}
195 195
196void Client::OnPortInfo(Response::PortInfo data) { 196void Client::OnPortInfo([[maybe_unused]] Response::PortInfo data) {
197 LOG_TRACE(Input, "PortInfo packet received: {}", data.model); 197 LOG_TRACE(Input, "PortInfo packet received: {}", data.model);
198} 198}
199 199
@@ -344,7 +344,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
344 }; 344 };
345 Socket socket{host, port, pad_index, client_id, std::move(callback)}; 345 Socket socket{host, port, pad_index, client_id, std::move(callback)};
346 std::thread worker_thread{SocketLoop, &socket}; 346 std::thread worker_thread{SocketLoop, &socket};
347 const bool result = success_event.WaitFor(std::chrono::seconds(8)); 347 const bool result = success_event.WaitFor(std::chrono::seconds(5));
348 socket.Stop(); 348 socket.Stop();
349 worker_thread.join(); 349 worker_thread.join();
350 if (result) { 350 if (result) {
@@ -369,7 +369,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
369 u16 max_y{}; 369 u16 max_y{};
370 370
371 Status current_status{Status::Initialized}; 371 Status current_status{Status::Initialized};
372 SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, 372 SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
373 [&](Response::PadData data) { 373 [&](Response::PadData data) {
374 if (current_status == Status::Initialized) { 374 if (current_status == Status::Initialized) {
375 // Receiving data means the communication is ready now 375 // Receiving data means the communication is ready now
diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h
index 3ba4d1fc8..fc1aea4b9 100644
--- a/src/input_common/udp/protocol.h
+++ b/src/input_common/udp/protocol.h
@@ -7,7 +7,16 @@
7#include <array> 7#include <array>
8#include <optional> 8#include <optional>
9#include <type_traits> 9#include <type_traits>
10
11#ifdef _MSC_VER
12#pragma warning(push)
13#pragma warning(disable : 4701)
14#endif
10#include <boost/crc.hpp> 15#include <boost/crc.hpp>
16#ifdef _MSC_VER
17#pragma warning(pop)
18#endif
19
11#include "common/bit_field.h" 20#include "common/bit_field.h"
12#include "common/swap.h" 21#include "common/swap.h"
13 22
@@ -93,7 +102,7 @@ static_assert(std::is_trivially_copyable_v<PadData>,
93 102
94/** 103/**
95 * Creates a message with the proper header data that can be sent to the server. 104 * Creates a message with the proper header data that can be sent to the server.
96 * @param T data Request body to send 105 * @param data Request body to send
97 * @param client_id ID of the udp client (usually not checked on the server) 106 * @param client_id ID of the udp client (usually not checked on the server)
98 */ 107 */
99template <typename T> 108template <typename T>
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 105b85a92..d8801b1f5 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -13,20 +13,6 @@
13 13
14namespace Tegra { 14namespace Tegra {
15 15
16void CommandList::RefreshIntegrityChecks(GPU& gpu) {
17 command_list_hashes.resize(command_lists.size());
18
19 for (std::size_t index = 0; index < command_lists.size(); ++index) {
20 const CommandListHeader command_list_header = command_lists[index];
21 std::vector<CommandHeader> command_headers(command_list_header.size);
22 gpu.MemoryManager().ReadBlockUnsafe(command_list_header.addr, command_headers.data(),
23 command_list_header.size * sizeof(u32));
24 command_list_hashes[index] =
25 Common::CityHash64(reinterpret_cast<char*>(command_headers.data()),
26 command_list_header.size * sizeof(u32));
27 }
28}
29
30DmaPusher::DmaPusher(Core::System& system, GPU& gpu) : gpu{gpu}, system{system} {} 16DmaPusher::DmaPusher(Core::System& system, GPU& gpu) : gpu{gpu}, system{system} {}
31 17
32DmaPusher::~DmaPusher() = default; 18DmaPusher::~DmaPusher() = default;
@@ -77,8 +63,7 @@ bool DmaPusher::Step() {
77 dma_pushbuffer.pop(); 63 dma_pushbuffer.pop();
78 } else { 64 } else {
79 const CommandListHeader command_list_header{ 65 const CommandListHeader command_list_header{
80 command_list.command_lists[dma_pushbuffer_subindex]}; 66 command_list.command_lists[dma_pushbuffer_subindex++]};
81 const u64 next_hash = command_list.command_list_hashes[dma_pushbuffer_subindex++];
82 const GPUVAddr dma_get = command_list_header.addr; 67 const GPUVAddr dma_get = command_list_header.addr;
83 68
84 if (dma_pushbuffer_subindex >= command_list.command_lists.size()) { 69 if (dma_pushbuffer_subindex >= command_list.command_lists.size()) {
@@ -95,15 +80,6 @@ bool DmaPusher::Step() {
95 command_headers.resize(command_list_header.size); 80 command_headers.resize(command_list_header.size);
96 gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), 81 gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(),
97 command_list_header.size * sizeof(u32)); 82 command_list_header.size * sizeof(u32));
98
99 // Integrity check
100 const u64 new_hash = Common::CityHash64(reinterpret_cast<char*>(command_headers.data()),
101 command_list_header.size * sizeof(u32));
102 if (new_hash != next_hash) {
103 LOG_CRITICAL(HW_GPU, "CommandList at addr=0x{:X} is corrupt, skipping!", dma_get);
104 dma_pushbuffer.pop();
105 return true;
106 }
107 } 83 }
108 for (std::size_t index = 0; index < command_headers.size();) { 84 for (std::size_t index = 0; index < command_headers.size();) {
109 const CommandHeader& command_header = command_headers[index]; 85 const CommandHeader& command_header = command_headers[index];
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index 9d9a750d9..96ac267f7 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -90,10 +90,7 @@ struct CommandList final {
90 explicit CommandList(std::vector<Tegra::CommandHeader>&& prefetch_command_list) 90 explicit CommandList(std::vector<Tegra::CommandHeader>&& prefetch_command_list)
91 : prefetch_command_list{std::move(prefetch_command_list)} {} 91 : prefetch_command_list{std::move(prefetch_command_list)} {}
92 92
93 void RefreshIntegrityChecks(GPU& gpu);
94
95 std::vector<Tegra::CommandListHeader> command_lists; 93 std::vector<Tegra::CommandListHeader> command_lists;
96 std::vector<u64> command_list_hashes;
97 std::vector<Tegra::CommandHeader> prefetch_command_list; 94 std::vector<Tegra::CommandHeader> prefetch_command_list;
98}; 95};
99 96
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 57ebc785f..6287df633 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -124,6 +124,112 @@ void Maxwell3D::InitializeRegisterDefaults() {
124 mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true; 124 mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true;
125} 125}
126 126
127void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
128 if (executing_macro == 0) {
129 // A macro call must begin by writing the macro method's register, not its argument.
130 ASSERT_MSG((method % 2) == 0,
131 "Can't start macro execution by writing to the ARGS register");
132 executing_macro = method;
133 }
134
135 macro_params.insert(macro_params.end(), base_start, base_start + amount);
136
137 // Call the macro when there are no more parameters in the command buffer
138 if (is_last_call) {
139 CallMacroMethod(executing_macro, macro_params);
140 macro_params.clear();
141 }
142}
143
144u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
145 // Keep track of the register value in shadow_state when requested.
146 const auto control = shadow_state.shadow_ram_control;
147 if (control == Regs::ShadowRamControl::Track ||
148 control == Regs::ShadowRamControl::TrackWithFilter) {
149 shadow_state.reg_array[method] = argument;
150 return argument;
151 }
152 if (control == Regs::ShadowRamControl::Replay) {
153 return shadow_state.reg_array[method];
154 }
155 return argument;
156}
157
158void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) {
159 if (regs.reg_array[method] == argument) {
160 return;
161 }
162 regs.reg_array[method] = argument;
163
164 for (const auto& table : dirty.tables) {
165 dirty.flags[table[method]] = true;
166 }
167}
168
169void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument,
170 bool is_last_call) {
171 switch (method) {
172 case MAXWELL3D_REG_INDEX(wait_for_idle):
173 return rasterizer->WaitForIdle();
174 case MAXWELL3D_REG_INDEX(shadow_ram_control):
175 shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(nonshadow_argument);
176 return;
177 case MAXWELL3D_REG_INDEX(macros.data):
178 return macro_engine->AddCode(regs.macros.upload_address, argument);
179 case MAXWELL3D_REG_INDEX(macros.bind):
180 return ProcessMacroBind(argument);
181 case MAXWELL3D_REG_INDEX(firmware[4]):
182 return ProcessFirmwareCall4();
183 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
184 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
185 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
186 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]):
187 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]):
188 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]):
189 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]):
190 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]):
191 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]):
192 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]):
193 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]):
194 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]):
195 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]):
196 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]):
197 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]):
198 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]):
199 return StartCBData(method);
200 case MAXWELL3D_REG_INDEX(cb_bind[0]):
201 return ProcessCBBind(0);
202 case MAXWELL3D_REG_INDEX(cb_bind[1]):
203 return ProcessCBBind(1);
204 case MAXWELL3D_REG_INDEX(cb_bind[2]):
205 return ProcessCBBind(2);
206 case MAXWELL3D_REG_INDEX(cb_bind[3]):
207 return ProcessCBBind(3);
208 case MAXWELL3D_REG_INDEX(cb_bind[4]):
209 return ProcessCBBind(4);
210 case MAXWELL3D_REG_INDEX(draw.vertex_end_gl):
211 return DrawArrays();
212 case MAXWELL3D_REG_INDEX(clear_buffers):
213 return ProcessClearBuffers();
214 case MAXWELL3D_REG_INDEX(query.query_get):
215 return ProcessQueryGet();
216 case MAXWELL3D_REG_INDEX(condition.mode):
217 return ProcessQueryCondition();
218 case MAXWELL3D_REG_INDEX(counter_reset):
219 return ProcessCounterReset();
220 case MAXWELL3D_REG_INDEX(sync_info):
221 return ProcessSyncPoint();
222 case MAXWELL3D_REG_INDEX(exec_upload):
223 return upload_state.ProcessExec(regs.exec_upload.linear != 0);
224 case MAXWELL3D_REG_INDEX(data_upload):
225 upload_state.ProcessData(argument, is_last_call);
226 if (is_last_call) {
227 OnMemoryWrite();
228 }
229 return;
230 }
231}
232
127void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) { 233void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) {
128 // Reset the current macro. 234 // Reset the current macro.
129 executing_macro = 0; 235 executing_macro = 0;
@@ -157,142 +263,16 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
157 // Methods after 0xE00 are special, they're actually triggers for some microcode that was 263 // Methods after 0xE00 are special, they're actually triggers for some microcode that was
158 // uploaded to the GPU during initialization. 264 // uploaded to the GPU during initialization.
159 if (method >= MacroRegistersStart) { 265 if (method >= MacroRegistersStart) {
160 // We're trying to execute a macro 266 ProcessMacro(method, &method_argument, 1, is_last_call);
161 if (executing_macro == 0) {
162 // A macro call must begin by writing the macro method's register, not its argument.
163 ASSERT_MSG((method % 2) == 0,
164 "Can't start macro execution by writing to the ARGS register");
165 executing_macro = method;
166 }
167
168 macro_params.push_back(method_argument);
169
170 // Call the macro when there are no more parameters in the command buffer
171 if (is_last_call) {
172 CallMacroMethod(executing_macro, macro_params);
173 macro_params.clear();
174 }
175 return; 267 return;
176 } 268 }
177 269
178 ASSERT_MSG(method < Regs::NUM_REGS, 270 ASSERT_MSG(method < Regs::NUM_REGS,
179 "Invalid Maxwell3D register, increase the size of the Regs structure"); 271 "Invalid Maxwell3D register, increase the size of the Regs structure");
180 272
181 u32 arg = method_argument; 273 const u32 argument = ProcessShadowRam(method, method_argument);
182 // Keep track of the register value in shadow_state when requested. 274 ProcessDirtyRegisters(method, argument);
183 if (shadow_state.shadow_ram_control == Regs::ShadowRamControl::Track || 275 ProcessMethodCall(method, argument, method_argument, is_last_call);
184 shadow_state.shadow_ram_control == Regs::ShadowRamControl::TrackWithFilter) {
185 shadow_state.reg_array[method] = arg;
186 } else if (shadow_state.shadow_ram_control == Regs::ShadowRamControl::Replay) {
187 arg = shadow_state.reg_array[method];
188 }
189
190 if (regs.reg_array[method] != arg) {
191 regs.reg_array[method] = arg;
192
193 for (const auto& table : dirty.tables) {
194 dirty.flags[table[method]] = true;
195 }
196 }
197
198 switch (method) {
199 case MAXWELL3D_REG_INDEX(wait_for_idle): {
200 rasterizer->WaitForIdle();
201 break;
202 }
203 case MAXWELL3D_REG_INDEX(shadow_ram_control): {
204 shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(method_argument);
205 break;
206 }
207 case MAXWELL3D_REG_INDEX(macros.data): {
208 macro_engine->AddCode(regs.macros.upload_address, arg);
209 break;
210 }
211 case MAXWELL3D_REG_INDEX(macros.bind): {
212 ProcessMacroBind(arg);
213 break;
214 }
215 case MAXWELL3D_REG_INDEX(firmware[4]): {
216 ProcessFirmwareCall4();
217 break;
218 }
219 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
220 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
221 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
222 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]):
223 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]):
224 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]):
225 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]):
226 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]):
227 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]):
228 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]):
229 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]):
230 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]):
231 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]):
232 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]):
233 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]):
234 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): {
235 StartCBData(method);
236 break;
237 }
238 case MAXWELL3D_REG_INDEX(cb_bind[0]): {
239 ProcessCBBind(0);
240 break;
241 }
242 case MAXWELL3D_REG_INDEX(cb_bind[1]): {
243 ProcessCBBind(1);
244 break;
245 }
246 case MAXWELL3D_REG_INDEX(cb_bind[2]): {
247 ProcessCBBind(2);
248 break;
249 }
250 case MAXWELL3D_REG_INDEX(cb_bind[3]): {
251 ProcessCBBind(3);
252 break;
253 }
254 case MAXWELL3D_REG_INDEX(cb_bind[4]): {
255 ProcessCBBind(4);
256 break;
257 }
258 case MAXWELL3D_REG_INDEX(draw.vertex_end_gl): {
259 DrawArrays();
260 break;
261 }
262 case MAXWELL3D_REG_INDEX(clear_buffers): {
263 ProcessClearBuffers();
264 break;
265 }
266 case MAXWELL3D_REG_INDEX(query.query_get): {
267 ProcessQueryGet();
268 break;
269 }
270 case MAXWELL3D_REG_INDEX(condition.mode): {
271 ProcessQueryCondition();
272 break;
273 }
274 case MAXWELL3D_REG_INDEX(counter_reset): {
275 ProcessCounterReset();
276 break;
277 }
278 case MAXWELL3D_REG_INDEX(sync_info): {
279 ProcessSyncPoint();
280 break;
281 }
282 case MAXWELL3D_REG_INDEX(exec_upload): {
283 upload_state.ProcessExec(regs.exec_upload.linear != 0);
284 break;
285 }
286 case MAXWELL3D_REG_INDEX(data_upload): {
287 upload_state.ProcessData(arg, is_last_call);
288 if (is_last_call) {
289 OnMemoryWrite();
290 }
291 break;
292 }
293 default:
294 break;
295 }
296} 276}
297 277
298void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, 278void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
@@ -300,23 +280,7 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
300 // Methods after 0xE00 are special, they're actually triggers for some microcode that was 280 // Methods after 0xE00 are special, they're actually triggers for some microcode that was
301 // uploaded to the GPU during initialization. 281 // uploaded to the GPU during initialization.
302 if (method >= MacroRegistersStart) { 282 if (method >= MacroRegistersStart) {
303 // We're trying to execute a macro 283 ProcessMacro(method, base_start, amount, amount == methods_pending);
304 if (executing_macro == 0) {
305 // A macro call must begin by writing the macro method's register, not its argument.
306 ASSERT_MSG((method % 2) == 0,
307 "Can't start macro execution by writing to the ARGS register");
308 executing_macro = method;
309 }
310
311 for (std::size_t i = 0; i < amount; i++) {
312 macro_params.push_back(base_start[i]);
313 }
314
315 // Call the macro when there are no more parameters in the command buffer
316 if (amount == methods_pending) {
317 CallMacroMethod(executing_macro, macro_params);
318 macro_params.clear();
319 }
320 return; 284 return;
321 } 285 }
322 switch (method) { 286 switch (method) {
@@ -335,15 +299,14 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
335 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]): 299 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]):
336 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]): 300 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]):
337 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]): 301 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]):
338 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): { 302 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]):
339 ProcessCBMultiData(method, base_start, amount); 303 ProcessCBMultiData(method, base_start, amount);
340 break; 304 break;
341 } 305 default:
342 default: {
343 for (std::size_t i = 0; i < amount; i++) { 306 for (std::size_t i = 0; i < amount; i++) {
344 CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1); 307 CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1);
345 } 308 }
346 } 309 break;
347 } 310 }
348} 311}
349 312
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index bc289c55d..1cbe8fe67 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1461,6 +1461,14 @@ public:
1461private: 1461private:
1462 void InitializeRegisterDefaults(); 1462 void InitializeRegisterDefaults();
1463 1463
1464 void ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call);
1465
1466 u32 ProcessShadowRam(u32 method, u32 argument);
1467
1468 void ProcessDirtyRegisters(u32 method, u32 argument);
1469
1470 void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call);
1471
1464 Core::System& system; 1472 Core::System& system;
1465 MemoryManager& memory_manager; 1473 MemoryManager& memory_manager;
1466 1474
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index a3c05d1b0..37d17efdc 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -32,31 +32,31 @@ struct Register {
32 32
33 constexpr Register() = default; 33 constexpr Register() = default;
34 34
35 constexpr Register(u64 value) : value(value) {} 35 constexpr Register(u64 value_) : value(value_) {}
36 36
37 constexpr operator u64() const { 37 [[nodiscard]] constexpr operator u64() const {
38 return value; 38 return value;
39 } 39 }
40 40
41 template <typename T> 41 template <typename T>
42 constexpr u64 operator-(const T& oth) const { 42 [[nodiscard]] constexpr u64 operator-(const T& oth) const {
43 return value - oth; 43 return value - oth;
44 } 44 }
45 45
46 template <typename T> 46 template <typename T>
47 constexpr u64 operator&(const T& oth) const { 47 [[nodiscard]] constexpr u64 operator&(const T& oth) const {
48 return value & oth; 48 return value & oth;
49 } 49 }
50 50
51 constexpr u64 operator&(const Register& oth) const { 51 [[nodiscard]] constexpr u64 operator&(const Register& oth) const {
52 return value & oth.value; 52 return value & oth.value;
53 } 53 }
54 54
55 constexpr u64 operator~() const { 55 [[nodiscard]] constexpr u64 operator~() const {
56 return ~value; 56 return ~value;
57 } 57 }
58 58
59 u64 GetSwizzledIndex(u64 elem) const { 59 [[nodiscard]] u64 GetSwizzledIndex(u64 elem) const {
60 elem = (value + elem) & 3; 60 elem = (value + elem) & 3;
61 return (value & ~3) + elem; 61 return (value & ~3) + elem;
62 } 62 }
@@ -75,7 +75,7 @@ enum class AttributeSize : u64 {
75union Attribute { 75union Attribute {
76 Attribute() = default; 76 Attribute() = default;
77 77
78 constexpr explicit Attribute(u64 value) : value(value) {} 78 constexpr explicit Attribute(u64 value_) : value(value_) {}
79 79
80 enum class Index : u64 { 80 enum class Index : u64 {
81 LayerViewportPointSize = 6, 81 LayerViewportPointSize = 6,
@@ -107,7 +107,7 @@ union Attribute {
107 BitField<31, 1, u64> patch; 107 BitField<31, 1, u64> patch;
108 BitField<47, 3, AttributeSize> size; 108 BitField<47, 3, AttributeSize> size;
109 109
110 bool IsPhysical() const { 110 [[nodiscard]] bool IsPhysical() const {
111 return patch == 0 && element == 0 && static_cast<u64>(index.Value()) == 0; 111 return patch == 0 && element == 0 && static_cast<u64>(index.Value()) == 0;
112 } 112 }
113 } fmt20; 113 } fmt20;
@@ -124,7 +124,7 @@ union Attribute {
124union Sampler { 124union Sampler {
125 Sampler() = default; 125 Sampler() = default;
126 126
127 constexpr explicit Sampler(u64 value) : value(value) {} 127 constexpr explicit Sampler(u64 value_) : value(value_) {}
128 128
129 enum class Index : u64 { 129 enum class Index : u64 {
130 Sampler_0 = 8, 130 Sampler_0 = 8,
@@ -137,7 +137,7 @@ union Sampler {
137union Image { 137union Image {
138 Image() = default; 138 Image() = default;
139 139
140 constexpr explicit Image(u64 value) : value{value} {} 140 constexpr explicit Image(u64 value_) : value{value_} {}
141 141
142 BitField<36, 13, u64> index; 142 BitField<36, 13, u64> index;
143 u64 value; 143 u64 value;
@@ -505,14 +505,14 @@ struct IpaMode {
505 IpaInterpMode interpolation_mode; 505 IpaInterpMode interpolation_mode;
506 IpaSampleMode sampling_mode; 506 IpaSampleMode sampling_mode;
507 507
508 bool operator==(const IpaMode& a) const { 508 [[nodiscard]] bool operator==(const IpaMode& a) const {
509 return std::tie(interpolation_mode, sampling_mode) == 509 return std::tie(interpolation_mode, sampling_mode) ==
510 std::tie(a.interpolation_mode, a.sampling_mode); 510 std::tie(a.interpolation_mode, a.sampling_mode);
511 } 511 }
512 bool operator!=(const IpaMode& a) const { 512 [[nodiscard]] bool operator!=(const IpaMode& a) const {
513 return !operator==(a); 513 return !operator==(a);
514 } 514 }
515 bool operator<(const IpaMode& a) const { 515 [[nodiscard]] bool operator<(const IpaMode& a) const {
516 return std::tie(interpolation_mode, sampling_mode) < 516 return std::tie(interpolation_mode, sampling_mode) <
517 std::tie(a.interpolation_mode, a.sampling_mode); 517 std::tie(a.interpolation_mode, a.sampling_mode);
518 } 518 }
@@ -658,10 +658,10 @@ union Instruction {
658 return *this; 658 return *this;
659 } 659 }
660 660
661 constexpr Instruction(u64 value) : value{value} {} 661 constexpr Instruction(u64 value_) : value{value_} {}
662 constexpr Instruction(const Instruction& instr) : value(instr.value) {} 662 constexpr Instruction(const Instruction& instr) : value(instr.value) {}
663 663
664 constexpr bool Bit(u64 offset) const { 664 [[nodiscard]] constexpr bool Bit(u64 offset) const {
665 return ((value >> offset) & 1) != 0; 665 return ((value >> offset) & 1) != 0;
666 } 666 }
667 667
@@ -746,34 +746,34 @@ union Instruction {
746 BitField<28, 8, u64> imm_lut28; 746 BitField<28, 8, u64> imm_lut28;
747 BitField<48, 8, u64> imm_lut48; 747 BitField<48, 8, u64> imm_lut48;
748 748
749 u32 GetImmLut28() const { 749 [[nodiscard]] u32 GetImmLut28() const {
750 return static_cast<u32>(imm_lut28); 750 return static_cast<u32>(imm_lut28);
751 } 751 }
752 752
753 u32 GetImmLut48() const { 753 [[nodiscard]] u32 GetImmLut48() const {
754 return static_cast<u32>(imm_lut48); 754 return static_cast<u32>(imm_lut48);
755 } 755 }
756 } lop3; 756 } lop3;
757 757
758 u16 GetImm20_16() const { 758 [[nodiscard]] u16 GetImm20_16() const {
759 return static_cast<u16>(imm20_16); 759 return static_cast<u16>(imm20_16);
760 } 760 }
761 761
762 u32 GetImm20_19() const { 762 [[nodiscard]] u32 GetImm20_19() const {
763 u32 imm{static_cast<u32>(imm20_19)}; 763 u32 imm{static_cast<u32>(imm20_19)};
764 imm <<= 12; 764 imm <<= 12;
765 imm |= negate_imm ? 0x80000000 : 0; 765 imm |= negate_imm ? 0x80000000 : 0;
766 return imm; 766 return imm;
767 } 767 }
768 768
769 u32 GetImm20_32() const { 769 [[nodiscard]] u32 GetImm20_32() const {
770 return static_cast<u32>(imm20_32); 770 return static_cast<u32>(imm20_32);
771 } 771 }
772 772
773 s32 GetSignedImm20_20() const { 773 [[nodiscard]] s32 GetSignedImm20_20() const {
774 u32 immediate = static_cast<u32>(imm20_19 | (negate_imm << 19)); 774 const auto immediate = static_cast<u32>(imm20_19 | (negate_imm << 19));
775 // Sign extend the 20-bit value. 775 // Sign extend the 20-bit value.
776 u32 mask = 1U << (20 - 1); 776 const auto mask = 1U << (20 - 1);
777 return static_cast<s32>((immediate ^ mask) - mask); 777 return static_cast<s32>((immediate ^ mask) - mask);
778 } 778 }
779 } alu; 779 } alu;
@@ -857,7 +857,7 @@ union Instruction {
857 BitField<56, 1, u64> second_negate; 857 BitField<56, 1, u64> second_negate;
858 BitField<30, 9, u64> second; 858 BitField<30, 9, u64> second;
859 859
860 u32 PackImmediates() const { 860 [[nodiscard]] u32 PackImmediates() const {
861 // Immediates are half floats shifted. 861 // Immediates are half floats shifted.
862 constexpr u32 imm_shift = 6; 862 constexpr u32 imm_shift = 6;
863 return static_cast<u32>((first << imm_shift) | (second << (16 + imm_shift))); 863 return static_cast<u32>((first << imm_shift) | (second << (16 + imm_shift)));
@@ -1033,7 +1033,7 @@ union Instruction {
1033 BitField<28, 2, AtomicType> type; 1033 BitField<28, 2, AtomicType> type;
1034 BitField<30, 22, s64> offset; 1034 BitField<30, 22, s64> offset;
1035 1035
1036 s32 GetImmediateOffset() const { 1036 [[nodiscard]] s32 GetImmediateOffset() const {
1037 return static_cast<s32>(offset << 2); 1037 return static_cast<s32>(offset << 2);
1038 } 1038 }
1039 } atoms; 1039 } atoms;
@@ -1215,7 +1215,7 @@ union Instruction {
1215 BitField<39, 4, u64> rounding; 1215 BitField<39, 4, u64> rounding;
1216 // H0, H1 extract for F16 missing 1216 // H0, H1 extract for F16 missing
1217 BitField<41, 1, u64> selector; // Guessed as some games set it, TODO: reverse this value 1217 BitField<41, 1, u64> selector; // Guessed as some games set it, TODO: reverse this value
1218 F2fRoundingOp GetRoundingMode() const { 1218 [[nodiscard]] F2fRoundingOp GetRoundingMode() const {
1219 constexpr u64 rounding_mask = 0x0B; 1219 constexpr u64 rounding_mask = 0x0B;
1220 return static_cast<F2fRoundingOp>(rounding.Value() & rounding_mask); 1220 return static_cast<F2fRoundingOp>(rounding.Value() & rounding_mask);
1221 } 1221 }
@@ -1239,15 +1239,15 @@ union Instruction {
1239 BitField<54, 1, u64> aoffi_flag; 1239 BitField<54, 1, u64> aoffi_flag;
1240 BitField<55, 3, TextureProcessMode> process_mode; 1240 BitField<55, 3, TextureProcessMode> process_mode;
1241 1241
1242 bool IsComponentEnabled(std::size_t component) const { 1242 [[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
1243 return ((1ull << component) & component_mask) != 0; 1243 return ((1ULL << component) & component_mask) != 0;
1244 } 1244 }
1245 1245
1246 TextureProcessMode GetTextureProcessMode() const { 1246 [[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
1247 return process_mode; 1247 return process_mode;
1248 } 1248 }
1249 1249
1250 bool UsesMiscMode(TextureMiscMode mode) const { 1250 [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
1251 switch (mode) { 1251 switch (mode) {
1252 case TextureMiscMode::DC: 1252 case TextureMiscMode::DC:
1253 return dc_flag != 0; 1253 return dc_flag != 0;
@@ -1271,15 +1271,15 @@ union Instruction {
1271 BitField<36, 1, u64> aoffi_flag; 1271 BitField<36, 1, u64> aoffi_flag;
1272 BitField<37, 3, TextureProcessMode> process_mode; 1272 BitField<37, 3, TextureProcessMode> process_mode;
1273 1273
1274 bool IsComponentEnabled(std::size_t component) const { 1274 [[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
1275 return ((1ULL << component) & component_mask) != 0; 1275 return ((1ULL << component) & component_mask) != 0;
1276 } 1276 }
1277 1277
1278 TextureProcessMode GetTextureProcessMode() const { 1278 [[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
1279 return process_mode; 1279 return process_mode;
1280 } 1280 }
1281 1281
1282 bool UsesMiscMode(TextureMiscMode mode) const { 1282 [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
1283 switch (mode) { 1283 switch (mode) {
1284 case TextureMiscMode::DC: 1284 case TextureMiscMode::DC:
1285 return dc_flag != 0; 1285 return dc_flag != 0;
@@ -1299,7 +1299,7 @@ union Instruction {
1299 BitField<31, 4, u64> component_mask; 1299 BitField<31, 4, u64> component_mask;
1300 BitField<49, 1, u64> nodep_flag; 1300 BitField<49, 1, u64> nodep_flag;
1301 1301
1302 bool UsesMiscMode(TextureMiscMode mode) const { 1302 [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
1303 switch (mode) { 1303 switch (mode) {
1304 case TextureMiscMode::NODEP: 1304 case TextureMiscMode::NODEP:
1305 return nodep_flag != 0; 1305 return nodep_flag != 0;
@@ -1309,7 +1309,7 @@ union Instruction {
1309 return false; 1309 return false;
1310 } 1310 }
1311 1311
1312 bool IsComponentEnabled(std::size_t component) const { 1312 [[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
1313 return ((1ULL << component) & component_mask) != 0; 1313 return ((1ULL << component) & component_mask) != 0;
1314 } 1314 }
1315 } txq; 1315 } txq;
@@ -1321,11 +1321,11 @@ union Instruction {
1321 BitField<35, 1, u64> ndv_flag; 1321 BitField<35, 1, u64> ndv_flag;
1322 BitField<49, 1, u64> nodep_flag; 1322 BitField<49, 1, u64> nodep_flag;
1323 1323
1324 bool IsComponentEnabled(std::size_t component) const { 1324 [[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
1325 return ((1ull << component) & component_mask) != 0; 1325 return ((1ULL << component) & component_mask) != 0;
1326 } 1326 }
1327 1327
1328 bool UsesMiscMode(TextureMiscMode mode) const { 1328 [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
1329 switch (mode) { 1329 switch (mode) {
1330 case TextureMiscMode::NDV: 1330 case TextureMiscMode::NDV:
1331 return (ndv_flag != 0); 1331 return (ndv_flag != 0);
@@ -1347,7 +1347,7 @@ union Instruction {
1347 BitField<54, 2, u64> offset_mode; 1347 BitField<54, 2, u64> offset_mode;
1348 BitField<56, 2, u64> component; 1348 BitField<56, 2, u64> component;
1349 1349
1350 bool UsesMiscMode(TextureMiscMode mode) const { 1350 [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
1351 switch (mode) { 1351 switch (mode) {
1352 case TextureMiscMode::NDV: 1352 case TextureMiscMode::NDV:
1353 return ndv_flag != 0; 1353 return ndv_flag != 0;
@@ -1373,7 +1373,7 @@ union Instruction {
1373 BitField<33, 2, u64> offset_mode; 1373 BitField<33, 2, u64> offset_mode;
1374 BitField<37, 2, u64> component; 1374 BitField<37, 2, u64> component;
1375 1375
1376 bool UsesMiscMode(TextureMiscMode mode) const { 1376 [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
1377 switch (mode) { 1377 switch (mode) {
1378 case TextureMiscMode::NDV: 1378 case TextureMiscMode::NDV:
1379 return ndv_flag != 0; 1379 return ndv_flag != 0;
@@ -1399,7 +1399,7 @@ union Instruction {
1399 BitField<52, 2, u64> component; 1399 BitField<52, 2, u64> component;
1400 BitField<55, 1, u64> fp16_flag; 1400 BitField<55, 1, u64> fp16_flag;
1401 1401
1402 bool UsesMiscMode(TextureMiscMode mode) const { 1402 [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
1403 switch (mode) { 1403 switch (mode) {
1404 case TextureMiscMode::DC: 1404 case TextureMiscMode::DC:
1405 return dc_flag != 0; 1405 return dc_flag != 0;
@@ -1422,16 +1422,20 @@ union Instruction {
1422 BitField<53, 4, u64> texture_info; 1422 BitField<53, 4, u64> texture_info;
1423 BitField<59, 1, u64> fp32_flag; 1423 BitField<59, 1, u64> fp32_flag;
1424 1424
1425 TextureType GetTextureType() const { 1425 [[nodiscard]] TextureType GetTextureType() const {
1426 // The TEXS instruction has a weird encoding for the texture type. 1426 // The TEXS instruction has a weird encoding for the texture type.
1427 if (texture_info == 0) 1427 if (texture_info == 0) {
1428 return TextureType::Texture1D; 1428 return TextureType::Texture1D;
1429 if (texture_info >= 1 && texture_info <= 9) 1429 }
1430 if (texture_info >= 1 && texture_info <= 9) {
1430 return TextureType::Texture2D; 1431 return TextureType::Texture2D;
1431 if (texture_info >= 10 && texture_info <= 11) 1432 }
1433 if (texture_info >= 10 && texture_info <= 11) {
1432 return TextureType::Texture3D; 1434 return TextureType::Texture3D;
1433 if (texture_info >= 12 && texture_info <= 13) 1435 }
1436 if (texture_info >= 12 && texture_info <= 13) {
1434 return TextureType::TextureCube; 1437 return TextureType::TextureCube;
1438 }
1435 1439
1436 LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}", 1440 LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
1437 static_cast<u32>(texture_info.Value())); 1441 static_cast<u32>(texture_info.Value()));
@@ -1439,7 +1443,7 @@ union Instruction {
1439 return TextureType::Texture1D; 1443 return TextureType::Texture1D;
1440 } 1444 }
1441 1445
1442 TextureProcessMode GetTextureProcessMode() const { 1446 [[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
1443 switch (texture_info) { 1447 switch (texture_info) {
1444 case 0: 1448 case 0:
1445 case 2: 1449 case 2:
@@ -1458,7 +1462,7 @@ union Instruction {
1458 return TextureProcessMode::None; 1462 return TextureProcessMode::None;
1459 } 1463 }
1460 1464
1461 bool UsesMiscMode(TextureMiscMode mode) const { 1465 [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
1462 switch (mode) { 1466 switch (mode) {
1463 case TextureMiscMode::DC: 1467 case TextureMiscMode::DC:
1464 return (texture_info >= 4 && texture_info <= 6) || texture_info == 9; 1468 return (texture_info >= 4 && texture_info <= 6) || texture_info == 9;
@@ -1470,16 +1474,16 @@ union Instruction {
1470 return false; 1474 return false;
1471 } 1475 }
1472 1476
1473 bool IsArrayTexture() const { 1477 [[nodiscard]] bool IsArrayTexture() const {
1474 // TEXS only supports Texture2D arrays. 1478 // TEXS only supports Texture2D arrays.
1475 return texture_info >= 7 && texture_info <= 9; 1479 return texture_info >= 7 && texture_info <= 9;
1476 } 1480 }
1477 1481
1478 bool HasTwoDestinations() const { 1482 [[nodiscard]] bool HasTwoDestinations() const {
1479 return gpr28.Value() != Register::ZeroIndex; 1483 return gpr28.Value() != Register::ZeroIndex;
1480 } 1484 }
1481 1485
1482 bool IsComponentEnabled(std::size_t component) const { 1486 [[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
1483 static constexpr std::array<std::array<u32, 8>, 4> mask_lut{{ 1487 static constexpr std::array<std::array<u32, 8>, 4> mask_lut{{
1484 {}, 1488 {},
1485 {0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc}, 1489 {0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc},
@@ -1506,7 +1510,7 @@ union Instruction {
1506 BitField<54, 1, u64> cl; 1510 BitField<54, 1, u64> cl;
1507 BitField<55, 1, u64> process_mode; 1511 BitField<55, 1, u64> process_mode;
1508 1512
1509 TextureProcessMode GetTextureProcessMode() const { 1513 [[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
1510 return process_mode == 0 ? TextureProcessMode::LZ : TextureProcessMode::LL; 1514 return process_mode == 0 ? TextureProcessMode::LZ : TextureProcessMode::LL;
1511 } 1515 }
1512 } tld; 1516 } tld;
@@ -1516,7 +1520,7 @@ union Instruction {
1516 BitField<53, 4, u64> texture_info; 1520 BitField<53, 4, u64> texture_info;
1517 BitField<59, 1, u64> fp32_flag; 1521 BitField<59, 1, u64> fp32_flag;
1518 1522
1519 TextureType GetTextureType() const { 1523 [[nodiscard]] TextureType GetTextureType() const {
1520 // The TLDS instruction has a weird encoding for the texture type. 1524 // The TLDS instruction has a weird encoding for the texture type.
1521 if (texture_info <= 1) { 1525 if (texture_info <= 1) {
1522 return TextureType::Texture1D; 1526 return TextureType::Texture1D;
@@ -1535,13 +1539,14 @@ union Instruction {
1535 return TextureType::Texture1D; 1539 return TextureType::Texture1D;
1536 } 1540 }
1537 1541
1538 TextureProcessMode GetTextureProcessMode() const { 1542 [[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
1539 if (texture_info == 1 || texture_info == 5 || texture_info == 12) 1543 if (texture_info == 1 || texture_info == 5 || texture_info == 12) {
1540 return TextureProcessMode::LL; 1544 return TextureProcessMode::LL;
1545 }
1541 return TextureProcessMode::LZ; 1546 return TextureProcessMode::LZ;
1542 } 1547 }
1543 1548
1544 bool UsesMiscMode(TextureMiscMode mode) const { 1549 [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
1545 switch (mode) { 1550 switch (mode) {
1546 case TextureMiscMode::AOFFI: 1551 case TextureMiscMode::AOFFI:
1547 return texture_info == 12 || texture_info == 4; 1552 return texture_info == 12 || texture_info == 4;
@@ -1555,7 +1560,7 @@ union Instruction {
1555 return false; 1560 return false;
1556 } 1561 }
1557 1562
1558 bool IsArrayTexture() const { 1563 [[nodiscard]] bool IsArrayTexture() const {
1559 // TEXS only supports Texture2D arrays. 1564 // TEXS only supports Texture2D arrays.
1560 return texture_info == 8; 1565 return texture_info == 8;
1561 } 1566 }
@@ -1567,7 +1572,7 @@ union Instruction {
1567 BitField<35, 1, u64> aoffi_flag; 1572 BitField<35, 1, u64> aoffi_flag;
1568 BitField<49, 1, u64> nodep_flag; 1573 BitField<49, 1, u64> nodep_flag;
1569 1574
1570 bool UsesMiscMode(TextureMiscMode mode) const { 1575 [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
1571 switch (mode) { 1576 switch (mode) {
1572 case TextureMiscMode::AOFFI: 1577 case TextureMiscMode::AOFFI:
1573 return aoffi_flag != 0; 1578 return aoffi_flag != 0;
@@ -1591,7 +1596,7 @@ union Instruction {
1591 BitField<20, 3, StoreType> store_data_layout; 1596 BitField<20, 3, StoreType> store_data_layout;
1592 BitField<20, 4, u64> component_mask_selector; 1597 BitField<20, 4, u64> component_mask_selector;
1593 1598
1594 bool IsComponentEnabled(std::size_t component) const { 1599 [[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
1595 ASSERT(mode == SurfaceDataMode::P); 1600 ASSERT(mode == SurfaceDataMode::P);
1596 constexpr u8 R = 0b0001; 1601 constexpr u8 R = 0b0001;
1597 constexpr u8 G = 0b0010; 1602 constexpr u8 G = 0b0010;
@@ -1604,7 +1609,7 @@ union Instruction {
1604 return std::bitset<4>{mask.at(component_mask_selector)}.test(component); 1609 return std::bitset<4>{mask.at(component_mask_selector)}.test(component);
1605 } 1610 }
1606 1611
1607 StoreType GetStoreDataLayout() const { 1612 [[nodiscard]] StoreType GetStoreDataLayout() const {
1608 ASSERT(mode == SurfaceDataMode::D_BA); 1613 ASSERT(mode == SurfaceDataMode::D_BA);
1609 return store_data_layout; 1614 return store_data_layout;
1610 } 1615 }
@@ -1622,14 +1627,15 @@ union Instruction {
1622 BitField<20, 24, u64> target; 1627 BitField<20, 24, u64> target;
1623 BitField<5, 1, u64> constant_buffer; 1628 BitField<5, 1, u64> constant_buffer;
1624 1629
1625 s32 GetBranchTarget() const { 1630 [[nodiscard]] s32 GetBranchTarget() const {
1626 // Sign extend the branch target offset 1631 // Sign extend the branch target offset
1627 u32 mask = 1U << (24 - 1); 1632 const auto mask = 1U << (24 - 1);
1628 u32 value = static_cast<u32>(target); 1633 const auto target_value = static_cast<u32>(target);
1634 constexpr auto instruction_size = static_cast<s32>(sizeof(Instruction));
1635
1629 // The branch offset is relative to the next instruction and is stored in bytes, so 1636 // The branch offset is relative to the next instruction and is stored in bytes, so
1630 // divide it by the size of an instruction and add 1 to it. 1637 // divide it by the size of an instruction and add 1 to it.
1631 return static_cast<s32>((value ^ mask) - mask) / static_cast<s32>(sizeof(Instruction)) + 1638 return static_cast<s32>((target_value ^ mask) - mask) / instruction_size + 1;
1632 1;
1633 } 1639 }
1634 } bra; 1640 } bra;
1635 1641
@@ -1637,14 +1643,15 @@ union Instruction {
1637 BitField<20, 24, u64> target; 1643 BitField<20, 24, u64> target;
1638 BitField<5, 1, u64> constant_buffer; 1644 BitField<5, 1, u64> constant_buffer;
1639 1645
1640 s32 GetBranchExtend() const { 1646 [[nodiscard]] s32 GetBranchExtend() const {
1641 // Sign extend the branch target offset 1647 // Sign extend the branch target offset
1642 u32 mask = 1U << (24 - 1); 1648 const auto mask = 1U << (24 - 1);
1643 u32 value = static_cast<u32>(target); 1649 const auto target_value = static_cast<u32>(target);
1650 constexpr auto instruction_size = static_cast<s32>(sizeof(Instruction));
1651
1644 // The branch offset is relative to the next instruction and is stored in bytes, so 1652 // The branch offset is relative to the next instruction and is stored in bytes, so
1645 // divide it by the size of an instruction and add 1 to it. 1653 // divide it by the size of an instruction and add 1 to it.
1646 return static_cast<s32>((value ^ mask) - mask) / static_cast<s32>(sizeof(Instruction)) + 1654 return static_cast<s32>((target_value ^ mask) - mask) / instruction_size + 1;
1647 1;
1648 } 1655 }
1649 } brx; 1656 } brx;
1650 1657
@@ -1697,7 +1704,7 @@ union Instruction {
1697 BitField<50, 1, u64> is_op_b_register; 1704 BitField<50, 1, u64> is_op_b_register;
1698 BitField<51, 3, VmnmxOperation> operation; 1705 BitField<51, 3, VmnmxOperation> operation;
1699 1706
1700 VmnmxType SourceFormatA() const { 1707 [[nodiscard]] VmnmxType SourceFormatA() const {
1701 switch (src_format_a) { 1708 switch (src_format_a) {
1702 case 0b11: 1709 case 0b11:
1703 return VmnmxType::Bits32; 1710 return VmnmxType::Bits32;
@@ -1708,7 +1715,7 @@ union Instruction {
1708 } 1715 }
1709 } 1716 }
1710 1717
1711 VmnmxType SourceFormatB() const { 1718 [[nodiscard]] VmnmxType SourceFormatB() const {
1712 switch (src_format_b) { 1719 switch (src_format_b) {
1713 case 0b11: 1720 case 0b11:
1714 return VmnmxType::Bits32; 1721 return VmnmxType::Bits32;
@@ -1739,7 +1746,7 @@ union Instruction {
1739 BitField<20, 14, u64> shifted_offset; 1746 BitField<20, 14, u64> shifted_offset;
1740 BitField<34, 5, u64> index; 1747 BitField<34, 5, u64> index;
1741 1748
1742 u64 GetOffset() const { 1749 [[nodiscard]] u64 GetOffset() const {
1743 return shifted_offset * 4; 1750 return shifted_offset * 4;
1744 } 1751 }
1745 } cbuf34; 1752 } cbuf34;
@@ -1748,7 +1755,7 @@ union Instruction {
1748 BitField<20, 16, s64> offset; 1755 BitField<20, 16, s64> offset;
1749 BitField<36, 5, u64> index; 1756 BitField<36, 5, u64> index;
1750 1757
1751 s64 GetOffset() const { 1758 [[nodiscard]] s64 GetOffset() const {
1752 return offset; 1759 return offset;
1753 } 1760 }
1754 } cbuf36; 1761 } cbuf36;
@@ -1997,29 +2004,29 @@ public:
1997 2004
1998 /// Returns whether an opcode has an execution predicate field or not (ie, whether it can be 2005 /// Returns whether an opcode has an execution predicate field or not (ie, whether it can be
1999 /// conditionally executed). 2006 /// conditionally executed).
2000 static bool IsPredicatedInstruction(Id opcode) { 2007 [[nodiscard]] static bool IsPredicatedInstruction(Id opcode) {
2001 // TODO(Subv): Add the rest of unpredicated instructions. 2008 // TODO(Subv): Add the rest of unpredicated instructions.
2002 return opcode != Id::SSY && opcode != Id::PBK; 2009 return opcode != Id::SSY && opcode != Id::PBK;
2003 } 2010 }
2004 2011
2005 class Matcher { 2012 class Matcher {
2006 public: 2013 public:
2007 constexpr Matcher(const char* const name, u16 mask, u16 expected, Id id, Type type) 2014 constexpr Matcher(const char* const name_, u16 mask_, u16 expected_, Id id_, Type type_)
2008 : name{name}, mask{mask}, expected{expected}, id{id}, type{type} {} 2015 : name{name_}, mask{mask_}, expected{expected_}, id{id_}, type{type_} {}
2009 2016
2010 constexpr const char* GetName() const { 2017 [[nodiscard]] constexpr const char* GetName() const {
2011 return name; 2018 return name;
2012 } 2019 }
2013 2020
2014 constexpr u16 GetMask() const { 2021 [[nodiscard]] constexpr u16 GetMask() const {
2015 return mask; 2022 return mask;
2016 } 2023 }
2017 2024
2018 constexpr Id GetId() const { 2025 [[nodiscard]] constexpr Id GetId() const {
2019 return id; 2026 return id;
2020 } 2027 }
2021 2028
2022 constexpr Type GetType() const { 2029 [[nodiscard]] constexpr Type GetType() const {
2023 return type; 2030 return type;
2024 } 2031 }
2025 2032
@@ -2028,7 +2035,7 @@ public:
2028 * @param instruction The instruction to test 2035 * @param instruction The instruction to test
2029 * @returns true if the given instruction matches. 2036 * @returns true if the given instruction matches.
2030 */ 2037 */
2031 constexpr bool Matches(u16 instruction) const { 2038 [[nodiscard]] constexpr bool Matches(u16 instruction) const {
2032 return (instruction & mask) == expected; 2039 return (instruction & mask) == expected;
2033 } 2040 }
2034 2041
@@ -2040,7 +2047,8 @@ public:
2040 Type type; 2047 Type type;
2041 }; 2048 };
2042 2049
2043 static std::optional<std::reference_wrapper<const Matcher>> Decode(Instruction instr) { 2050 using DecodeResult = std::optional<std::reference_wrapper<const Matcher>>;
2051 [[nodiscard]] static DecodeResult Decode(Instruction instr) {
2044 static const auto table{GetDecodeTable()}; 2052 static const auto table{GetDecodeTable()};
2045 2053
2046 const auto matches_instruction = [instr](const auto& matcher) { 2054 const auto matches_instruction = [instr](const auto& matcher) {
@@ -2062,7 +2070,7 @@ private:
2062 * A '0' in a bitstring indicates that a zero must be present at that bit position. 2070 * A '0' in a bitstring indicates that a zero must be present at that bit position.
2063 * A '1' in a bitstring indicates that a one must be present at that bit position. 2071 * A '1' in a bitstring indicates that a one must be present at that bit position.
2064 */ 2072 */
2065 static constexpr auto GetMaskAndExpect(const char* const bitstring) { 2073 [[nodiscard]] static constexpr auto GetMaskAndExpect(const char* const bitstring) {
2066 u16 mask = 0, expect = 0; 2074 u16 mask = 0, expect = 0;
2067 for (std::size_t i = 0; i < opcode_bitsize; i++) { 2075 for (std::size_t i = 0; i < opcode_bitsize; i++) {
2068 const std::size_t bit_position = opcode_bitsize - i - 1; 2076 const std::size_t bit_position = opcode_bitsize - i - 1;
@@ -2084,14 +2092,14 @@ private:
2084 2092
2085 public: 2093 public:
2086 /// Creates a matcher that can match and parse instructions based on bitstring. 2094 /// Creates a matcher that can match and parse instructions based on bitstring.
2087 static constexpr auto GetMatcher(const char* const bitstring, Id op, Type type, 2095 [[nodiscard]] static constexpr auto GetMatcher(const char* const bitstring, Id op,
2088 const char* const name) { 2096 Type type, const char* const name) {
2089 const auto [mask, expected] = GetMaskAndExpect(bitstring); 2097 const auto [mask, expected] = GetMaskAndExpect(bitstring);
2090 return Matcher(name, mask, expected, op, type); 2098 return Matcher(name, mask, expected, op, type);
2091 } 2099 }
2092 }; 2100 };
2093 2101
2094 static std::vector<Matcher> GetDecodeTable() { 2102 [[nodiscard]] static std::vector<Matcher> GetDecodeTable() {
2095 std::vector<Matcher> table = { 2103 std::vector<Matcher> table = {
2096#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name) 2104#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
2097 INST("111000110011----", Id::KIL, Type::Flow, "KIL"), 2105 INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index cf5235a79..21410e125 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -17,11 +17,11 @@
17#include "video_core/dma_pusher.h" 17#include "video_core/dma_pusher.h"
18 18
19using CacheAddr = std::uintptr_t; 19using CacheAddr = std::uintptr_t;
20inline CacheAddr ToCacheAddr(const void* host_ptr) { 20[[nodiscard]] inline CacheAddr ToCacheAddr(const void* host_ptr) {
21 return reinterpret_cast<CacheAddr>(host_ptr); 21 return reinterpret_cast<CacheAddr>(host_ptr);
22} 22}
23 23
24inline u8* FromCacheAddr(CacheAddr cache_addr) { 24[[nodiscard]] inline u8* FromCacheAddr(CacheAddr cache_addr) {
25 return reinterpret_cast<u8*>(cache_addr); 25 return reinterpret_cast<u8*>(cache_addr);
26} 26}
27 27
@@ -149,13 +149,13 @@ public:
149 u32 subchannel{}; 149 u32 subchannel{};
150 u32 method_count{}; 150 u32 method_count{};
151 151
152 bool IsLastCall() const {
153 return method_count <= 1;
154 }
155
156 MethodCall(u32 method, u32 argument, u32 subchannel = 0, u32 method_count = 0) 152 MethodCall(u32 method, u32 argument, u32 subchannel = 0, u32 method_count = 0)
157 : method(method), argument(argument), subchannel(subchannel), 153 : method(method), argument(argument), subchannel(subchannel),
158 method_count(method_count) {} 154 method_count(method_count) {}
155
156 [[nodiscard]] bool IsLastCall() const {
157 return method_count <= 1;
158 }
159 }; 159 };
160 160
161 explicit GPU(Core::System& system, bool is_async, bool use_nvdec); 161 explicit GPU(Core::System& system, bool is_async, bool use_nvdec);
@@ -179,10 +179,10 @@ public:
179 virtual void OnCommandListEnd(); 179 virtual void OnCommandListEnd();
180 180
181 /// Request a host GPU memory flush from the CPU. 181 /// Request a host GPU memory flush from the CPU.
182 u64 RequestFlush(VAddr addr, std::size_t size); 182 [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size);
183 183
184 /// Obtains current flush request fence id. 184 /// Obtains current flush request fence id.
185 u64 CurrentFlushRequestFence() const { 185 [[nodiscard]] u64 CurrentFlushRequestFence() const {
186 return current_flush_fence.load(std::memory_order_relaxed); 186 return current_flush_fence.load(std::memory_order_relaxed);
187 } 187 }
188 188
@@ -190,48 +190,52 @@ public:
190 void TickWork(); 190 void TickWork();
191 191
192 /// Returns a reference to the Maxwell3D GPU engine. 192 /// Returns a reference to the Maxwell3D GPU engine.
193 Engines::Maxwell3D& Maxwell3D(); 193 [[nodiscard]] Engines::Maxwell3D& Maxwell3D();
194 194
195 /// Returns a const reference to the Maxwell3D GPU engine. 195 /// Returns a const reference to the Maxwell3D GPU engine.
196 const Engines::Maxwell3D& Maxwell3D() const; 196 [[nodiscard]] const Engines::Maxwell3D& Maxwell3D() const;
197 197
198 /// Returns a reference to the KeplerCompute GPU engine. 198 /// Returns a reference to the KeplerCompute GPU engine.
199 Engines::KeplerCompute& KeplerCompute(); 199 [[nodiscard]] Engines::KeplerCompute& KeplerCompute();
200 200
201 /// Returns a reference to the KeplerCompute GPU engine. 201 /// Returns a reference to the KeplerCompute GPU engine.
202 const Engines::KeplerCompute& KeplerCompute() const; 202 [[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const;
203 203
204 /// Returns a reference to the GPU memory manager. 204 /// Returns a reference to the GPU memory manager.
205 Tegra::MemoryManager& MemoryManager(); 205 [[nodiscard]] Tegra::MemoryManager& MemoryManager();
206 206
207 /// Returns a const reference to the GPU memory manager. 207 /// Returns a const reference to the GPU memory manager.
208 const Tegra::MemoryManager& MemoryManager() const; 208 [[nodiscard]] const Tegra::MemoryManager& MemoryManager() const;
209 209
210 /// Returns a reference to the GPU DMA pusher. 210 /// Returns a reference to the GPU DMA pusher.
211 Tegra::DmaPusher& DmaPusher(); 211 [[nodiscard]] Tegra::DmaPusher& DmaPusher();
212 212
213 /// Returns a const reference to the GPU DMA pusher. 213 /// Returns a const reference to the GPU DMA pusher.
214 const Tegra::DmaPusher& DmaPusher() const; 214 [[nodiscard]] const Tegra::DmaPusher& DmaPusher() const;
215 215
216 /// Returns a reference to the GPU CDMA pusher. 216 /// Returns a reference to the GPU CDMA pusher.
217 Tegra::CDmaPusher& CDmaPusher(); 217 [[nodiscard]] Tegra::CDmaPusher& CDmaPusher();
218 218
219 /// Returns a const reference to the GPU CDMA pusher. 219 /// Returns a const reference to the GPU CDMA pusher.
220 const Tegra::CDmaPusher& CDmaPusher() const; 220 [[nodiscard]] const Tegra::CDmaPusher& CDmaPusher() const;
221 221
222 VideoCore::RendererBase& Renderer() { 222 /// Returns a reference to the underlying renderer.
223 [[nodiscard]] VideoCore::RendererBase& Renderer() {
223 return *renderer; 224 return *renderer;
224 } 225 }
225 226
226 const VideoCore::RendererBase& Renderer() const { 227 /// Returns a const reference to the underlying renderer.
228 [[nodiscard]] const VideoCore::RendererBase& Renderer() const {
227 return *renderer; 229 return *renderer;
228 } 230 }
229 231
230 VideoCore::ShaderNotify& ShaderNotify() { 232 /// Returns a reference to the shader notifier.
233 [[nodiscard]] VideoCore::ShaderNotify& ShaderNotify() {
231 return *shader_notify; 234 return *shader_notify;
232 } 235 }
233 236
234 const VideoCore::ShaderNotify& ShaderNotify() const { 237 /// Returns a const reference to the shader notifier.
238 [[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const {
235 return *shader_notify; 239 return *shader_notify;
236 } 240 }
237 241
@@ -243,23 +247,23 @@ public:
243 247
244 void IncrementSyncPoint(u32 syncpoint_id); 248 void IncrementSyncPoint(u32 syncpoint_id);
245 249
246 u32 GetSyncpointValue(u32 syncpoint_id) const; 250 [[nodiscard]] u32 GetSyncpointValue(u32 syncpoint_id) const;
247 251
248 void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value); 252 void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value);
249 253
250 bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value); 254 [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value);
251 255
252 u64 GetTicks() const; 256 [[nodiscard]] u64 GetTicks() const;
253 257
254 std::unique_lock<std::mutex> LockSync() { 258 [[nodiscard]] std::unique_lock<std::mutex> LockSync() {
255 return std::unique_lock{sync_mutex}; 259 return std::unique_lock{sync_mutex};
256 } 260 }
257 261
258 bool IsAsync() const { 262 [[nodiscard]] bool IsAsync() const {
259 return is_async; 263 return is_async;
260 } 264 }
261 265
262 bool UseNvdec() const { 266 [[nodiscard]] bool UseNvdec() const {
263 return use_nvdec; 267 return use_nvdec;
264 } 268 }
265 269
@@ -273,7 +277,7 @@ public:
273 BitField<0, 1, FenceOperation> op; 277 BitField<0, 1, FenceOperation> op;
274 BitField<8, 24, u32> syncpoint_id; 278 BitField<8, 24, u32> syncpoint_id;
275 279
276 static CommandHeader Build(FenceOperation op, u32 syncpoint_id) { 280 [[nodiscard]] static CommandHeader Build(FenceOperation op, u32 syncpoint_id) {
277 FenceAction result{}; 281 FenceAction result{};
278 result.op.Assign(op); 282 result.op.Assign(op);
279 result.syncpoint_id.Assign(syncpoint_id); 283 result.syncpoint_id.Assign(syncpoint_id);
@@ -291,7 +295,7 @@ public:
291 u32 address_high; 295 u32 address_high;
292 u32 address_low; 296 u32 address_low;
293 297
294 GPUVAddr SemaphoreAddress() const { 298 [[nodiscard]] GPUVAddr SemaphoreAddress() const {
295 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 299 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
296 address_low); 300 address_low);
297 } 301 }
@@ -374,7 +378,7 @@ private:
374 u32 methods_pending); 378 u32 methods_pending);
375 379
376 /// Determines where the method should be executed. 380 /// Determines where the method should be executed.
377 bool ExecuteMethodOnEngine(u32 method); 381 [[nodiscard]] bool ExecuteMethodOnEngine(u32 method);
378 382
379protected: 383protected:
380 Core::System& system; 384 Core::System& system;
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index b3e0919f8..27ef4c69a 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -32,7 +32,7 @@ using DiskResourceLoadCallback = std::function<void(LoadCallbackStage, std::size
32 32
33class RasterizerInterface { 33class RasterizerInterface {
34public: 34public:
35 virtual ~RasterizerInterface() {} 35 virtual ~RasterizerInterface() = default;
36 36
37 /// Dispatches a draw invocation 37 /// Dispatches a draw invocation
38 virtual void Draw(bool is_indexed, bool is_instanced) = 0; 38 virtual void Draw(bool is_indexed, bool is_instanced) = 0;
@@ -90,15 +90,16 @@ public:
90 virtual void TickFrame() = 0; 90 virtual void TickFrame() = 0;
91 91
92 /// Attempt to use a faster method to perform a surface copy 92 /// Attempt to use a faster method to perform a surface copy
93 virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, 93 [[nodiscard]] virtual bool AccelerateSurfaceCopy(
94 const Tegra::Engines::Fermi2D::Regs::Surface& dst, 94 const Tegra::Engines::Fermi2D::Regs::Surface& src,
95 const Tegra::Engines::Fermi2D::Config& copy_config) { 95 const Tegra::Engines::Fermi2D::Regs::Surface& dst,
96 const Tegra::Engines::Fermi2D::Config& copy_config) {
96 return false; 97 return false;
97 } 98 }
98 99
99 /// Attempt to use a faster method to display the framebuffer to screen 100 /// Attempt to use a faster method to display the framebuffer to screen
100 virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 101 [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config,
101 u32 pixel_stride) { 102 VAddr framebuffer_addr, u32 pixel_stride) {
102 return false; 103 return false;
103 } 104 }
104 105
@@ -110,12 +111,12 @@ public:
110 const DiskResourceLoadCallback& callback) {} 111 const DiskResourceLoadCallback& callback) {}
111 112
112 /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver. 113 /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
113 GuestDriverProfile& AccessGuestDriverProfile() { 114 [[nodiscard]] GuestDriverProfile& AccessGuestDriverProfile() {
114 return guest_driver_profile; 115 return guest_driver_profile;
115 } 116 }
116 117
117 /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver. 118 /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
118 const GuestDriverProfile& AccessGuestDriverProfile() const { 119 [[nodiscard]] const GuestDriverProfile& AccessGuestDriverProfile() const {
119 return guest_driver_profile; 120 return guest_driver_profile;
120 } 121 }
121 122
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 5c650808b..51dde8eb5 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -38,7 +38,7 @@ public:
38 virtual ~RendererBase(); 38 virtual ~RendererBase();
39 39
40 /// Initialize the renderer 40 /// Initialize the renderer
41 virtual bool Init() = 0; 41 [[nodiscard]] virtual bool Init() = 0;
42 42
43 /// Shutdown the renderer 43 /// Shutdown the renderer
44 virtual void ShutDown() = 0; 44 virtual void ShutDown() = 0;
@@ -49,43 +49,43 @@ public:
49 // Getter/setter functions: 49 // Getter/setter functions:
50 // ------------------------ 50 // ------------------------
51 51
52 f32 GetCurrentFPS() const { 52 [[nodiscard]] f32 GetCurrentFPS() const {
53 return m_current_fps; 53 return m_current_fps;
54 } 54 }
55 55
56 int GetCurrentFrame() const { 56 [[nodiscard]] int GetCurrentFrame() const {
57 return m_current_frame; 57 return m_current_frame;
58 } 58 }
59 59
60 RasterizerInterface& Rasterizer() { 60 [[nodiscard]] RasterizerInterface& Rasterizer() {
61 return *rasterizer; 61 return *rasterizer;
62 } 62 }
63 63
64 const RasterizerInterface& Rasterizer() const { 64 [[nodiscard]] const RasterizerInterface& Rasterizer() const {
65 return *rasterizer; 65 return *rasterizer;
66 } 66 }
67 67
68 Core::Frontend::GraphicsContext& Context() { 68 [[nodiscard]] Core::Frontend::GraphicsContext& Context() {
69 return *context; 69 return *context;
70 } 70 }
71 71
72 const Core::Frontend::GraphicsContext& Context() const { 72 [[nodiscard]] const Core::Frontend::GraphicsContext& Context() const {
73 return *context; 73 return *context;
74 } 74 }
75 75
76 Core::Frontend::EmuWindow& GetRenderWindow() { 76 [[nodiscard]] Core::Frontend::EmuWindow& GetRenderWindow() {
77 return render_window; 77 return render_window;
78 } 78 }
79 79
80 const Core::Frontend::EmuWindow& GetRenderWindow() const { 80 [[nodiscard]] const Core::Frontend::EmuWindow& GetRenderWindow() const {
81 return render_window; 81 return render_window;
82 } 82 }
83 83
84 RendererSettings& Settings() { 84 [[nodiscard]] RendererSettings& Settings() {
85 return renderer_settings; 85 return renderer_settings;
86 } 86 }
87 87
88 const RendererSettings& Settings() const { 88 [[nodiscard]] const RendererSettings& Settings() const {
89 return renderer_settings; 89 return renderer_settings;
90 } 90 }
91 91
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 36bf92808..cfddbde5d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1156,7 +1156,7 @@ void RasterizerOpenGL::SyncViewport() {
1156 flags[Dirty::ClipControl] = false; 1156 flags[Dirty::ClipControl] = false;
1157 1157
1158 bool flip_y = false; 1158 bool flip_y = false;
1159 if (regs.viewport_transform[0].scale_y < 0.0) { 1159 if (regs.viewport_transform[0].scale_y < 0.0f) {
1160 flip_y = !flip_y; 1160 flip_y = !flip_y;
1161 } 1161 }
1162 if (regs.screen_y_control.y_negate != 0) { 1162 if (regs.screen_y_control.y_negate != 0) {
@@ -1579,10 +1579,6 @@ void RasterizerOpenGL::SyncAlphaTest() {
1579 flags[Dirty::AlphaTest] = false; 1579 flags[Dirty::AlphaTest] = false;
1580 1580
1581 const auto& regs = maxwell3d.regs; 1581 const auto& regs = maxwell3d.regs;
1582 if (regs.alpha_test_enabled && regs.rt_control.count > 1) {
1583 LOG_WARNING(Render_OpenGL, "Alpha testing with more than one render target is not tested");
1584 }
1585
1586 if (regs.alpha_test_enabled) { 1582 if (regs.alpha_test_enabled) {
1587 glEnable(GL_ALPHA_TEST); 1583 glEnable(GL_ALPHA_TEST);
1588 glAlphaFunc(MaxwellToGL::ComparisonOp(regs.alpha_test_func), regs.alpha_test_ref); 1584 glAlphaFunc(MaxwellToGL::ComparisonOp(regs.alpha_test_func), regs.alpha_test_ref);
diff --git a/src/video_core/shader/async_shaders.cpp b/src/video_core/shader/async_shaders.cpp
index 39cc3b869..6920afdf2 100644
--- a/src/video_core/shader/async_shaders.cpp
+++ b/src/video_core/shader/async_shaders.cpp
@@ -43,8 +43,8 @@ void AsyncShaders::AllocateWorkers() {
43 // Create workers 43 // Create workers
44 for (std::size_t i = 0; i < num_workers; i++) { 44 for (std::size_t i = 0; i < num_workers; i++) {
45 context_list.push_back(emu_window.CreateSharedContext()); 45 context_list.push_back(emu_window.CreateSharedContext());
46 worker_threads.push_back( 46 worker_threads.emplace_back(&AsyncShaders::ShaderCompilerThread, this,
47 std::thread(&AsyncShaders::ShaderCompilerThread, this, context_list[i].get())); 47 context_list[i].get());
48 } 48 }
49} 49}
50 50
@@ -106,8 +106,7 @@ std::vector<AsyncShaders::Result> AsyncShaders::GetCompletedWork() {
106 std::vector<Result> results; 106 std::vector<Result> results;
107 { 107 {
108 std::unique_lock lock{completed_mutex}; 108 std::unique_lock lock{completed_mutex};
109 results.assign(std::make_move_iterator(finished_work.begin()), 109 results = std::move(finished_work);
110 std::make_move_iterator(finished_work.end()));
111 finished_work.clear(); 110 finished_work.clear();
112 } 111 }
113 return results; 112 return results;
@@ -116,11 +115,10 @@ std::vector<AsyncShaders::Result> AsyncShaders::GetCompletedWork() {
116void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device, 115void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
117 Tegra::Engines::ShaderType shader_type, u64 uid, 116 Tegra::Engines::ShaderType shader_type, u64 uid,
118 std::vector<u64> code, std::vector<u64> code_b, 117 std::vector<u64> code, std::vector<u64> code_b,
119 u32 main_offset, 118 u32 main_offset, CompilerSettings compiler_settings,
120 VideoCommon::Shader::CompilerSettings compiler_settings, 119 const Registry& registry, VAddr cpu_addr) {
121 const VideoCommon::Shader::Registry& registry, 120 std::unique_lock lock(queue_mutex);
122 VAddr cpu_addr) { 121 pending_queue.push({
123 WorkerParams params{
124 .backend = device.UseAssemblyShaders() ? Backend::GLASM : Backend::OpenGL, 122 .backend = device.UseAssemblyShaders() ? Backend::GLASM : Backend::OpenGL,
125 .device = &device, 123 .device = &device,
126 .shader_type = shader_type, 124 .shader_type = shader_type,
@@ -131,9 +129,7 @@ void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
131 .compiler_settings = compiler_settings, 129 .compiler_settings = compiler_settings,
132 .registry = registry, 130 .registry = registry,
133 .cpu_address = cpu_addr, 131 .cpu_address = cpu_addr,
134 }; 132 });
135 std::unique_lock lock(queue_mutex);
136 pending_queue.push(std::move(params));
137 cv.notify_one(); 133 cv.notify_one();
138} 134}
139 135
@@ -145,7 +141,8 @@ void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
145 std::vector<VkDescriptorSetLayoutBinding> bindings, 141 std::vector<VkDescriptorSetLayoutBinding> bindings,
146 Vulkan::SPIRVProgram program, 142 Vulkan::SPIRVProgram program,
147 Vulkan::GraphicsPipelineCacheKey key) { 143 Vulkan::GraphicsPipelineCacheKey key) {
148 WorkerParams params{ 144 std::unique_lock lock(queue_mutex);
145 pending_queue.push({
149 .backend = Backend::Vulkan, 146 .backend = Backend::Vulkan,
150 .pp_cache = pp_cache, 147 .pp_cache = pp_cache,
151 .vk_device = &device, 148 .vk_device = &device,
@@ -153,13 +150,10 @@ void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
153 .descriptor_pool = &descriptor_pool, 150 .descriptor_pool = &descriptor_pool,
154 .update_descriptor_queue = &update_descriptor_queue, 151 .update_descriptor_queue = &update_descriptor_queue,
155 .renderpass_cache = &renderpass_cache, 152 .renderpass_cache = &renderpass_cache,
156 .bindings = bindings, 153 .bindings = std::move(bindings),
157 .program = program, 154 .program = std::move(program),
158 .key = key, 155 .key = key,
159 }; 156 });
160
161 std::unique_lock lock(queue_mutex);
162 pending_queue.push(std::move(params));
163 cv.notify_one(); 157 cv.notify_one();
164} 158}
165 159
diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp
index 618d309d2..1ed4212ee 100644
--- a/src/video_core/shader/decode/image.cpp
+++ b/src/video_core/shader/decode/image.cpp
@@ -212,10 +212,10 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) {
212 return 0; 212 return 0;
213 case TextureFormat::R8G24: 213 case TextureFormat::R8G24:
214 if (component == 0) { 214 if (component == 0) {
215 return 8; 215 return 24;
216 } 216 }
217 if (component == 1) { 217 if (component == 1) {
218 return 24; 218 return 8;
219 } 219 }
220 return 0; 220 return 0;
221 case TextureFormat::R8G8: 221 case TextureFormat::R8G8:
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 8abb74d56..b16b54032 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -68,12 +68,12 @@ add_executable(yuzu
68 configuration/configure_input_advanced.cpp 68 configuration/configure_input_advanced.cpp
69 configuration/configure_input_advanced.h 69 configuration/configure_input_advanced.h
70 configuration/configure_input_advanced.ui 70 configuration/configure_input_advanced.ui
71 configuration/configure_input_dialog.cpp
72 configuration/configure_input_dialog.h
73 configuration/configure_input_dialog.ui
74 configuration/configure_input_player.cpp 71 configuration/configure_input_player.cpp
75 configuration/configure_input_player.h 72 configuration/configure_input_player.h
76 configuration/configure_input_player.ui 73 configuration/configure_input_player.ui
74 configuration/configure_input_profile_dialog.cpp
75 configuration/configure_input_profile_dialog.h
76 configuration/configure_input_profile_dialog.ui
77 configuration/configure_motion_touch.cpp 77 configuration/configure_motion_touch.cpp
78 configuration/configure_motion_touch.h 78 configuration/configure_motion_touch.h
79 configuration/configure_motion_touch.ui 79 configuration/configure_motion_touch.ui
@@ -105,9 +105,14 @@ add_executable(yuzu
105 configuration/configure_ui.cpp 105 configuration/configure_ui.cpp
106 configuration/configure_ui.h 106 configuration/configure_ui.h
107 configuration/configure_ui.ui 107 configuration/configure_ui.ui
108 configuration/configure_vibration.cpp
109 configuration/configure_vibration.h
110 configuration/configure_vibration.ui
108 configuration/configure_web.cpp 111 configuration/configure_web.cpp
109 configuration/configure_web.h 112 configuration/configure_web.h
110 configuration/configure_web.ui 113 configuration/configure_web.ui
114 configuration/input_profiles.cpp
115 configuration/input_profiles.h
111 debugger/console.cpp 116 debugger/console.cpp
112 debugger/console.h 117 debugger/console.h
113 debugger/profiler.cpp 118 debugger/profiler.cpp
diff --git a/src/yuzu/aboutdialog.ui b/src/yuzu/aboutdialog.ui
index f122ba39d..1b320630c 100644
--- a/src/yuzu/aboutdialog.ui
+++ b/src/yuzu/aboutdialog.ui
@@ -160,32 +160,12 @@ p, li { white-space: pre-wrap; }
160 <signal>accepted()</signal> 160 <signal>accepted()</signal>
161 <receiver>AboutDialog</receiver> 161 <receiver>AboutDialog</receiver>
162 <slot>accept()</slot> 162 <slot>accept()</slot>
163 <hints>
164 <hint type="sourcelabel">
165 <x>248</x>
166 <y>254</y>
167 </hint>
168 <hint type="destinationlabel">
169 <x>157</x>
170 <y>274</y>
171 </hint>
172 </hints>
173 </connection> 163 </connection>
174 <connection> 164 <connection>
175 <sender>buttonBox</sender> 165 <sender>buttonBox</sender>
176 <signal>rejected()</signal> 166 <signal>rejected()</signal>
177 <receiver>AboutDialog</receiver> 167 <receiver>AboutDialog</receiver>
178 <slot>reject()</slot> 168 <slot>reject()</slot>
179 <hints>
180 <hint type="sourcelabel">
181 <x>316</x>
182 <y>260</y>
183 </hint>
184 <hint type="destinationlabel">
185 <x>286</x>
186 <y>274</y>
187 </hint>
188 </hints>
189 </connection> 169 </connection>
190 </connections> 170 </connections>
191</ui> 171</ui>
diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp
index c6fa3e4f6..8ecfec770 100644
--- a/src/yuzu/applets/controller.cpp
+++ b/src/yuzu/applets/controller.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <thread>
6 7
7#include "common/assert.h" 8#include "common/assert.h"
8#include "common/string_util.h" 9#include "common/string_util.h"
@@ -13,11 +14,16 @@
13#include "core/hle/service/sm/sm.h" 14#include "core/hle/service/sm/sm.h"
14#include "ui_controller.h" 15#include "ui_controller.h"
15#include "yuzu/applets/controller.h" 16#include "yuzu/applets/controller.h"
16#include "yuzu/configuration/configure_input_dialog.h" 17#include "yuzu/configuration/configure_input.h"
18#include "yuzu/configuration/configure_input_profile_dialog.h"
19#include "yuzu/configuration/configure_vibration.h"
20#include "yuzu/configuration/input_profiles.h"
17#include "yuzu/main.h" 21#include "yuzu/main.h"
18 22
19namespace { 23namespace {
20 24
25constexpr std::size_t HANDHELD_INDEX = 8;
26
21constexpr std::array<std::array<bool, 4>, 8> led_patterns{{ 27constexpr std::array<std::array<bool, 4>, 8> led_patterns{{
22 {true, false, false, false}, 28 {true, false, false, false},
23 {true, true, false, false}, 29 {true, true, false, false},
@@ -106,7 +112,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
106 QWidget* parent, Core::Frontend::ControllerParameters parameters_, 112 QWidget* parent, Core::Frontend::ControllerParameters parameters_,
107 InputCommon::InputSubsystem* input_subsystem_) 113 InputCommon::InputSubsystem* input_subsystem_)
108 : QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()), 114 : QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()),
109 parameters(std::move(parameters_)), input_subsystem(input_subsystem_) { 115 parameters(std::move(parameters_)), input_subsystem{input_subsystem_},
116 input_profiles(std::make_unique<InputProfiles>()) {
110 ui->setupUi(this); 117 ui->setupUi(this);
111 118
112 player_widgets = { 119 player_widgets = {
@@ -223,12 +230,22 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
223 } 230 }
224 } 231 }
225 232
233 connect(ui->vibrationButton, &QPushButton::clicked, this,
234 &QtControllerSelectorDialog::CallConfigureVibrationDialog);
235
226 connect(ui->inputConfigButton, &QPushButton::clicked, this, 236 connect(ui->inputConfigButton, &QPushButton::clicked, this,
227 &QtControllerSelectorDialog::CallConfigureInputDialog); 237 &QtControllerSelectorDialog::CallConfigureInputProfileDialog);
228 238
229 connect(ui->buttonBox, &QDialogButtonBox::accepted, this, 239 connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
230 &QtControllerSelectorDialog::ApplyConfiguration); 240 &QtControllerSelectorDialog::ApplyConfiguration);
231 241
242 // Enhancement: Check if the parameters have already been met before disconnecting controllers.
243 // If all the parameters are met AND only allows a single player,
244 // stop the constructor here as we do not need to continue.
245 if (CheckIfParametersMet() && parameters.enable_single_mode) {
246 return;
247 }
248
232 // If keep_controllers_connected is false, forcefully disconnect all controllers 249 // If keep_controllers_connected is false, forcefully disconnect all controllers
233 if (!parameters.keep_controllers_connected) { 250 if (!parameters.keep_controllers_connected) {
234 for (auto player : player_groupboxes) { 251 for (auto player : player_groupboxes) {
@@ -236,58 +253,66 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
236 } 253 }
237 } 254 }
238 255
239 CheckIfParametersMet();
240
241 resize(0, 0); 256 resize(0, 0);
242} 257}
243 258
244QtControllerSelectorDialog::~QtControllerSelectorDialog() = default; 259QtControllerSelectorDialog::~QtControllerSelectorDialog() = default;
245 260
246void QtControllerSelectorDialog::ApplyConfiguration() { 261int QtControllerSelectorDialog::exec() {
247 // Update the controller state once more, just to be sure they are properly applied. 262 if (parameters_met && parameters.enable_single_mode) {
248 for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { 263 return QDialog::Accepted;
249 UpdateControllerState(index);
250 } 264 }
265 return QDialog::exec();
266}
251 267
252 const bool pre_docked_mode = Settings::values.use_docked_mode; 268void QtControllerSelectorDialog::ApplyConfiguration() {
253 Settings::values.use_docked_mode = ui->radioDocked->isChecked(); 269 const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
254 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode); 270 Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
271 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue());
255 272
256 Settings::values.vibration_enabled = ui->vibrationGroup->isChecked(); 273 Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
274 Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
257} 275}
258 276
259void QtControllerSelectorDialog::LoadConfiguration() { 277void QtControllerSelectorDialog::LoadConfiguration() {
260 for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { 278 for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
261 const auto connected = Settings::values.players[index].connected || 279 const auto connected =
262 (index == 0 && Settings::values.players[8].connected); 280 Settings::values.players.GetValue()[index].connected ||
281 (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
263 player_groupboxes[index]->setChecked(connected); 282 player_groupboxes[index]->setChecked(connected);
264 connected_controller_checkboxes[index]->setChecked(connected); 283 connected_controller_checkboxes[index]->setChecked(connected);
265 emulated_controllers[index]->setCurrentIndex( 284 emulated_controllers[index]->setCurrentIndex(
266 GetIndexFromControllerType(Settings::values.players[index].controller_type)); 285 GetIndexFromControllerType(Settings::values.players.GetValue()[index].controller_type));
267 } 286 }
268 287
269 UpdateDockedState(Settings::values.players[8].connected); 288 UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
270 289
271 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled); 290 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
291 ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
272} 292}
273 293
274void QtControllerSelectorDialog::CallConfigureInputDialog() { 294void QtControllerSelectorDialog::CallConfigureVibrationDialog() {
275 const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; 295 ConfigureVibration dialog(this);
276
277 ConfigureInputDialog dialog(this, max_supported_players, input_subsystem);
278 296
279 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | 297 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
280 Qt::WindowSystemMenuHint); 298 Qt::WindowSystemMenuHint);
281 dialog.setWindowModality(Qt::WindowModal); 299 dialog.setWindowModality(Qt::WindowModal);
282 dialog.exec();
283 300
284 dialog.ApplyConfiguration(); 301 if (dialog.exec() == QDialog::Accepted) {
302 dialog.ApplyConfiguration();
303 }
304}
285 305
286 LoadConfiguration(); 306void QtControllerSelectorDialog::CallConfigureInputProfileDialog() {
287 CheckIfParametersMet(); 307 ConfigureInputProfileDialog dialog(this, input_subsystem, input_profiles.get());
308
309 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
310 Qt::WindowSystemMenuHint);
311 dialog.setWindowModality(Qt::WindowModal);
312 dialog.exec();
288} 313}
289 314
290void QtControllerSelectorDialog::CheckIfParametersMet() { 315bool QtControllerSelectorDialog::CheckIfParametersMet() {
291 // Here, we check and validate the current configuration against all applicable parameters. 316 // Here, we check and validate the current configuration against all applicable parameters.
292 const auto num_connected_players = static_cast<int>( 317 const auto num_connected_players = static_cast<int>(
293 std::count_if(player_groupboxes.begin(), player_groupboxes.end(), 318 std::count_if(player_groupboxes.begin(), player_groupboxes.end(),
@@ -301,7 +326,7 @@ void QtControllerSelectorDialog::CheckIfParametersMet() {
301 num_connected_players > max_supported_players) { 326 num_connected_players > max_supported_players) {
302 parameters_met = false; 327 parameters_met = false;
303 ui->buttonBox->setEnabled(parameters_met); 328 ui->buttonBox->setEnabled(parameters_met);
304 return; 329 return parameters_met;
305 } 330 }
306 331
307 // Next, check against all connected controllers. 332 // Next, check against all connected controllers.
@@ -326,18 +351,13 @@ void QtControllerSelectorDialog::CheckIfParametersMet() {
326 return true; 351 return true;
327 }(); 352 }();
328 353
329 if (!all_controllers_compatible) { 354 parameters_met = all_controllers_compatible;
330 parameters_met = false;
331 ui->buttonBox->setEnabled(parameters_met);
332 return;
333 }
334
335 parameters_met = true;
336 ui->buttonBox->setEnabled(parameters_met); 355 ui->buttonBox->setEnabled(parameters_met);
356 return parameters_met;
337} 357}
338 358
339void QtControllerSelectorDialog::SetSupportedControllers() { 359void QtControllerSelectorDialog::SetSupportedControllers() {
340 const QString theme = [this] { 360 const QString theme = [] {
341 if (QIcon::themeName().contains(QStringLiteral("dark"))) { 361 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
342 return QStringLiteral("_dark"); 362 return QStringLiteral("_dark");
343 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) { 363 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
@@ -426,7 +446,7 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
426 } 446 }
427 }(); 447 }();
428 448
429 const QString theme = [this] { 449 const QString theme = [] {
430 if (QIcon::themeName().contains(QStringLiteral("dark"))) { 450 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
431 return QStringLiteral("_dark"); 451 return QStringLiteral("_dark");
432 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) { 452 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
@@ -441,32 +461,48 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
441} 461}
442 462
443void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) { 463void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
444 auto& player = Settings::values.players[player_index]; 464 auto& player = Settings::values.players.GetValue()[player_index];
445 465
446 player.controller_type = 466 const auto controller_type =
447 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()); 467 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex());
448 player.connected = player_groupboxes[player_index]->isChecked(); 468 const auto player_connected = player_groupboxes[player_index]->isChecked() &&
469 controller_type != Settings::ControllerType::Handheld;
449 470
450 // Player 2-8 471 if (player.controller_type == controller_type && player.connected == player_connected) {
451 if (player_index != 0) { 472 // Set vibration devices in the event that the input device has changed.
452 UpdateController(player.controller_type, player_index, player.connected); 473 ConfigureVibration::SetVibrationDevices(player_index);
453 return; 474 return;
454 } 475 }
455 476
456 // Player 1 and Handheld 477 // Disconnect the controller first.
457 auto& handheld = Settings::values.players[8]; 478 UpdateController(controller_type, player_index, false);
458 // If Handheld is selected, copy all the settings from Player 1 to Handheld. 479
459 if (player.controller_type == Settings::ControllerType::Handheld) { 480 player.controller_type = controller_type;
460 handheld = player; 481 player.connected = player_connected;
461 handheld.connected = player_groupboxes[player_index]->isChecked(); 482
462 player.connected = false; // Disconnect Player 1 483 ConfigureVibration::SetVibrationDevices(player_index);
463 } else { 484
464 player.connected = player_groupboxes[player_index]->isChecked(); 485 // Handheld
465 handheld.connected = false; // Disconnect Handheld 486 if (player_index == 0) {
487 auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
488 if (controller_type == Settings::ControllerType::Handheld) {
489 handheld = player;
490 }
491 handheld.connected = player_groupboxes[player_index]->isChecked() &&
492 controller_type == Settings::ControllerType::Handheld;
493 UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected);
466 } 494 }
467 495
468 UpdateController(player.controller_type, player_index, player.connected); 496 if (!player.connected) {
469 UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected); 497 return;
498 }
499
500 // This emulates a delay between disconnecting and reconnecting controllers as some games
501 // do not respond to a change in controller type if it was instantaneous.
502 using namespace std::chrono_literals;
503 std::this_thread::sleep_for(20ms);
504
505 UpdateController(controller_type, player_index, player_connected);
470} 506}
471 507
472void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { 508void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
@@ -520,8 +556,8 @@ void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) {
520 ui->radioDocked->setEnabled(!is_handheld); 556 ui->radioDocked->setEnabled(!is_handheld);
521 ui->radioUndocked->setEnabled(!is_handheld); 557 ui->radioUndocked->setEnabled(!is_handheld);
522 558
523 ui->radioDocked->setChecked(Settings::values.use_docked_mode); 559 ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue());
524 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode); 560 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue());
525 561
526 // Also force into undocked mode if the controller type is handheld. 562 // Also force into undocked mode if the controller type is handheld.
527 if (is_handheld) { 563 if (is_handheld) {
@@ -564,8 +600,8 @@ void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
564 600
565 for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) { 601 for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) {
566 // Disconnect any unsupported players here and disable or hide them if applicable. 602 // Disconnect any unsupported players here and disable or hide them if applicable.
567 Settings::values.players[index].connected = false; 603 Settings::values.players.GetValue()[index].connected = false;
568 UpdateController(Settings::values.players[index].controller_type, index, false); 604 UpdateController(Settings::values.players.GetValue()[index].controller_type, index, false);
569 // Hide the player widgets when max_supported_controllers is less than or equal to 4. 605 // Hide the player widgets when max_supported_controllers is less than or equal to 4.
570 if (max_supported_players <= 4) { 606 if (max_supported_players <= 4) {
571 player_widgets[index]->hide(); 607 player_widgets[index]->hide();
diff --git a/src/yuzu/applets/controller.h b/src/yuzu/applets/controller.h
index 729ecc831..4344e1dd0 100644
--- a/src/yuzu/applets/controller.h
+++ b/src/yuzu/applets/controller.h
@@ -16,6 +16,8 @@ class QDialogButtonBox;
16class QGroupBox; 16class QGroupBox;
17class QLabel; 17class QLabel;
18 18
19class InputProfiles;
20
19namespace InputCommon { 21namespace InputCommon {
20class InputSubsystem; 22class InputSubsystem;
21} 23}
@@ -33,6 +35,8 @@ public:
33 InputCommon::InputSubsystem* input_subsystem_); 35 InputCommon::InputSubsystem* input_subsystem_);
34 ~QtControllerSelectorDialog() override; 36 ~QtControllerSelectorDialog() override;
35 37
38 int exec() override;
39
36private: 40private:
37 // Applies the current configuration. 41 // Applies the current configuration.
38 void ApplyConfiguration(); 42 void ApplyConfiguration();
@@ -40,12 +44,15 @@ private:
40 // Loads the current input configuration into the frontend applet. 44 // Loads the current input configuration into the frontend applet.
41 void LoadConfiguration(); 45 void LoadConfiguration();
42 46
43 // Initializes the "Configure Input" Dialog. 47 // Initializes the "Configure Vibration" Dialog.
44 void CallConfigureInputDialog(); 48 void CallConfigureVibrationDialog();
45 49
46 // Checks the current configuration against the given parameters and 50 // Initializes the "Create Input Profile" Dialog.
47 // sets the value of parameters_met. 51 void CallConfigureInputProfileDialog();
48 void CheckIfParametersMet(); 52
53 // Checks the current configuration against the given parameters.
54 // This sets and returns the value of parameters_met.
55 bool CheckIfParametersMet();
49 56
50 // Sets the controller icons for "Supported Controller Types". 57 // Sets the controller icons for "Supported Controller Types".
51 void SetSupportedControllers(); 58 void SetSupportedControllers();
@@ -78,6 +85,8 @@ private:
78 85
79 InputCommon::InputSubsystem* input_subsystem; 86 InputCommon::InputSubsystem* input_subsystem;
80 87
88 std::unique_ptr<InputProfiles> input_profiles;
89
81 // This is true if and only if all parameters are met. Otherwise, this is false. 90 // This is true if and only if all parameters are met. Otherwise, this is false.
82 // This determines whether the "OK" button can be clicked to exit the applet. 91 // This determines whether the "OK" button can be clicked to exit the applet.
83 bool parameters_met{false}; 92 bool parameters_met{false};
diff --git a/src/yuzu/applets/controller.ui b/src/yuzu/applets/controller.ui
index c4108a979..c8cb6bcf3 100644
--- a/src/yuzu/applets/controller.ui
+++ b/src/yuzu/applets/controller.ui
@@ -1217,9 +1217,6 @@
1217 </item> 1217 </item>
1218 <item> 1218 <item>
1219 <widget class="QComboBox" name="comboPlayer3Emulated"> 1219 <widget class="QComboBox" name="comboPlayer3Emulated">
1220 <property name="editable">
1221 <bool>false</bool>
1222 </property>
1223 <item> 1220 <item>
1224 <property name="text"> 1221 <property name="text">
1225 <string>Pro Controller</string> 1222 <string>Pro Controller</string>
@@ -2279,7 +2276,7 @@
2279 <number>6</number> 2276 <number>6</number>
2280 </property> 2277 </property>
2281 <property name="leftMargin"> 2278 <property name="leftMargin">
2282 <number>6</number> 2279 <number>8</number>
2283 </property> 2280 </property>
2284 <property name="topMargin"> 2281 <property name="topMargin">
2285 <number>6</number> 2282 <number>6</number>
@@ -2332,30 +2329,24 @@
2332 <number>3</number> 2329 <number>3</number>
2333 </property> 2330 </property>
2334 <item> 2331 <item>
2335 <widget class="QSpinBox" name="vibrationSpin"> 2332 <widget class="QPushButton" name="vibrationButton">
2336 <property name="minimumSize"> 2333 <property name="minimumSize">
2337 <size> 2334 <size>
2338 <width>65</width> 2335 <width>68</width>
2339 <height>0</height> 2336 <height>0</height>
2340 </size> 2337 </size>
2341 </property> 2338 </property>
2342 <property name="maximumSize"> 2339 <property name="maximumSize">
2343 <size> 2340 <size>
2344 <width>65</width> 2341 <width>68</width>
2345 <height>16777215</height> 2342 <height>16777215</height>
2346 </size> 2343 </size>
2347 </property> 2344 </property>
2348 <property name="suffix"> 2345 <property name="styleSheet">
2349 <string>%</string> 2346 <string notr="true">min-width: 68px;</string>
2350 </property>
2351 <property name="minimum">
2352 <number>1</number>
2353 </property>
2354 <property name="maximum">
2355 <number>200</number>
2356 </property> 2347 </property>
2357 <property name="value"> 2348 <property name="text">
2358 <number>100</number> 2349 <string>Configure</string>
2359 </property> 2350 </property>
2360 </widget> 2351 </widget>
2361 </item> 2352 </item>
@@ -2387,18 +2378,18 @@
2387 <widget class="QPushButton" name="motionButton"> 2378 <widget class="QPushButton" name="motionButton">
2388 <property name="minimumSize"> 2379 <property name="minimumSize">
2389 <size> 2380 <size>
2390 <width>57</width> 2381 <width>68</width>
2391 <height>0</height> 2382 <height>0</height>
2392 </size> 2383 </size>
2393 </property> 2384 </property>
2394 <property name="maximumSize"> 2385 <property name="maximumSize">
2395 <size> 2386 <size>
2396 <width>55</width> 2387 <width>68</width>
2397 <height>16777215</height> 2388 <height>16777215</height>
2398 </size> 2389 </size>
2399 </property> 2390 </property>
2400 <property name="styleSheet"> 2391 <property name="styleSheet">
2401 <string notr="true">min-width: 55px;</string> 2392 <string notr="true">min-width: 68px;</string>
2402 </property> 2393 </property>
2403 <property name="text"> 2394 <property name="text">
2404 <string>Configure</string> 2395 <string>Configure</string>
@@ -2411,7 +2402,7 @@
2411 <item> 2402 <item>
2412 <widget class="QGroupBox" name="inputConfigGroup"> 2403 <widget class="QGroupBox" name="inputConfigGroup">
2413 <property name="title"> 2404 <property name="title">
2414 <string>Input Config</string> 2405 <string>Profiles</string>
2415 </property> 2406 </property>
2416 <layout class="QHBoxLayout" name="horizontalLayout_7"> 2407 <layout class="QHBoxLayout" name="horizontalLayout_7">
2417 <property name="leftMargin"> 2408 <property name="leftMargin">
@@ -2430,15 +2421,15 @@
2430 <widget class="QPushButton" name="inputConfigButton"> 2421 <widget class="QPushButton" name="inputConfigButton">
2431 <property name="maximumSize"> 2422 <property name="maximumSize">
2432 <size> 2423 <size>
2433 <width>65</width> 2424 <width>68</width>
2434 <height>16777215</height> 2425 <height>16777215</height>
2435 </size> 2426 </size>
2436 </property> 2427 </property>
2437 <property name="styleSheet"> 2428 <property name="styleSheet">
2438 <string notr="true">min-width: 55px;</string> 2429 <string notr="true">min-width: 68px;</string>
2439 </property> 2430 </property>
2440 <property name="text"> 2431 <property name="text">
2441 <string>Open</string> 2432 <string>Create</string>
2442 </property> 2433 </property>
2443 </widget> 2434 </widget>
2444 </item> 2435 </item>
@@ -2657,16 +2648,6 @@
2657 <signal>accepted()</signal> 2648 <signal>accepted()</signal>
2658 <receiver>QtControllerSelectorDialog</receiver> 2649 <receiver>QtControllerSelectorDialog</receiver>
2659 <slot>accept()</slot> 2650 <slot>accept()</slot>
2660 <hints>
2661 <hint type="sourcelabel">
2662 <x>20</x>
2663 <y>20</y>
2664 </hint>
2665 <hint type="destinationlabel">
2666 <x>20</x>
2667 <y>20</y>
2668 </hint>
2669 </hints>
2670 </connection> 2651 </connection>
2671 </connections> 2652 </connections>
2672</ui> 2653</ui>
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 408eac2b7..f0338cf7a 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -10,6 +10,7 @@
10#include <QMessageBox> 10#include <QMessageBox>
11#include <QPainter> 11#include <QPainter>
12#include <QScreen> 12#include <QScreen>
13#include <QString>
13#include <QStringList> 14#include <QStringList>
14#include <QWindow> 15#include <QWindow>
15 16
@@ -301,6 +302,12 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
301 this->setMouseTracking(true); 302 this->setMouseTracking(true);
302 303
303 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); 304 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
305 connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
306 Qt::QueuedConnection);
307}
308
309void GRenderWindow::ExecuteProgram(std::size_t program_index) {
310 emit ExecuteProgramSignal(program_index);
304} 311}
305 312
306GRenderWindow::~GRenderWindow() { 313GRenderWindow::~GRenderWindow() {
@@ -381,7 +388,12 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
381} 388}
382 389
383void GRenderWindow::mousePressEvent(QMouseEvent* event) { 390void GRenderWindow::mousePressEvent(QMouseEvent* event) {
384 // touch input is handled in TouchBeginEvent 391 if (!Settings::values.touchscreen.enabled) {
392 input_subsystem->GetKeyboard()->PressKey(event->button());
393 return;
394 }
395
396 // Touch input is handled in TouchBeginEvent
385 if (event->source() == Qt::MouseEventSynthesizedBySystem) { 397 if (event->source() == Qt::MouseEventSynthesizedBySystem) {
386 return; 398 return;
387 } 399 }
@@ -397,7 +409,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
397} 409}
398 410
399void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { 411void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
400 // touch input is handled in TouchUpdateEvent 412 // Touch input is handled in TouchUpdateEvent
401 if (event->source() == Qt::MouseEventSynthesizedBySystem) { 413 if (event->source() == Qt::MouseEventSynthesizedBySystem) {
402 return; 414 return;
403 } 415 }
@@ -410,7 +422,12 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
410} 422}
411 423
412void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { 424void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
413 // touch input is handled in TouchEndEvent 425 if (!Settings::values.touchscreen.enabled) {
426 input_subsystem->GetKeyboard()->ReleaseKey(event->button());
427 return;
428 }
429
430 // Touch input is handled in TouchEndEvent
414 if (event->source() == Qt::MouseEventSynthesizedBySystem) { 431 if (event->source() == Qt::MouseEventSynthesizedBySystem) {
415 return; 432 return;
416 } 433 }
@@ -603,19 +620,33 @@ bool GRenderWindow::LoadOpenGL() {
603 auto context = CreateSharedContext(); 620 auto context = CreateSharedContext();
604 auto scope = context->Acquire(); 621 auto scope = context->Acquire();
605 if (!gladLoadGL()) { 622 if (!gladLoadGL()) {
606 QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), 623 QMessageBox::warning(
607 tr("Your GPU may not support OpenGL 4.3, or you do not have the " 624 this, tr("Error while initializing OpenGL!"),
608 "latest graphics driver.")); 625 tr("Your GPU may not support OpenGL, or you do not have the latest graphics driver."));
626 return false;
627 }
628
629 const QString renderer =
630 QString::fromUtf8(reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
631
632 if (!GLAD_GL_VERSION_4_3) {
633 LOG_ERROR(Frontend, "GPU does not support OpenGL 4.3: {}", renderer.toStdString());
634 QMessageBox::warning(this, tr("Error while initializing OpenGL 4.3!"),
635 tr("Your GPU may not support OpenGL 4.3, or you do not have the "
636 "latest graphics driver.<br><br>GL Renderer:<br>%1")
637 .arg(renderer));
609 return false; 638 return false;
610 } 639 }
611 640
612 QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); 641 QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions();
613 if (!unsupported_gl_extensions.empty()) { 642 if (!unsupported_gl_extensions.empty()) {
614 QMessageBox::critical( 643 QMessageBox::warning(
615 this, tr("Error while initializing OpenGL!"), 644 this, tr("Error while initializing OpenGL!"),
616 tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you " 645 tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you "
617 "have the latest graphics driver.<br><br>Unsupported extensions:<br>") + 646 "have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported "
618 unsupported_gl_extensions.join(QStringLiteral("<br>"))); 647 "extensions:<br>%2")
648 .arg(renderer)
649 .arg(unsupported_gl_extensions.join(QStringLiteral("<br>"))));
619 return false; 650 return false;
620 } 651 }
621 return true; 652 return true;
@@ -645,8 +676,13 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
645 if (!GLAD_GL_ARB_depth_buffer_float) 676 if (!GLAD_GL_ARB_depth_buffer_float)
646 unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float")); 677 unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float"));
647 678
648 for (const QString& ext : unsupported_ext) 679 if (!unsupported_ext.empty()) {
649 LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); 680 LOG_ERROR(Frontend, "GPU does not support all required extensions: {}",
681 glGetString(GL_RENDERER));
682 }
683 for (const QString& ext : unsupported_ext) {
684 LOG_ERROR(Frontend, "Unsupported GL extension: {}", ext.toStdString());
685 }
650 686
651 return unsupported_ext; 687 return unsupported_ext;
652} 688}
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index ca35cf831..503b4f89e 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -166,6 +166,12 @@ public:
166 166
167 std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; 167 std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
168 168
169 /**
170 * Instructs the window to re-launch the application using the specified program_index.
171 * @param program_index Specifies the index within the application of the program to launch.
172 */
173 void ExecuteProgram(std::size_t program_index);
174
169public slots: 175public slots:
170 void OnEmulationStarting(EmuThread* emu_thread); 176 void OnEmulationStarting(EmuThread* emu_thread);
171 void OnEmulationStopping(); 177 void OnEmulationStopping();
@@ -175,6 +181,7 @@ signals:
175 /// Emitted when the window is closed 181 /// Emitted when the window is closed
176 void Closed(); 182 void Closed();
177 void FirstFrameDisplayed(); 183 void FirstFrameDisplayed();
184 void ExecuteProgramSignal(std::size_t program_index);
178 185
179private: 186private:
180 void TouchBeginEvent(const QTouchEvent* event); 187 void TouchBeginEvent(const QTouchEvent* event);
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 1ce62e4a6..3c423a271 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -5,6 +5,7 @@
5#include <array> 5#include <array>
6#include <QKeySequence> 6#include <QKeySequence>
7#include <QSettings> 7#include <QSettings>
8#include "common/common_paths.h"
8#include "common/file_util.h" 9#include "common/file_util.h"
9#include "core/hle/service/acc/profile_manager.h" 10#include "core/hle/service/acc/profile_manager.h"
10#include "core/hle/service/hid/controllers/npad.h" 11#include "core/hle/service/hid/controllers/npad.h"
@@ -14,14 +15,10 @@
14 15
15namespace FS = Common::FS; 16namespace FS = Common::FS;
16 17
17Config::Config(const std::string& config_file, bool is_global) { 18Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) {
18 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 19 global = config_type == ConfigType::GlobalConfig;
19 qt_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + config_file; 20
20 FS::CreateFullPath(qt_config_loc); 21 Initialize(config_name);
21 qt_config =
22 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
23 global = is_global;
24 Reload();
25} 22}
26 23
27Config::~Config() { 24Config::~Config() {
@@ -242,84 +239,152 @@ const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
242}}; 239}};
243// clang-format on 240// clang-format on
244 241
245void Config::ReadPlayerValues() { 242void Config::Initialize(const std::string& config_name) {
246 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 243 switch (type) {
247 auto& player = Settings::values.players[p]; 244 case ConfigType::GlobalConfig:
245 qt_config_loc = fmt::format("{}" DIR_SEP "{}.ini", FS::GetUserPath(FS::UserPath::ConfigDir),
246 config_name);
247 FS::CreateFullPath(qt_config_loc);
248 qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
249 QSettings::IniFormat);
250 Reload();
251 break;
252 case ConfigType::PerGameConfig:
253 qt_config_loc = fmt::format("{}custom" DIR_SEP "{}.ini",
254 FS::GetUserPath(FS::UserPath::ConfigDir), config_name);
255 FS::CreateFullPath(qt_config_loc);
256 qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
257 QSettings::IniFormat);
258 Reload();
259 break;
260 case ConfigType::InputProfile:
261 qt_config_loc = fmt::format("{}input" DIR_SEP "{}.ini",
262 FS::GetUserPath(FS::UserPath::ConfigDir), config_name);
263 FS::CreateFullPath(qt_config_loc);
264 qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
265 QSettings::IniFormat);
266 break;
267 }
268}
269
270void Config::ReadPlayerValue(std::size_t player_index) {
271 const QString player_prefix = [this, player_index] {
272 if (type == ConfigType::InputProfile) {
273 return QString{};
274 } else {
275 return QStringLiteral("player_%1_").arg(player_index);
276 }
277 }();
278
279 auto& player = Settings::values.players.GetValue()[player_index];
280
281 if (player_prefix.isEmpty()) {
282 const auto controller = static_cast<Settings::ControllerType>(
283 qt_config
284 ->value(QStringLiteral("%1type").arg(player_prefix),
285 static_cast<u8>(Settings::ControllerType::ProController))
286 .toUInt());
248 287
288 if (controller == Settings::ControllerType::LeftJoycon ||
289 controller == Settings::ControllerType::RightJoycon) {
290 player.controller_type = controller;
291 }
292 } else {
249 player.connected = 293 player.connected =
250 ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool(); 294 ReadSetting(QStringLiteral("%1connected").arg(player_prefix), player_index == 0)
295 .toBool();
251 296
252 player.controller_type = static_cast<Settings::ControllerType>( 297 player.controller_type = static_cast<Settings::ControllerType>(
253 qt_config 298 qt_config
254 ->value(QStringLiteral("player_%1_type").arg(p), 299 ->value(QStringLiteral("%1type").arg(player_prefix),
255 static_cast<u8>(Settings::ControllerType::ProController)) 300 static_cast<u8>(Settings::ControllerType::ProController))
256 .toUInt()); 301 .toUInt());
257 302
303 player.vibration_enabled =
304 qt_config->value(QStringLiteral("%1vibration_enabled").arg(player_prefix), true)
305 .toBool();
306
307 player.vibration_strength =
308 qt_config->value(QStringLiteral("%1vibration_strength").arg(player_prefix), 100)
309 .toInt();
310
258 player.body_color_left = qt_config 311 player.body_color_left = qt_config
259 ->value(QStringLiteral("player_%1_body_color_left").arg(p), 312 ->value(QStringLiteral("%1body_color_left").arg(player_prefix),
260 Settings::JOYCON_BODY_NEON_BLUE) 313 Settings::JOYCON_BODY_NEON_BLUE)
261 .toUInt(); 314 .toUInt();
262 player.body_color_right = qt_config 315 player.body_color_right =
263 ->value(QStringLiteral("player_%1_body_color_right").arg(p), 316 qt_config
264 Settings::JOYCON_BODY_NEON_RED) 317 ->value(QStringLiteral("%1body_color_right").arg(player_prefix),
265 .toUInt(); 318 Settings::JOYCON_BODY_NEON_RED)
266 player.button_color_left = qt_config 319 .toUInt();
267 ->value(QStringLiteral("player_%1_button_color_left").arg(p), 320 player.button_color_left =
268 Settings::JOYCON_BUTTONS_NEON_BLUE) 321 qt_config
269 .toUInt(); 322 ->value(QStringLiteral("%1button_color_left").arg(player_prefix),
323 Settings::JOYCON_BUTTONS_NEON_BLUE)
324 .toUInt();
270 player.button_color_right = 325 player.button_color_right =
271 qt_config 326 qt_config
272 ->value(QStringLiteral("player_%1_button_color_right").arg(p), 327 ->value(QStringLiteral("%1button_color_right").arg(player_prefix),
273 Settings::JOYCON_BUTTONS_NEON_RED) 328 Settings::JOYCON_BUTTONS_NEON_RED)
274 .toUInt(); 329 .toUInt();
330 }
275 331
276 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 332 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
277 const std::string default_param = 333 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
278 InputCommon::GenerateKeyboardParam(default_buttons[i]); 334 auto& player_buttons = player.buttons[i];
279 auto& player_buttons = player.buttons[i]; 335
280 336 player_buttons = qt_config
281 player_buttons = qt_config 337 ->value(QStringLiteral("%1").arg(player_prefix) +
282 ->value(QStringLiteral("player_%1_").arg(p) + 338 QString::fromUtf8(Settings::NativeButton::mapping[i]),
283 QString::fromUtf8(Settings::NativeButton::mapping[i]), 339 QString::fromStdString(default_param))
284 QString::fromStdString(default_param)) 340 .toString()
285 .toString() 341 .toStdString();
286 .toStdString(); 342 if (player_buttons.empty()) {
287 if (player_buttons.empty()) { 343 player_buttons = default_param;
288 player_buttons = default_param;
289 }
290 } 344 }
345 }
291 346
292 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { 347 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
293 const std::string default_param = 348 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
294 InputCommon::GenerateKeyboardParam(default_motions[i]); 349 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
295 auto& player_motions = player.motions[i]; 350 default_analogs[i][3], default_stick_mod[i], 0.5f);
296 351 auto& player_analogs = player.analogs[i];
297 player_motions = qt_config 352
298 ->value(QStringLiteral("player_%1_").arg(p) + 353 player_analogs = qt_config
299 QString::fromUtf8(Settings::NativeMotion::mapping[i]), 354 ->value(QStringLiteral("%1").arg(player_prefix) +
300 QString::fromStdString(default_param)) 355 QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
301 .toString() 356 QString::fromStdString(default_param))
302 .toStdString(); 357 .toString()
303 if (player_motions.empty()) { 358 .toStdString();
304 player_motions = default_param; 359 if (player_analogs.empty()) {
305 } 360 player_analogs = default_param;
306 } 361 }
362 }
307 363
308 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 364 for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
309 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 365 auto& player_vibrations = player.vibrations[i];
310 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 366
311 default_analogs[i][3], default_stick_mod[i], 0.5f); 367 player_vibrations =
312 auto& player_analogs = player.analogs[i]; 368 qt_config
313 369 ->value(QStringLiteral("%1").arg(player_prefix) +
314 player_analogs = qt_config 370 QString::fromUtf8(Settings::NativeVibration::mapping[i]),
315 ->value(QStringLiteral("player_%1_").arg(p) + 371 QString{})
316 QString::fromUtf8(Settings::NativeAnalog::mapping[i]), 372 .toString()
317 QString::fromStdString(default_param)) 373 .toStdString();
318 .toString() 374 }
319 .toStdString(); 375
320 if (player_analogs.empty()) { 376 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
321 player_analogs = default_param; 377 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
322 } 378 auto& player_motions = player.motions[i];
379
380 player_motions = qt_config
381 ->value(QStringLiteral("%1").arg(player_prefix) +
382 QString::fromUtf8(Settings::NativeMotion::mapping[i]),
383 QString::fromStdString(default_param))
384 .toString()
385 .toStdString();
386 if (player_motions.empty()) {
387 player_motions = default_param;
323 } 388 }
324 } 389 }
325} 390}
@@ -436,18 +501,21 @@ void Config::ReadAudioValues() {
436void Config::ReadControlValues() { 501void Config::ReadControlValues() {
437 qt_config->beginGroup(QStringLiteral("Controls")); 502 qt_config->beginGroup(QStringLiteral("Controls"));
438 503
439 ReadPlayerValues(); 504 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
505 ReadPlayerValue(p);
506 }
440 ReadDebugValues(); 507 ReadDebugValues();
441 ReadKeyboardValues(); 508 ReadKeyboardValues();
442 ReadMouseValues(); 509 ReadMouseValues();
443 ReadTouchscreenValues(); 510 ReadTouchscreenValues();
444 ReadMotionTouchValues(); 511 ReadMotionTouchValues();
445 512
446 Settings::values.vibration_enabled = 513 ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), false);
447 ReadSetting(QStringLiteral("vibration_enabled"), true).toBool(); 514 ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"),
448 Settings::values.motion_enabled = ReadSetting(QStringLiteral("motion_enabled"), true).toBool(); 515 true);
449 Settings::values.use_docked_mode = 516 ReadSettingGlobal(Settings::values.enable_accurate_vibrations,
450 ReadSetting(QStringLiteral("use_docked_mode"), false).toBool(); 517 QStringLiteral("enable_accurate_vibrations"), false);
518 ReadSettingGlobal(Settings::values.motion_enabled, QStringLiteral("motion_enabled"), true);
451 519
452 qt_config->endGroup(); 520 qt_config->endGroup();
453} 521}
@@ -581,6 +649,8 @@ void Config::ReadDebuggingValues() {
581 Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool(); 649 Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool();
582 Settings::values.disable_macro_jit = 650 Settings::values.disable_macro_jit =
583 ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool(); 651 ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool();
652 Settings::values.extended_logging =
653 ReadSetting(QStringLiteral("extended_logging"), false).toBool();
584 654
585 qt_config->endGroup(); 655 qt_config->endGroup();
586} 656}
@@ -920,49 +990,64 @@ void Config::ReadValues() {
920 ReadSystemValues(); 990 ReadSystemValues();
921} 991}
922 992
923void Config::SavePlayerValues() { 993void Config::SavePlayerValue(std::size_t player_index) {
924 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 994 const QString player_prefix = [this, player_index] {
925 const auto& player = Settings::values.players[p]; 995 if (type == ConfigType::InputProfile) {
996 return QString{};
997 } else {
998 return QStringLiteral("player_%1_").arg(player_index);
999 }
1000 }();
926 1001
927 WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false); 1002 const auto& player = Settings::values.players.GetValue()[player_index];
928 WriteSetting(QStringLiteral("player_%1_type").arg(p),
929 static_cast<u8>(player.controller_type),
930 static_cast<u8>(Settings::ControllerType::ProController));
931 1003
932 WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left, 1004 WriteSetting(QStringLiteral("%1type").arg(player_prefix),
1005 static_cast<u8>(player.controller_type),
1006 static_cast<u8>(Settings::ControllerType::ProController));
1007
1008 if (!player_prefix.isEmpty()) {
1009 WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, false);
1010 WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix),
1011 player.vibration_enabled, true);
1012 WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix),
1013 player.vibration_strength, 100);
1014 WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left,
933 Settings::JOYCON_BODY_NEON_BLUE); 1015 Settings::JOYCON_BODY_NEON_BLUE);
934 WriteSetting(QStringLiteral("player_%1_body_color_right").arg(p), player.body_color_right, 1016 WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix),
935 Settings::JOYCON_BODY_NEON_RED); 1017 player.body_color_right, Settings::JOYCON_BODY_NEON_RED);
936 WriteSetting(QStringLiteral("player_%1_button_color_left").arg(p), player.button_color_left, 1018 WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix),
937 Settings::JOYCON_BUTTONS_NEON_BLUE); 1019 player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE);
938 WriteSetting(QStringLiteral("player_%1_button_color_right").arg(p), 1020 WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix),
939 player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED); 1021 player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED);
1022 }
940 1023
941 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 1024 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
942 const std::string default_param = 1025 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
943 InputCommon::GenerateKeyboardParam(default_buttons[i]); 1026 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
944 WriteSetting(QStringLiteral("player_%1_").arg(p) + 1027 QString::fromStdString(Settings::NativeButton::mapping[i]),
945 QString::fromStdString(Settings::NativeButton::mapping[i]), 1028 QString::fromStdString(player.buttons[i]),
946 QString::fromStdString(player.buttons[i]), 1029 QString::fromStdString(default_param));
947 QString::fromStdString(default_param)); 1030 }
948 } 1031 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
949 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { 1032 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
950 const std::string default_param = 1033 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
951 InputCommon::GenerateKeyboardParam(default_motions[i]); 1034 default_analogs[i][3], default_stick_mod[i], 0.5f);
952 WriteSetting(QStringLiteral("player_%1_").arg(p) + 1035 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
953 QString::fromStdString(Settings::NativeMotion::mapping[i]), 1036 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
954 QString::fromStdString(player.motions[i]), 1037 QString::fromStdString(player.analogs[i]),
955 QString::fromStdString(default_param)); 1038 QString::fromStdString(default_param));
956 } 1039 }
957 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 1040 for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
958 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 1041 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
959 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 1042 QString::fromStdString(Settings::NativeVibration::mapping[i]),
960 default_analogs[i][3], default_stick_mod[i], 0.5f); 1043 QString::fromStdString(player.vibrations[i]), QString{});
961 WriteSetting(QStringLiteral("player_%1_").arg(p) + 1044 }
962 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 1045 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
963 QString::fromStdString(player.analogs[i]), 1046 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
964 QString::fromStdString(default_param)); 1047 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
965 } 1048 QString::fromStdString(Settings::NativeMotion::mapping[i]),
1049 QString::fromStdString(player.motions[i]),
1050 QString::fromStdString(default_param));
966 } 1051 }
967} 1052}
968 1053
@@ -1087,14 +1172,20 @@ void Config::SaveAudioValues() {
1087void Config::SaveControlValues() { 1172void Config::SaveControlValues() {
1088 qt_config->beginGroup(QStringLiteral("Controls")); 1173 qt_config->beginGroup(QStringLiteral("Controls"));
1089 1174
1090 SavePlayerValues(); 1175 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
1176 SavePlayerValue(p);
1177 }
1091 SaveDebugValues(); 1178 SaveDebugValues();
1092 SaveMouseValues(); 1179 SaveMouseValues();
1093 SaveTouchscreenValues(); 1180 SaveTouchscreenValues();
1094 SaveMotionTouchValues(); 1181 SaveMotionTouchValues();
1095 1182
1096 WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true); 1183 WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
1097 WriteSetting(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true); 1184 WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled,
1185 true);
1186 WriteSettingGlobal(QStringLiteral("enable_accurate_vibrations"),
1187 Settings::values.enable_accurate_vibrations, false);
1188 WriteSettingGlobal(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true);
1098 WriteSetting(QStringLiteral("motion_device"), 1189 WriteSetting(QStringLiteral("motion_device"),
1099 QString::fromStdString(Settings::values.motion_device), 1190 QString::fromStdString(Settings::values.motion_device),
1100 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); 1191 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
@@ -1102,7 +1193,6 @@ void Config::SaveControlValues() {
1102 QString::fromStdString(Settings::values.touch_device), 1193 QString::fromStdString(Settings::values.touch_device),
1103 QStringLiteral("engine:emu_window")); 1194 QStringLiteral("engine:emu_window"));
1104 WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false); 1195 WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false);
1105 WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
1106 1196
1107 qt_config->endGroup(); 1197 qt_config->endGroup();
1108} 1198}
@@ -1515,3 +1605,19 @@ void Config::Save() {
1515 Settings::Sanitize(); 1605 Settings::Sanitize();
1516 SaveValues(); 1606 SaveValues();
1517} 1607}
1608
1609void Config::ReadControlPlayerValue(std::size_t player_index) {
1610 qt_config->beginGroup(QStringLiteral("Controls"));
1611 ReadPlayerValue(player_index);
1612 qt_config->endGroup();
1613}
1614
1615void Config::SaveControlPlayerValue(std::size_t player_index) {
1616 qt_config->beginGroup(QStringLiteral("Controls"));
1617 SavePlayerValue(player_index);
1618 qt_config->endGroup();
1619}
1620
1621const std::string& Config::GetConfigFilePath() const {
1622 return qt_config_loc;
1623}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 5d8e45d78..8a600e19d 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -16,12 +16,24 @@ class QSettings;
16 16
17class Config { 17class Config {
18public: 18public:
19 explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true); 19 enum class ConfigType {
20 GlobalConfig,
21 PerGameConfig,
22 InputProfile,
23 };
24
25 explicit Config(const std::string& config_name = "qt-config",
26 ConfigType config_type = ConfigType::GlobalConfig);
20 ~Config(); 27 ~Config();
21 28
22 void Reload(); 29 void Reload();
23 void Save(); 30 void Save();
24 31
32 void ReadControlPlayerValue(std::size_t player_index);
33 void SaveControlPlayerValue(std::size_t player_index);
34
35 const std::string& GetConfigFilePath() const;
36
25 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; 37 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
26 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions; 38 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
27 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; 39 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
@@ -33,8 +45,10 @@ public:
33 static const std::array<UISettings::Shortcut, 16> default_hotkeys; 45 static const std::array<UISettings::Shortcut, 16> default_hotkeys;
34 46
35private: 47private:
48 void Initialize(const std::string& config_name);
49
36 void ReadValues(); 50 void ReadValues();
37 void ReadPlayerValues(); 51 void ReadPlayerValue(std::size_t player_index);
38 void ReadDebugValues(); 52 void ReadDebugValues();
39 void ReadKeyboardValues(); 53 void ReadKeyboardValues();
40 void ReadMouseValues(); 54 void ReadMouseValues();
@@ -62,7 +76,7 @@ private:
62 void ReadWebServiceValues(); 76 void ReadWebServiceValues();
63 77
64 void SaveValues(); 78 void SaveValues();
65 void SavePlayerValues(); 79 void SavePlayerValue(std::size_t player_index);
66 void SaveDebugValues(); 80 void SaveDebugValues();
67 void SaveMouseValues(); 81 void SaveMouseValues();
68 void SaveTouchscreenValues(); 82 void SaveTouchscreenValues();
@@ -111,9 +125,9 @@ private:
111 void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global, 125 void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
112 const QVariant& default_value); 126 const QVariant& default_value);
113 127
128 ConfigType type;
114 std::unique_ptr<QSettings> qt_config; 129 std::unique_ptr<QSettings> qt_config;
115 std::string qt_config_loc; 130 std::string qt_config_loc;
116
117 bool global; 131 bool global;
118}; 132};
119 133
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index fcf42cdcb..f92c3aff3 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -275,32 +275,12 @@
275 <signal>accepted()</signal> 275 <signal>accepted()</signal>
276 <receiver>ConfigureDialog</receiver> 276 <receiver>ConfigureDialog</receiver>
277 <slot>accept()</slot> 277 <slot>accept()</slot>
278 <hints>
279 <hint type="sourcelabel">
280 <x>220</x>
281 <y>380</y>
282 </hint>
283 <hint type="destinationlabel">
284 <x>220</x>
285 <y>200</y>
286 </hint>
287 </hints>
288 </connection> 278 </connection>
289 <connection> 279 <connection>
290 <sender>buttonBox</sender> 280 <sender>buttonBox</sender>
291 <signal>rejected()</signal> 281 <signal>rejected()</signal>
292 <receiver>ConfigureDialog</receiver> 282 <receiver>ConfigureDialog</receiver>
293 <slot>reject()</slot> 283 <slot>reject()</slot>
294 <hints>
295 <hint type="sourcelabel">
296 <x>220</x>
297 <y>380</y>
298 </hint>
299 <hint type="destinationlabel">
300 <x>220</x>
301 <y>200</y>
302 </hint>
303 </hints>
304 </connection> 284 </connection>
305 </connections> 285 </connections>
306</ui> 286</ui>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 2bfe2c306..027099ab7 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -41,6 +41,7 @@ void ConfigureDebug::SetConfiguration() {
41 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); 41 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug);
42 ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 42 ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
43 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit); 43 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit);
44 ui->extended_logging->setChecked(Settings::values.extended_logging);
44} 45}
45 46
46void ConfigureDebug::ApplyConfiguration() { 47void ConfigureDebug::ApplyConfiguration() {
@@ -53,6 +54,7 @@ void ConfigureDebug::ApplyConfiguration() {
53 Settings::values.quest_flag = ui->quest_flag->isChecked(); 54 Settings::values.quest_flag = ui->quest_flag->isChecked();
54 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); 55 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
55 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); 56 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
57 Settings::values.extended_logging = ui->extended_logging->isChecked();
56 Debugger::ToggleConsole(); 58 Debugger::ToggleConsole();
57 Log::Filter filter; 59 Log::Filter filter;
58 filter.ParseFilterString(Settings::values.log_filter); 60 filter.ParseFilterString(Settings::values.log_filter);
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 9d6feb9f7..6f94fe304 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -90,7 +90,7 @@
90 <item> 90 <item>
91 <widget class="QCheckBox" name="toggle_console"> 91 <widget class="QCheckBox" name="toggle_console">
92 <property name="text"> 92 <property name="text">
93 <string>Show Log Console (Windows Only)</string> 93 <string>Show Log in Console</string>
94 </property> 94 </property>
95 </widget> 95 </widget>
96 </item> 96 </item>
@@ -103,6 +103,34 @@
103 </item> 103 </item>
104 </layout> 104 </layout>
105 </item> 105 </item>
106 <item>
107 <widget class="QCheckBox" name="extended_logging">
108 <property name="enabled">
109 <bool>true</bool>
110 </property>
111 <property name="toolTip">
112 <string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
113 </property>
114 <property name="text">
115 <string>Enable Extended Logging</string>
116 </property>
117 </widget>
118 </item>
119 <item>
120 <widget class="QLabel" name="label_3">
121 <property name="font">
122 <font>
123 <italic>true</italic>
124 </font>
125 </property>
126 <property name="text">
127 <string>This will be reset automatically when yuzu closes.</string>
128 </property>
129 <property name="indent">
130 <number>20</number>
131 </property>
132 </widget>
133 </item>
106 </layout> 134 </layout>
107 </widget> 135 </widget>
108 </item> 136 </item>
@@ -115,7 +143,7 @@
115 <item> 143 <item>
116 <layout class="QHBoxLayout" name="horizontalLayout_4"> 144 <layout class="QHBoxLayout" name="horizontalLayout_4">
117 <item> 145 <item>
118 <widget class="QLabel" name="label_3"> 146 <widget class="QLabel" name="label_4">
119 <property name="text"> 147 <property name="text">
120 <string>Arguments String</string> 148 <string>Arguments String</string>
121 </property> 149 </property>
@@ -140,8 +168,8 @@
140 <property name="enabled"> 168 <property name="enabled">
141 <bool>true</bool> 169 <bool>true</bool>
142 </property> 170 </property>
143 <property name="whatsThis"> 171 <property name="toolTip">
144 <string>When checked, the graphics API enters in a slower debugging mode</string> 172 <string>When checked, the graphics API enters a slower debugging mode</string>
145 </property> 173 </property>
146 <property name="text"> 174 <property name="text">
147 <string>Enable Graphics Debugging</string> 175 <string>Enable Graphics Debugging</string>
@@ -153,8 +181,8 @@
153 <property name="enabled"> 181 <property name="enabled">
154 <bool>true</bool> 182 <bool>true</bool>
155 </property> 183 </property>
156 <property name="whatsThis"> 184 <property name="toolTip">
157 <string>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</string> 185 <string>When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower</string>
158 </property> 186 </property>
159 <property name="text"> 187 <property name="text">
160 <string>Disable Macro JIT</string> 188 <string>Disable Macro JIT</string>
@@ -169,7 +197,7 @@
169 <property name="title"> 197 <property name="title">
170 <string>Dump</string> 198 <string>Dump</string>
171 </property> 199 </property>
172 <layout class="QVBoxLayout" name="verticalLayout_6"> 200 <layout class="QVBoxLayout" name="verticalLayout_7">
173 <item> 201 <item>
174 <widget class="QCheckBox" name="reporting_services"> 202 <widget class="QCheckBox" name="reporting_services">
175 <property name="text"> 203 <property name="text">
@@ -178,7 +206,7 @@
178 </widget> 206 </widget>
179 </item> 207 </item>
180 <item> 208 <item>
181 <widget class="QLabel" name="label"> 209 <widget class="QLabel" name="label_5">
182 <property name="font"> 210 <property name="font">
183 <font> 211 <font>
184 <italic>true</italic> 212 <italic>true</italic>
@@ -200,7 +228,7 @@
200 <property name="title"> 228 <property name="title">
201 <string>Advanced</string> 229 <string>Advanced</string>
202 </property> 230 </property>
203 <layout class="QVBoxLayout" name="verticalLayout_7"> 231 <layout class="QVBoxLayout" name="verticalLayout_8">
204 <item> 232 <item>
205 <widget class="QCheckBox" name="quest_flag"> 233 <widget class="QCheckBox" name="quest_flag">
206 <property name="text"> 234 <property name="text">
diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp
index 0097c9a29..a878ef9c6 100644
--- a/src/yuzu/configuration/configure_debug_controller.cpp
+++ b/src/yuzu/configuration/configure_debug_controller.cpp
@@ -4,11 +4,14 @@
4 4
5#include "ui_configure_debug_controller.h" 5#include "ui_configure_debug_controller.h"
6#include "yuzu/configuration/configure_debug_controller.h" 6#include "yuzu/configuration/configure_debug_controller.h"
7#include "yuzu/configuration/configure_input_player.h"
7 8
8ConfigureDebugController::ConfigureDebugController(QWidget* parent, 9ConfigureDebugController::ConfigureDebugController(QWidget* parent,
9 InputCommon::InputSubsystem* input_subsystem) 10 InputCommon::InputSubsystem* input_subsystem,
11 InputProfiles* profiles)
10 : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()), 12 : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()),
11 debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, true)) { 13 debug_controller(
14 new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, true)) {
12 ui->setupUi(this); 15 ui->setupUi(this);
13 16
14 ui->controllerLayout->addWidget(debug_controller); 17 ui->controllerLayout->addWidget(debug_controller);
diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h
index 34dcf705f..b4f53fad5 100644
--- a/src/yuzu/configuration/configure_debug_controller.h
+++ b/src/yuzu/configuration/configure_debug_controller.h
@@ -6,10 +6,13 @@
6 6
7#include <memory> 7#include <memory>
8#include <QDialog> 8#include <QDialog>
9#include "yuzu/configuration/configure_input_player.h"
10 9
11class QPushButton; 10class QPushButton;
12 11
12class ConfigureInputPlayer;
13
14class InputProfiles;
15
13namespace InputCommon { 16namespace InputCommon {
14class InputSubsystem; 17class InputSubsystem;
15} 18}
@@ -22,8 +25,8 @@ class ConfigureDebugController : public QDialog {
22 Q_OBJECT 25 Q_OBJECT
23 26
24public: 27public:
25 explicit ConfigureDebugController(QWidget* parent, 28 explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem,
26 InputCommon::InputSubsystem* input_subsystem); 29 InputProfiles* profiles);
27 ~ConfigureDebugController() override; 30 ~ConfigureDebugController() override;
28 31
29 void ApplyConfiguration(); 32 void ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_debug_controller.ui b/src/yuzu/configuration/configure_debug_controller.ui
index a95ed50ff..7b7e6582c 100644
--- a/src/yuzu/configuration/configure_debug_controller.ui
+++ b/src/yuzu/configuration/configure_debug_controller.ui
@@ -66,32 +66,12 @@
66 <signal>accepted()</signal> 66 <signal>accepted()</signal>
67 <receiver>ConfigureDebugController</receiver> 67 <receiver>ConfigureDebugController</receiver>
68 <slot>accept()</slot> 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> 69 </connection>
80 <connection> 70 <connection>
81 <sender>buttonBox</sender> 71 <sender>buttonBox</sender>
82 <signal>rejected()</signal> 72 <signal>rejected()</signal>
83 <receiver>ConfigureDebugController</receiver> 73 <receiver>ConfigureDebugController</receiver>
84 <slot>reject()</slot> 74 <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> 75 </connection>
96 </connections> 76 </connections>
97</ui> 77</ui>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 2725fcb2b..d9009091b 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -23,6 +23,8 @@
23#include "yuzu/configuration/configure_motion_touch.h" 23#include "yuzu/configuration/configure_motion_touch.h"
24#include "yuzu/configuration/configure_mouse_advanced.h" 24#include "yuzu/configuration/configure_mouse_advanced.h"
25#include "yuzu/configuration/configure_touchscreen_advanced.h" 25#include "yuzu/configuration/configure_touchscreen_advanced.h"
26#include "yuzu/configuration/configure_vibration.h"
27#include "yuzu/configuration/input_profiles.h"
26 28
27namespace { 29namespace {
28template <typename Dialog, typename... Args> 30template <typename Dialog, typename... Args>
@@ -64,7 +66,8 @@ void OnDockedModeChanged(bool last_state, bool new_state) {
64} 66}
65 67
66ConfigureInput::ConfigureInput(QWidget* parent) 68ConfigureInput::ConfigureInput(QWidget* parent)
67 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) { 69 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
70 profiles(std::make_unique<InputProfiles>()) {
68 ui->setupUi(this); 71 ui->setupUi(this);
69} 72}
70 73
@@ -73,14 +76,22 @@ ConfigureInput::~ConfigureInput() = default;
73void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, 76void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
74 std::size_t max_players) { 77 std::size_t max_players) {
75 player_controllers = { 78 player_controllers = {
76 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem), 79 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem,
77 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem), 80 profiles.get()),
78 new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem), 81 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem,
79 new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem), 82 profiles.get()),
80 new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem), 83 new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem,
81 new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem), 84 profiles.get()),
82 new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem), 85 new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem,
83 new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem), 86 profiles.get()),
87 new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem,
88 profiles.get()),
89 new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem,
90 profiles.get()),
91 new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem,
92 profiles.get()),
93 new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem,
94 profiles.get()),
84 }; 95 };
85 96
86 player_tabs = { 97 player_tabs = {
@@ -113,8 +124,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
113 } 124 }
114 } 125 }
115 }); 126 });
116 connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, 127 connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this,
117 [this] { UpdateAllInputDevices(); }); 128 &ConfigureInput::UpdateAllInputDevices);
129 connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputProfiles, this,
130 &ConfigureInput::UpdateAllInputProfiles, Qt::QueuedConnection);
118 connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) { 131 connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) {
119 player_controllers[i]->ConnectPlayer(state == Qt::Checked); 132 player_controllers[i]->ConnectPlayer(state == Qt::Checked);
120 }); 133 });
@@ -134,7 +147,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
134 ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); 147 ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
135 ui->tabAdvanced->layout()->addWidget(advanced); 148 ui->tabAdvanced->layout()->addWidget(advanced);
136 connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] { 149 connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] {
137 CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem); 150 CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem, profiles.get());
138 }); 151 });
139 connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] { 152 connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] {
140 CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem); 153 CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem);
@@ -146,6 +159,9 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
146 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); 159 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
147 }); 160 });
148 161
162 connect(ui->vibrationButton, &QPushButton::clicked,
163 [this] { CallConfigureDialog<ConfigureVibration>(*this); });
164
149 connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] { 165 connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] {
150 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); 166 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
151 }); 167 });
@@ -171,12 +187,12 @@ void ConfigureInput::ApplyConfiguration() {
171 187
172 advanced->ApplyConfiguration(); 188 advanced->ApplyConfiguration();
173 189
174 const bool pre_docked_mode = Settings::values.use_docked_mode; 190 const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
175 Settings::values.use_docked_mode = ui->radioDocked->isChecked(); 191 Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
176 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode); 192 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue());
177 193
178 Settings::values.vibration_enabled = ui->vibrationGroup->isChecked(); 194 Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
179 Settings::values.motion_enabled = ui->motionGroup->isChecked(); 195 Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
180} 196}
181 197
182void ConfigureInput::changeEvent(QEvent* event) { 198void ConfigureInput::changeEvent(QEvent* event) {
@@ -193,16 +209,16 @@ void ConfigureInput::RetranslateUI() {
193 209
194void ConfigureInput::LoadConfiguration() { 210void ConfigureInput::LoadConfiguration() {
195 LoadPlayerControllerIndices(); 211 LoadPlayerControllerIndices();
196 UpdateDockedState(Settings::values.players[8].connected); 212 UpdateDockedState(Settings::values.players.GetValue()[8].connected);
197 213
198 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled); 214 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
199 ui->motionGroup->setChecked(Settings::values.motion_enabled); 215 ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
200} 216}
201 217
202void ConfigureInput::LoadPlayerControllerIndices() { 218void ConfigureInput::LoadPlayerControllerIndices() {
203 for (std::size_t i = 0; i < player_connected.size(); ++i) { 219 for (std::size_t i = 0; i < player_connected.size(); ++i) {
204 const auto connected = Settings::values.players[i].connected || 220 const auto connected = Settings::values.players.GetValue()[i].connected ||
205 (i == 0 && Settings::values.players[8].connected); 221 (i == 0 && Settings::values.players.GetValue()[8].connected);
206 player_connected[i]->setChecked(connected); 222 player_connected[i]->setChecked(connected);
207 } 223 }
208} 224}
@@ -231,8 +247,8 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) {
231 ui->radioDocked->setEnabled(!is_handheld); 247 ui->radioDocked->setEnabled(!is_handheld);
232 ui->radioUndocked->setEnabled(!is_handheld); 248 ui->radioUndocked->setEnabled(!is_handheld);
233 249
234 ui->radioDocked->setChecked(Settings::values.use_docked_mode); 250 ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue());
235 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode); 251 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue());
236 252
237 // Also force into undocked mode if the controller type is handheld. 253 // Also force into undocked mode if the controller type is handheld.
238 if (is_handheld) { 254 if (is_handheld) {
@@ -242,6 +258,16 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) {
242 258
243void ConfigureInput::UpdateAllInputDevices() { 259void ConfigureInput::UpdateAllInputDevices() {
244 for (const auto& player : player_controllers) { 260 for (const auto& player : player_controllers) {
245 player->UpdateInputDevices(); 261 player->UpdateInputDeviceCombobox();
262 }
263}
264
265void ConfigureInput::UpdateAllInputProfiles(std::size_t player_index) {
266 for (std::size_t i = 0; i < player_controllers.size(); ++i) {
267 if (i == player_index) {
268 continue;
269 }
270
271 player_controllers[i]->UpdateInputProfiles();
246 } 272 }
247} 273}
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 0e8b2fd4e..f4eb0d78b 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -8,17 +8,18 @@
8#include <memory> 8#include <memory>
9 9
10#include <QKeyEvent> 10#include <QKeyEvent>
11#include <QList>
11#include <QWidget> 12#include <QWidget>
12 13
13#include "yuzu/configuration/configure_input_advanced.h"
14#include "yuzu/configuration/configure_input_player.h"
15
16#include "ui_configure_input.h"
17
18class QCheckBox; 14class QCheckBox;
19class QString; 15class QString;
20class QTimer; 16class QTimer;
21 17
18class ConfigureInputAdvanced;
19class ConfigureInputPlayer;
20
21class InputProfiles;
22
22namespace InputCommon { 23namespace InputCommon {
23class InputSubsystem; 24class InputSubsystem;
24} 25}
@@ -51,6 +52,7 @@ private:
51 52
52 void UpdateDockedState(bool is_handheld); 53 void UpdateDockedState(bool is_handheld);
53 void UpdateAllInputDevices(); 54 void UpdateAllInputDevices();
55 void UpdateAllInputProfiles(std::size_t player_index);
54 56
55 /// Load configuration settings. 57 /// Load configuration settings.
56 void LoadConfiguration(); 58 void LoadConfiguration();
@@ -61,6 +63,8 @@ private:
61 63
62 std::unique_ptr<Ui::ConfigureInput> ui; 64 std::unique_ptr<Ui::ConfigureInput> ui;
63 65
66 std::unique_ptr<InputProfiles> profiles;
67
64 std::array<ConfigureInputPlayer*, 8> player_controllers; 68 std::array<ConfigureInputPlayer*, 8> player_controllers;
65 std::array<QWidget*, 8> player_tabs; 69 std::array<QWidget*, 8> player_tabs;
66 std::array<QCheckBox*, 8> player_connected; 70 std::array<QCheckBox*, 8> player_connected;
diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui
index 136955224..2707025e7 100644
--- a/src/yuzu/configuration/configure_input.ui
+++ b/src/yuzu/configuration/configure_input.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>700</width> 9 <width>680</width>
10 <height>540</height> 10 <height>540</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
@@ -142,7 +142,7 @@
142 <number>6</number> 142 <number>6</number>
143 </property> 143 </property>
144 <property name="leftMargin"> 144 <property name="leftMargin">
145 <number>3</number> 145 <number>8</number>
146 </property> 146 </property>
147 <property name="topMargin"> 147 <property name="topMargin">
148 <number>6</number> 148 <number>6</number>
@@ -195,30 +195,24 @@
195 <number>3</number> 195 <number>3</number>
196 </property> 196 </property>
197 <item> 197 <item>
198 <widget class="QSpinBox" name="vibrationSpin"> 198 <widget class="QPushButton" name="vibrationButton">
199 <property name="minimumSize"> 199 <property name="minimumSize">
200 <size> 200 <size>
201 <width>65</width> 201 <width>68</width>
202 <height>21</height> 202 <height>0</height>
203 </size> 203 </size>
204 </property> 204 </property>
205 <property name="maximumSize"> 205 <property name="maximumSize">
206 <size> 206 <size>
207 <width>65</width> 207 <width>68</width>
208 <height>16777215</height> 208 <height>16777215</height>
209 </size> 209 </size>
210 </property> 210 </property>
211 <property name="suffix"> 211 <property name="styleSheet">
212 <string>%</string> 212 <string notr="true">min-width: 68px;</string>
213 </property>
214 <property name="minimum">
215 <number>1</number>
216 </property>
217 <property name="maximum">
218 <number>200</number>
219 </property> 213 </property>
220 <property name="value"> 214 <property name="text">
221 <number>100</number> 215 <string>Configure</string>
222 </property> 216 </property>
223 </widget> 217 </widget>
224 </item> 218 </item>
@@ -250,18 +244,18 @@
250 <widget class="QPushButton" name="motionButton"> 244 <widget class="QPushButton" name="motionButton">
251 <property name="minimumSize"> 245 <property name="minimumSize">
252 <size> 246 <size>
253 <width>57</width> 247 <width>68</width>
254 <height>0</height> 248 <height>0</height>
255 </size> 249 </size>
256 </property> 250 </property>
257 <property name="maximumSize"> 251 <property name="maximumSize">
258 <size> 252 <size>
259 <width>55</width> 253 <width>68</width>
260 <height>16777215</height> 254 <height>16777215</height>
261 </size> 255 </size>
262 </property> 256 </property>
263 <property name="styleSheet"> 257 <property name="styleSheet">
264 <string notr="true">min-width: 55px;</string> 258 <string notr="true">min-width: 68px;</string>
265 </property> 259 </property>
266 <property name="text"> 260 <property name="text">
267 <string>Configure</string> 261 <string>Configure</string>
@@ -272,7 +266,7 @@
272 </widget> 266 </widget>
273 </item> 267 </item>
274 <item alignment="Qt::AlignVCenter"> 268 <item alignment="Qt::AlignVCenter">
275 <widget class="QWidget" name="widget" native="true"> 269 <widget class="QWidget" name="connectedControllers" native="true">
276 <layout class="QGridLayout" name="gridLayout_2"> 270 <layout class="QGridLayout" name="gridLayout_2">
277 <property name="leftMargin"> 271 <property name="leftMargin">
278 <number>5</number> 272 <number>5</number>
@@ -468,13 +462,13 @@
468 </property> 462 </property>
469 <property name="minimumSize"> 463 <property name="minimumSize">
470 <size> 464 <size>
471 <width>57</width> 465 <width>68</width>
472 <height>0</height> 466 <height>0</height>
473 </size> 467 </size>
474 </property> 468 </property>
475 <property name="maximumSize"> 469 <property name="maximumSize">
476 <size> 470 <size>
477 <width>55</width> 471 <width>68</width>
478 <height>16777215</height> 472 <height>16777215</height>
479 </size> 473 </size>
480 </property> 474 </property>
@@ -494,7 +488,7 @@
494 <enum>Qt::LeftToRight</enum> 488 <enum>Qt::LeftToRight</enum>
495 </property> 489 </property>
496 <property name="styleSheet"> 490 <property name="styleSheet">
497 <string notr="true">min-width: 55px;</string> 491 <string notr="true">min-width: 68px;</string>
498 </property> 492 </property>
499 <property name="text"> 493 <property name="text">
500 <string>Defaults</string> 494 <string>Defaults</string>
@@ -511,13 +505,13 @@
511 </property> 505 </property>
512 <property name="minimumSize"> 506 <property name="minimumSize">
513 <size> 507 <size>
514 <width>57</width> 508 <width>68</width>
515 <height>0</height> 509 <height>0</height>
516 </size> 510 </size>
517 </property> 511 </property>
518 <property name="maximumSize"> 512 <property name="maximumSize">
519 <size> 513 <size>
520 <width>55</width> 514 <width>68</width>
521 <height>16777215</height> 515 <height>16777215</height>
522 </size> 516 </size>
523 </property> 517 </property>
@@ -537,7 +531,7 @@
537 <enum>Qt::LeftToRight</enum> 531 <enum>Qt::LeftToRight</enum>
538 </property> 532 </property>
539 <property name="styleSheet"> 533 <property name="styleSheet">
540 <string notr="true">min-width: 55px;</string> 534 <string notr="true">min-width: 68px;</string>
541 </property> 535 </property>
542 <property name="text"> 536 <property name="text">
543 <string>Clear</string> 537 <string>Clear</string>
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index 81f9dc16c..abaf03630 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -68,8 +68,7 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
68 for (std::size_t button_idx = 0; button_idx < color_buttons.size(); ++button_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, 69 connect(color_buttons[button_idx], &QPushButton::clicked, this,
70 [this, player_idx, button_idx] { 70 [this, player_idx, button_idx] {
71 OnControllerButtonClick(static_cast<int>(player_idx), 71 OnControllerButtonClick(player_idx, button_idx);
72 static_cast<int>(button_idx));
73 }); 72 });
74 } 73 }
75 } 74 }
@@ -94,20 +93,21 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
94 93
95ConfigureInputAdvanced::~ConfigureInputAdvanced() = default; 94ConfigureInputAdvanced::~ConfigureInputAdvanced() = default;
96 95
97void ConfigureInputAdvanced::OnControllerButtonClick(int player_idx, int button_idx) { 96void ConfigureInputAdvanced::OnControllerButtonClick(std::size_t player_idx,
97 std::size_t button_idx) {
98 const QColor new_bg_color = QColorDialog::getColor(controllers_colors[player_idx][button_idx]); 98 const QColor new_bg_color = QColorDialog::getColor(controllers_colors[player_idx][button_idx]);
99 if (!new_bg_color.isValid()) { 99 if (!new_bg_color.isValid()) {
100 return; 100 return;
101 } 101 }
102 controllers_colors[player_idx][button_idx] = new_bg_color; 102 controllers_colors[player_idx][button_idx] = new_bg_color;
103 controllers_color_buttons[player_idx][button_idx]->setStyleSheet( 103 controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
104 QStringLiteral("background-color: %1; min-width: 55px;") 104 QStringLiteral("background-color: %1; min-width: 60px;")
105 .arg(controllers_colors[player_idx][button_idx].name())); 105 .arg(controllers_colors[player_idx][button_idx].name()));
106} 106}
107 107
108void ConfigureInputAdvanced::ApplyConfiguration() { 108void ConfigureInputAdvanced::ApplyConfiguration() {
109 for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) { 109 for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
110 auto& player = Settings::values.players[player_idx]; 110 auto& player = Settings::values.players.GetValue()[player_idx];
111 std::array<u32, 4> colors{}; 111 std::array<u32, 4> colors{};
112 std::transform(controllers_colors[player_idx].begin(), controllers_colors[player_idx].end(), 112 std::transform(controllers_colors[player_idx].begin(), controllers_colors[player_idx].end(),
113 colors.begin(), [](QColor color) { return color.rgb(); }); 113 colors.begin(), [](QColor color) { return color.rgb(); });
@@ -126,7 +126,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
126 126
127void ConfigureInputAdvanced::LoadConfiguration() { 127void ConfigureInputAdvanced::LoadConfiguration() {
128 for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) { 128 for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
129 auto& player = Settings::values.players[player_idx]; 129 auto& player = Settings::values.players.GetValue()[player_idx];
130 std::array<u32, 4> colors = { 130 std::array<u32, 4> colors = {
131 player.body_color_left, 131 player.body_color_left,
132 player.button_color_left, 132 player.button_color_left,
@@ -139,7 +139,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
139 139
140 for (std::size_t button_idx = 0; button_idx < colors.size(); ++button_idx) { 140 for (std::size_t button_idx = 0; button_idx < colors.size(); ++button_idx) {
141 controllers_color_buttons[player_idx][button_idx]->setStyleSheet( 141 controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
142 QStringLiteral("background-color: %1; min-width: 55px;") 142 QStringLiteral("background-color: %1; min-width: 60px;")
143 .arg(controllers_colors[player_idx][button_idx].name())); 143 .arg(controllers_colors[player_idx][button_idx].name()));
144 } 144 }
145 } 145 }
diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h
index 50bb87768..3083d55c1 100644
--- a/src/yuzu/configuration/configure_input_advanced.h
+++ b/src/yuzu/configuration/configure_input_advanced.h
@@ -35,7 +35,7 @@ private:
35 void RetranslateUI(); 35 void RetranslateUI();
36 void UpdateUIEnabled(); 36 void UpdateUIEnabled();
37 37
38 void OnControllerButtonClick(int player_idx, int button_idx); 38 void OnControllerButtonClick(std::size_t player_idx, std::size_t button_idx);
39 39
40 void LoadConfiguration(); 40 void LoadConfiguration();
41 41
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index 5958435fc..a880a7c68 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -192,18 +192,18 @@
192 </property> 192 </property>
193 <property name="minimumSize"> 193 <property name="minimumSize">
194 <size> 194 <size>
195 <width>57</width> 195 <width>68</width>
196 <height>0</height> 196 <height>0</height>
197 </size> 197 </size>
198 </property> 198 </property>
199 <property name="maximumSize"> 199 <property name="maximumSize">
200 <size> 200 <size>
201 <width>55</width> 201 <width>68</width>
202 <height>16777215</height> 202 <height>16777215</height>
203 </size> 203 </size>
204 </property> 204 </property>
205 <property name="styleSheet"> 205 <property name="styleSheet">
206 <string notr="true">min-width: 55px;</string> 206 <string notr="true">min-width: 68px;</string>
207 </property> 207 </property>
208 <property name="text"> 208 <property name="text">
209 <string/> 209 <string/>
@@ -247,18 +247,18 @@
247 </property> 247 </property>
248 <property name="minimumSize"> 248 <property name="minimumSize">
249 <size> 249 <size>
250 <width>57</width> 250 <width>68</width>
251 <height>0</height> 251 <height>0</height>
252 </size> 252 </size>
253 </property> 253 </property>
254 <property name="maximumSize"> 254 <property name="maximumSize">
255 <size> 255 <size>
256 <width>55</width> 256 <width>68</width>
257 <height>16777215</height> 257 <height>16777215</height>
258 </size> 258 </size>
259 </property> 259 </property>
260 <property name="styleSheet"> 260 <property name="styleSheet">
261 <string notr="true">min-width: 55px;</string> 261 <string notr="true">min-width: 68px;</string>
262 </property> 262 </property>
263 <property name="text"> 263 <property name="text">
264 <string/> 264 <string/>
@@ -323,18 +323,18 @@
323 </property> 323 </property>
324 <property name="minimumSize"> 324 <property name="minimumSize">
325 <size> 325 <size>
326 <width>57</width> 326 <width>68</width>
327 <height>0</height> 327 <height>0</height>
328 </size> 328 </size>
329 </property> 329 </property>
330 <property name="maximumSize"> 330 <property name="maximumSize">
331 <size> 331 <size>
332 <width>55</width> 332 <width>68</width>
333 <height>16777215</height> 333 <height>16777215</height>
334 </size> 334 </size>
335 </property> 335 </property>
336 <property name="styleSheet"> 336 <property name="styleSheet">
337 <string notr="true">min-width: 55px;</string> 337 <string notr="true">min-width: 68px;</string>
338 </property> 338 </property>
339 <property name="text"> 339 <property name="text">
340 <string/> 340 <string/>
@@ -378,18 +378,18 @@
378 </property> 378 </property>
379 <property name="minimumSize"> 379 <property name="minimumSize">
380 <size> 380 <size>
381 <width>57</width> 381 <width>68</width>
382 <height>0</height> 382 <height>0</height>
383 </size> 383 </size>
384 </property> 384 </property>
385 <property name="maximumSize"> 385 <property name="maximumSize">
386 <size> 386 <size>
387 <width>55</width> 387 <width>68</width>
388 <height>16777215</height> 388 <height>16777215</height>
389 </size> 389 </size>
390 </property> 390 </property>
391 <property name="styleSheet"> 391 <property name="styleSheet">
392 <string notr="true">min-width: 55px;</string> 392 <string notr="true">min-width: 68px;</string>
393 </property> 393 </property>
394 <property name="text"> 394 <property name="text">
395 <string/> 395 <string/>
@@ -478,18 +478,18 @@
478 </property> 478 </property>
479 <property name="minimumSize"> 479 <property name="minimumSize">
480 <size> 480 <size>
481 <width>57</width> 481 <width>68</width>
482 <height>0</height> 482 <height>0</height>
483 </size> 483 </size>
484 </property> 484 </property>
485 <property name="maximumSize"> 485 <property name="maximumSize">
486 <size> 486 <size>
487 <width>55</width> 487 <width>68</width>
488 <height>16777215</height> 488 <height>16777215</height>
489 </size> 489 </size>
490 </property> 490 </property>
491 <property name="styleSheet"> 491 <property name="styleSheet">
492 <string notr="true">min-width: 55px;</string> 492 <string notr="true">min-width: 68px;</string>
493 </property> 493 </property>
494 <property name="text"> 494 <property name="text">
495 <string/> 495 <string/>
@@ -533,18 +533,18 @@
533 </property> 533 </property>
534 <property name="minimumSize"> 534 <property name="minimumSize">
535 <size> 535 <size>
536 <width>57</width> 536 <width>68</width>
537 <height>0</height> 537 <height>0</height>
538 </size> 538 </size>
539 </property> 539 </property>
540 <property name="maximumSize"> 540 <property name="maximumSize">
541 <size> 541 <size>
542 <width>55</width> 542 <width>68</width>
543 <height>16777215</height> 543 <height>16777215</height>
544 </size> 544 </size>
545 </property> 545 </property>
546 <property name="styleSheet"> 546 <property name="styleSheet">
547 <string notr="true">min-width: 55px;</string> 547 <string notr="true">min-width: 68px;</string>
548 </property> 548 </property>
549 <property name="text"> 549 <property name="text">
550 <string/> 550 <string/>
@@ -609,18 +609,18 @@
609 </property> 609 </property>
610 <property name="minimumSize"> 610 <property name="minimumSize">
611 <size> 611 <size>
612 <width>57</width> 612 <width>68</width>
613 <height>0</height> 613 <height>0</height>
614 </size> 614 </size>
615 </property> 615 </property>
616 <property name="maximumSize"> 616 <property name="maximumSize">
617 <size> 617 <size>
618 <width>55</width> 618 <width>68</width>
619 <height>16777215</height> 619 <height>16777215</height>
620 </size> 620 </size>
621 </property> 621 </property>
622 <property name="styleSheet"> 622 <property name="styleSheet">
623 <string notr="true">min-width: 55px;</string> 623 <string notr="true">min-width: 68px;</string>
624 </property> 624 </property>
625 <property name="text"> 625 <property name="text">
626 <string/> 626 <string/>
@@ -664,18 +664,18 @@
664 </property> 664 </property>
665 <property name="minimumSize"> 665 <property name="minimumSize">
666 <size> 666 <size>
667 <width>57</width> 667 <width>68</width>
668 <height>0</height> 668 <height>0</height>
669 </size> 669 </size>
670 </property> 670 </property>
671 <property name="maximumSize"> 671 <property name="maximumSize">
672 <size> 672 <size>
673 <width>55</width> 673 <width>68</width>
674 <height>16777215</height> 674 <height>16777215</height>
675 </size> 675 </size>
676 </property> 676 </property>
677 <property name="styleSheet"> 677 <property name="styleSheet">
678 <string notr="true">min-width: 55px;</string> 678 <string notr="true">min-width: 68px;</string>
679 </property> 679 </property>
680 <property name="text"> 680 <property name="text">
681 <string/> 681 <string/>
@@ -782,18 +782,18 @@
782 </property> 782 </property>
783 <property name="minimumSize"> 783 <property name="minimumSize">
784 <size> 784 <size>
785 <width>57</width> 785 <width>68</width>
786 <height>0</height> 786 <height>0</height>
787 </size> 787 </size>
788 </property> 788 </property>
789 <property name="maximumSize"> 789 <property name="maximumSize">
790 <size> 790 <size>
791 <width>55</width> 791 <width>68</width>
792 <height>16777215</height> 792 <height>16777215</height>
793 </size> 793 </size>
794 </property> 794 </property>
795 <property name="styleSheet"> 795 <property name="styleSheet">
796 <string notr="true">min-width: 55px;</string> 796 <string notr="true">min-width: 68px;</string>
797 </property> 797 </property>
798 <property name="text"> 798 <property name="text">
799 <string/> 799 <string/>
@@ -837,18 +837,18 @@
837 </property> 837 </property>
838 <property name="minimumSize"> 838 <property name="minimumSize">
839 <size> 839 <size>
840 <width>57</width> 840 <width>68</width>
841 <height>0</height> 841 <height>0</height>
842 </size> 842 </size>
843 </property> 843 </property>
844 <property name="maximumSize"> 844 <property name="maximumSize">
845 <size> 845 <size>
846 <width>55</width> 846 <width>68</width>
847 <height>16777215</height> 847 <height>16777215</height>
848 </size> 848 </size>
849 </property> 849 </property>
850 <property name="styleSheet"> 850 <property name="styleSheet">
851 <string notr="true">min-width: 55px;</string> 851 <string notr="true">min-width: 68px;</string>
852 </property> 852 </property>
853 <property name="text"> 853 <property name="text">
854 <string/> 854 <string/>
@@ -913,18 +913,18 @@
913 </property> 913 </property>
914 <property name="minimumSize"> 914 <property name="minimumSize">
915 <size> 915 <size>
916 <width>57</width> 916 <width>68</width>
917 <height>0</height> 917 <height>0</height>
918 </size> 918 </size>
919 </property> 919 </property>
920 <property name="maximumSize"> 920 <property name="maximumSize">
921 <size> 921 <size>
922 <width>55</width> 922 <width>68</width>
923 <height>16777215</height> 923 <height>16777215</height>
924 </size> 924 </size>
925 </property> 925 </property>
926 <property name="styleSheet"> 926 <property name="styleSheet">
927 <string notr="true">min-width: 55px;</string> 927 <string notr="true">min-width: 68px;</string>
928 </property> 928 </property>
929 <property name="text"> 929 <property name="text">
930 <string/> 930 <string/>
@@ -968,18 +968,18 @@
968 </property> 968 </property>
969 <property name="minimumSize"> 969 <property name="minimumSize">
970 <size> 970 <size>
971 <width>57</width> 971 <width>68</width>
972 <height>0</height> 972 <height>0</height>
973 </size> 973 </size>
974 </property> 974 </property>
975 <property name="maximumSize"> 975 <property name="maximumSize">
976 <size> 976 <size>
977 <width>55</width> 977 <width>68</width>
978 <height>16777215</height> 978 <height>16777215</height>
979 </size> 979 </size>
980 </property> 980 </property>
981 <property name="styleSheet"> 981 <property name="styleSheet">
982 <string notr="true">min-width: 55px;</string> 982 <string notr="true">min-width: 68px;</string>
983 </property> 983 </property>
984 <property name="text"> 984 <property name="text">
985 <string/> 985 <string/>
@@ -1068,18 +1068,18 @@
1068 </property> 1068 </property>
1069 <property name="minimumSize"> 1069 <property name="minimumSize">
1070 <size> 1070 <size>
1071 <width>57</width> 1071 <width>68</width>
1072 <height>0</height> 1072 <height>0</height>
1073 </size> 1073 </size>
1074 </property> 1074 </property>
1075 <property name="maximumSize"> 1075 <property name="maximumSize">
1076 <size> 1076 <size>
1077 <width>55</width> 1077 <width>68</width>
1078 <height>16777215</height> 1078 <height>16777215</height>
1079 </size> 1079 </size>
1080 </property> 1080 </property>
1081 <property name="styleSheet"> 1081 <property name="styleSheet">
1082 <string notr="true">min-width: 55px;</string> 1082 <string notr="true">min-width: 68px;</string>
1083 </property> 1083 </property>
1084 <property name="text"> 1084 <property name="text">
1085 <string/> 1085 <string/>
@@ -1123,18 +1123,18 @@
1123 </property> 1123 </property>
1124 <property name="minimumSize"> 1124 <property name="minimumSize">
1125 <size> 1125 <size>
1126 <width>57</width> 1126 <width>68</width>
1127 <height>0</height> 1127 <height>0</height>
1128 </size> 1128 </size>
1129 </property> 1129 </property>
1130 <property name="maximumSize"> 1130 <property name="maximumSize">
1131 <size> 1131 <size>
1132 <width>55</width> 1132 <width>68</width>
1133 <height>16777215</height> 1133 <height>16777215</height>
1134 </size> 1134 </size>
1135 </property> 1135 </property>
1136 <property name="styleSheet"> 1136 <property name="styleSheet">
1137 <string notr="true">min-width: 55px;</string> 1137 <string notr="true">min-width: 68px;</string>
1138 </property> 1138 </property>
1139 <property name="text"> 1139 <property name="text">
1140 <string/> 1140 <string/>
@@ -1199,18 +1199,18 @@
1199 </property> 1199 </property>
1200 <property name="minimumSize"> 1200 <property name="minimumSize">
1201 <size> 1201 <size>
1202 <width>57</width> 1202 <width>68</width>
1203 <height>0</height> 1203 <height>0</height>
1204 </size> 1204 </size>
1205 </property> 1205 </property>
1206 <property name="maximumSize"> 1206 <property name="maximumSize">
1207 <size> 1207 <size>
1208 <width>55</width> 1208 <width>68</width>
1209 <height>16777215</height> 1209 <height>16777215</height>
1210 </size> 1210 </size>
1211 </property> 1211 </property>
1212 <property name="styleSheet"> 1212 <property name="styleSheet">
1213 <string notr="true">min-width: 55px;</string> 1213 <string notr="true">min-width: 68px;</string>
1214 </property> 1214 </property>
1215 <property name="text"> 1215 <property name="text">
1216 <string/> 1216 <string/>
@@ -1254,18 +1254,18 @@
1254 </property> 1254 </property>
1255 <property name="minimumSize"> 1255 <property name="minimumSize">
1256 <size> 1256 <size>
1257 <width>57</width> 1257 <width>68</width>
1258 <height>0</height> 1258 <height>0</height>
1259 </size> 1259 </size>
1260 </property> 1260 </property>
1261 <property name="maximumSize"> 1261 <property name="maximumSize">
1262 <size> 1262 <size>
1263 <width>55</width> 1263 <width>68</width>
1264 <height>16777215</height> 1264 <height>16777215</height>
1265 </size> 1265 </size>
1266 </property> 1266 </property>
1267 <property name="styleSheet"> 1267 <property name="styleSheet">
1268 <string notr="true">min-width: 55px;</string> 1268 <string notr="true">min-width: 68px;</string>
1269 </property> 1269 </property>
1270 <property name="text"> 1270 <property name="text">
1271 <string/> 1271 <string/>
@@ -1393,18 +1393,18 @@
1393 </property> 1393 </property>
1394 <property name="minimumSize"> 1394 <property name="minimumSize">
1395 <size> 1395 <size>
1396 <width>57</width> 1396 <width>68</width>
1397 <height>0</height> 1397 <height>0</height>
1398 </size> 1398 </size>
1399 </property> 1399 </property>
1400 <property name="maximumSize"> 1400 <property name="maximumSize">
1401 <size> 1401 <size>
1402 <width>55</width> 1402 <width>68</width>
1403 <height>16777215</height> 1403 <height>16777215</height>
1404 </size> 1404 </size>
1405 </property> 1405 </property>
1406 <property name="styleSheet"> 1406 <property name="styleSheet">
1407 <string notr="true">min-width: 55px;</string> 1407 <string notr="true">min-width: 68px;</string>
1408 </property> 1408 </property>
1409 <property name="text"> 1409 <property name="text">
1410 <string/> 1410 <string/>
@@ -1448,18 +1448,18 @@
1448 </property> 1448 </property>
1449 <property name="minimumSize"> 1449 <property name="minimumSize">
1450 <size> 1450 <size>
1451 <width>57</width> 1451 <width>68</width>
1452 <height>0</height> 1452 <height>0</height>
1453 </size> 1453 </size>
1454 </property> 1454 </property>
1455 <property name="maximumSize"> 1455 <property name="maximumSize">
1456 <size> 1456 <size>
1457 <width>55</width> 1457 <width>68</width>
1458 <height>16777215</height> 1458 <height>16777215</height>
1459 </size> 1459 </size>
1460 </property> 1460 </property>
1461 <property name="styleSheet"> 1461 <property name="styleSheet">
1462 <string notr="true">min-width: 55px;</string> 1462 <string notr="true">min-width: 68px;</string>
1463 </property> 1463 </property>
1464 <property name="text"> 1464 <property name="text">
1465 <string/> 1465 <string/>
@@ -1524,18 +1524,18 @@
1524 </property> 1524 </property>
1525 <property name="minimumSize"> 1525 <property name="minimumSize">
1526 <size> 1526 <size>
1527 <width>57</width> 1527 <width>68</width>
1528 <height>0</height> 1528 <height>0</height>
1529 </size> 1529 </size>
1530 </property> 1530 </property>
1531 <property name="maximumSize"> 1531 <property name="maximumSize">
1532 <size> 1532 <size>
1533 <width>55</width> 1533 <width>68</width>
1534 <height>16777215</height> 1534 <height>16777215</height>
1535 </size> 1535 </size>
1536 </property> 1536 </property>
1537 <property name="styleSheet"> 1537 <property name="styleSheet">
1538 <string notr="true">min-width: 55px;</string> 1538 <string notr="true">min-width: 68px;</string>
1539 </property> 1539 </property>
1540 <property name="text"> 1540 <property name="text">
1541 <string/> 1541 <string/>
@@ -1579,18 +1579,18 @@
1579 </property> 1579 </property>
1580 <property name="minimumSize"> 1580 <property name="minimumSize">
1581 <size> 1581 <size>
1582 <width>57</width> 1582 <width>68</width>
1583 <height>0</height> 1583 <height>0</height>
1584 </size> 1584 </size>
1585 </property> 1585 </property>
1586 <property name="maximumSize"> 1586 <property name="maximumSize">
1587 <size> 1587 <size>
1588 <width>55</width> 1588 <width>68</width>
1589 <height>16777215</height> 1589 <height>16777215</height>
1590 </size> 1590 </size>
1591 </property> 1591 </property>
1592 <property name="styleSheet"> 1592 <property name="styleSheet">
1593 <string notr="true">min-width: 55px;</string> 1593 <string notr="true">min-width: 68px;</string>
1594 </property> 1594 </property>
1595 <property name="text"> 1595 <property name="text">
1596 <string/> 1596 <string/>
@@ -1679,18 +1679,18 @@
1679 </property> 1679 </property>
1680 <property name="minimumSize"> 1680 <property name="minimumSize">
1681 <size> 1681 <size>
1682 <width>57</width> 1682 <width>68</width>
1683 <height>0</height> 1683 <height>0</height>
1684 </size> 1684 </size>
1685 </property> 1685 </property>
1686 <property name="maximumSize"> 1686 <property name="maximumSize">
1687 <size> 1687 <size>
1688 <width>55</width> 1688 <width>68</width>
1689 <height>16777215</height> 1689 <height>16777215</height>
1690 </size> 1690 </size>
1691 </property> 1691 </property>
1692 <property name="styleSheet"> 1692 <property name="styleSheet">
1693 <string notr="true">min-width: 55px;</string> 1693 <string notr="true">min-width: 68px;</string>
1694 </property> 1694 </property>
1695 <property name="text"> 1695 <property name="text">
1696 <string/> 1696 <string/>
@@ -1734,18 +1734,18 @@
1734 </property> 1734 </property>
1735 <property name="minimumSize"> 1735 <property name="minimumSize">
1736 <size> 1736 <size>
1737 <width>57</width> 1737 <width>68</width>
1738 <height>0</height> 1738 <height>0</height>
1739 </size> 1739 </size>
1740 </property> 1740 </property>
1741 <property name="maximumSize"> 1741 <property name="maximumSize">
1742 <size> 1742 <size>
1743 <width>55</width> 1743 <width>68</width>
1744 <height>16777215</height> 1744 <height>16777215</height>
1745 </size> 1745 </size>
1746 </property> 1746 </property>
1747 <property name="styleSheet"> 1747 <property name="styleSheet">
1748 <string notr="true">min-width: 55px;</string> 1748 <string notr="true">min-width: 68px;</string>
1749 </property> 1749 </property>
1750 <property name="text"> 1750 <property name="text">
1751 <string/> 1751 <string/>
@@ -1810,18 +1810,18 @@
1810 </property> 1810 </property>
1811 <property name="minimumSize"> 1811 <property name="minimumSize">
1812 <size> 1812 <size>
1813 <width>57</width> 1813 <width>68</width>
1814 <height>0</height> 1814 <height>0</height>
1815 </size> 1815 </size>
1816 </property> 1816 </property>
1817 <property name="maximumSize"> 1817 <property name="maximumSize">
1818 <size> 1818 <size>
1819 <width>55</width> 1819 <width>68</width>
1820 <height>16777215</height> 1820 <height>16777215</height>
1821 </size> 1821 </size>
1822 </property> 1822 </property>
1823 <property name="styleSheet"> 1823 <property name="styleSheet">
1824 <string notr="true">min-width: 55px;</string> 1824 <string notr="true">min-width: 68px;</string>
1825 </property> 1825 </property>
1826 <property name="text"> 1826 <property name="text">
1827 <string/> 1827 <string/>
@@ -1865,18 +1865,18 @@
1865 </property> 1865 </property>
1866 <property name="minimumSize"> 1866 <property name="minimumSize">
1867 <size> 1867 <size>
1868 <width>57</width> 1868 <width>68</width>
1869 <height>0</height> 1869 <height>0</height>
1870 </size> 1870 </size>
1871 </property> 1871 </property>
1872 <property name="maximumSize"> 1872 <property name="maximumSize">
1873 <size> 1873 <size>
1874 <width>55</width> 1874 <width>68</width>
1875 <height>16777215</height> 1875 <height>16777215</height>
1876 </size> 1876 </size>
1877 </property> 1877 </property>
1878 <property name="styleSheet"> 1878 <property name="styleSheet">
1879 <string notr="true">min-width: 55px;</string> 1879 <string notr="true">min-width: 68px;</string>
1880 </property> 1880 </property>
1881 <property name="text"> 1881 <property name="text">
1882 <string/> 1882 <string/>
@@ -1983,18 +1983,18 @@
1983 </property> 1983 </property>
1984 <property name="minimumSize"> 1984 <property name="minimumSize">
1985 <size> 1985 <size>
1986 <width>57</width> 1986 <width>68</width>
1987 <height>0</height> 1987 <height>0</height>
1988 </size> 1988 </size>
1989 </property> 1989 </property>
1990 <property name="maximumSize"> 1990 <property name="maximumSize">
1991 <size> 1991 <size>
1992 <width>55</width> 1992 <width>68</width>
1993 <height>16777215</height> 1993 <height>16777215</height>
1994 </size> 1994 </size>
1995 </property> 1995 </property>
1996 <property name="styleSheet"> 1996 <property name="styleSheet">
1997 <string notr="true">min-width: 55px;</string> 1997 <string notr="true">min-width: 68px;</string>
1998 </property> 1998 </property>
1999 <property name="text"> 1999 <property name="text">
2000 <string/> 2000 <string/>
@@ -2038,18 +2038,18 @@
2038 </property> 2038 </property>
2039 <property name="minimumSize"> 2039 <property name="minimumSize">
2040 <size> 2040 <size>
2041 <width>57</width> 2041 <width>68</width>
2042 <height>0</height> 2042 <height>0</height>
2043 </size> 2043 </size>
2044 </property> 2044 </property>
2045 <property name="maximumSize"> 2045 <property name="maximumSize">
2046 <size> 2046 <size>
2047 <width>55</width> 2047 <width>68</width>
2048 <height>16777215</height> 2048 <height>16777215</height>
2049 </size> 2049 </size>
2050 </property> 2050 </property>
2051 <property name="styleSheet"> 2051 <property name="styleSheet">
2052 <string notr="true">min-width: 55px;</string> 2052 <string notr="true">min-width: 68px;</string>
2053 </property> 2053 </property>
2054 <property name="text"> 2054 <property name="text">
2055 <string/> 2055 <string/>
@@ -2114,18 +2114,18 @@
2114 </property> 2114 </property>
2115 <property name="minimumSize"> 2115 <property name="minimumSize">
2116 <size> 2116 <size>
2117 <width>57</width> 2117 <width>68</width>
2118 <height>0</height> 2118 <height>0</height>
2119 </size> 2119 </size>
2120 </property> 2120 </property>
2121 <property name="maximumSize"> 2121 <property name="maximumSize">
2122 <size> 2122 <size>
2123 <width>55</width> 2123 <width>68</width>
2124 <height>16777215</height> 2124 <height>16777215</height>
2125 </size> 2125 </size>
2126 </property> 2126 </property>
2127 <property name="styleSheet"> 2127 <property name="styleSheet">
2128 <string notr="true">min-width: 55px;</string> 2128 <string notr="true">min-width: 68px;</string>
2129 </property> 2129 </property>
2130 <property name="text"> 2130 <property name="text">
2131 <string/> 2131 <string/>
@@ -2169,18 +2169,18 @@
2169 </property> 2169 </property>
2170 <property name="minimumSize"> 2170 <property name="minimumSize">
2171 <size> 2171 <size>
2172 <width>57</width> 2172 <width>68</width>
2173 <height>0</height> 2173 <height>0</height>
2174 </size> 2174 </size>
2175 </property> 2175 </property>
2176 <property name="maximumSize"> 2176 <property name="maximumSize">
2177 <size> 2177 <size>
2178 <width>55</width> 2178 <width>68</width>
2179 <height>16777215</height> 2179 <height>16777215</height>
2180 </size> 2180 </size>
2181 </property> 2181 </property>
2182 <property name="styleSheet"> 2182 <property name="styleSheet">
2183 <string notr="true">min-width: 55px;</string> 2183 <string notr="true">min-width: 68px;</string>
2184 </property> 2184 </property>
2185 <property name="text"> 2185 <property name="text">
2186 <string/> 2186 <string/>
@@ -2269,18 +2269,18 @@
2269 </property> 2269 </property>
2270 <property name="minimumSize"> 2270 <property name="minimumSize">
2271 <size> 2271 <size>
2272 <width>57</width> 2272 <width>68</width>
2273 <height>0</height> 2273 <height>0</height>
2274 </size> 2274 </size>
2275 </property> 2275 </property>
2276 <property name="maximumSize"> 2276 <property name="maximumSize">
2277 <size> 2277 <size>
2278 <width>55</width> 2278 <width>68</width>
2279 <height>16777215</height> 2279 <height>16777215</height>
2280 </size> 2280 </size>
2281 </property> 2281 </property>
2282 <property name="styleSheet"> 2282 <property name="styleSheet">
2283 <string notr="true">min-width: 55px;</string> 2283 <string notr="true">min-width: 68px;</string>
2284 </property> 2284 </property>
2285 <property name="text"> 2285 <property name="text">
2286 <string/> 2286 <string/>
@@ -2324,18 +2324,18 @@
2324 </property> 2324 </property>
2325 <property name="minimumSize"> 2325 <property name="minimumSize">
2326 <size> 2326 <size>
2327 <width>57</width> 2327 <width>68</width>
2328 <height>0</height> 2328 <height>0</height>
2329 </size> 2329 </size>
2330 </property> 2330 </property>
2331 <property name="maximumSize"> 2331 <property name="maximumSize">
2332 <size> 2332 <size>
2333 <width>55</width> 2333 <width>68</width>
2334 <height>16777215</height> 2334 <height>16777215</height>
2335 </size> 2335 </size>
2336 </property> 2336 </property>
2337 <property name="styleSheet"> 2337 <property name="styleSheet">
2338 <string notr="true">min-width: 55px;</string> 2338 <string notr="true">min-width: 68px;</string>
2339 </property> 2339 </property>
2340 <property name="text"> 2340 <property name="text">
2341 <string/> 2341 <string/>
@@ -2400,18 +2400,18 @@
2400 </property> 2400 </property>
2401 <property name="minimumSize"> 2401 <property name="minimumSize">
2402 <size> 2402 <size>
2403 <width>57</width> 2403 <width>68</width>
2404 <height>0</height> 2404 <height>0</height>
2405 </size> 2405 </size>
2406 </property> 2406 </property>
2407 <property name="maximumSize"> 2407 <property name="maximumSize">
2408 <size> 2408 <size>
2409 <width>55</width> 2409 <width>68</width>
2410 <height>16777215</height> 2410 <height>16777215</height>
2411 </size> 2411 </size>
2412 </property> 2412 </property>
2413 <property name="styleSheet"> 2413 <property name="styleSheet">
2414 <string notr="true">min-width: 55px;</string> 2414 <string notr="true">min-width: 68px;</string>
2415 </property> 2415 </property>
2416 <property name="text"> 2416 <property name="text">
2417 <string/> 2417 <string/>
@@ -2455,18 +2455,18 @@
2455 </property> 2455 </property>
2456 <property name="minimumSize"> 2456 <property name="minimumSize">
2457 <size> 2457 <size>
2458 <width>57</width> 2458 <width>68</width>
2459 <height>0</height> 2459 <height>0</height>
2460 </size> 2460 </size>
2461 </property> 2461 </property>
2462 <property name="maximumSize"> 2462 <property name="maximumSize">
2463 <size> 2463 <size>
2464 <width>55</width> 2464 <width>68</width>
2465 <height>16777215</height> 2465 <height>16777215</height>
2466 </size> 2466 </size>
2467 </property> 2467 </property>
2468 <property name="styleSheet"> 2468 <property name="styleSheet">
2469 <string notr="true">min-width: 55px;</string> 2469 <string notr="true">min-width: 68px;</string>
2470 </property> 2470 </property>
2471 <property name="text"> 2471 <property name="text">
2472 <string/> 2472 <string/>
diff --git a/src/yuzu/configuration/configure_input_dialog.cpp b/src/yuzu/configuration/configure_input_dialog.cpp
deleted file mode 100644
index 1866003c2..000000000
--- a/src/yuzu/configuration/configure_input_dialog.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
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_input_dialog.h"
6#include "yuzu/configuration/configure_input_dialog.h"
7
8ConfigureInputDialog::ConfigureInputDialog(QWidget* parent, std::size_t max_players,
9 InputCommon::InputSubsystem* input_subsystem)
10 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputDialog>()),
11 input_widget(new ConfigureInput(this)) {
12 ui->setupUi(this);
13
14 input_widget->Initialize(input_subsystem, max_players);
15
16 ui->inputLayout->addWidget(input_widget);
17
18 RetranslateUI();
19}
20
21ConfigureInputDialog::~ConfigureInputDialog() = default;
22
23void ConfigureInputDialog::ApplyConfiguration() {
24 input_widget->ApplyConfiguration();
25}
26
27void ConfigureInputDialog::changeEvent(QEvent* event) {
28 if (event->type() == QEvent::LanguageChange) {
29 RetranslateUI();
30 }
31
32 QDialog::changeEvent(event);
33}
34
35void ConfigureInputDialog::RetranslateUI() {
36 ui->retranslateUi(this);
37}
diff --git a/src/yuzu/configuration/configure_input_dialog.h b/src/yuzu/configuration/configure_input_dialog.h
deleted file mode 100644
index d1bd865f9..000000000
--- a/src/yuzu/configuration/configure_input_dialog.h
+++ /dev/null
@@ -1,38 +0,0 @@
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.h"
10
11class QPushButton;
12
13namespace InputCommon {
14class InputSubsystem;
15}
16
17namespace Ui {
18class ConfigureInputDialog;
19}
20
21class ConfigureInputDialog : public QDialog {
22 Q_OBJECT
23
24public:
25 explicit ConfigureInputDialog(QWidget* parent, std::size_t max_players,
26 InputCommon::InputSubsystem* input_subsystem);
27 ~ConfigureInputDialog() override;
28
29 void ApplyConfiguration();
30
31private:
32 void changeEvent(QEvent* event) override;
33 void RetranslateUI();
34
35 std::unique_ptr<Ui::ConfigureInputDialog> ui;
36
37 ConfigureInput* input_widget;
38};
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index f58ca29d7..56ab32a35 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <thread>
7#include <utility> 8#include <utility>
8#include <QGridLayout> 9#include <QGridLayout>
9#include <QInputDialog> 10#include <QInputDialog>
@@ -22,8 +23,9 @@
22#include "ui_configure_input_player.h" 23#include "ui_configure_input_player.h"
23#include "yuzu/configuration/config.h" 24#include "yuzu/configuration/config.h"
24#include "yuzu/configuration/configure_input_player.h" 25#include "yuzu/configuration/configure_input_player.h"
25 26#include "yuzu/configuration/configure_vibration.h"
26constexpr std::size_t HANDHELD_INDEX = 8; 27#include "yuzu/configuration/input_profiles.h"
28#include "yuzu/util/limitable_input_dialog.h"
27 29
28const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> 30const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
29 ConfigureInputPlayer::analog_sub_buttons{{ 31 ConfigureInputPlayer::analog_sub_buttons{{
@@ -35,6 +37,8 @@ const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
35 37
36namespace { 38namespace {
37 39
40constexpr std::size_t HANDHELD_INDEX = 8;
41
38void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, 42void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
39 bool connected) { 43 bool connected) {
40 Core::System& system{Core::System::GetInstance()}; 44 Core::System& system{Core::System::GetInstance()};
@@ -240,10 +244,11 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir)
240ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, 244ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index,
241 QWidget* bottom_row, 245 QWidget* bottom_row,
242 InputCommon::InputSubsystem* input_subsystem_, 246 InputCommon::InputSubsystem* input_subsystem_,
243 bool debug) 247 InputProfiles* profiles_, bool debug)
244 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), 248 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
245 debug(debug), input_subsystem{input_subsystem_}, timeout_timer(std::make_unique<QTimer>()), 249 debug(debug), input_subsystem{input_subsystem_}, profiles(profiles_),
246 poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row) { 250 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()),
251 bottom_row(bottom_row) {
247 ui->setupUi(this); 252 ui->setupUi(this);
248 253
249 setFocusPolicy(Qt::ClickFocus); 254 setFocusPolicy(Qt::ClickFocus);
@@ -366,6 +371,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
366 } 371 }
367 372
368 connect(analog_button, &QPushButton::clicked, [=, this] { 373 connect(analog_button, &QPushButton::clicked, [=, this] {
374 if (!map_analog_stick_accepted) {
375 map_analog_stick_accepted =
376 QMessageBox::information(
377 this, tr("Map Analog Stick"),
378 tr("After pressing OK, first move your joystick horizontally, and then "
379 "vertically.\nTo invert the axes, first move your joystick "
380 "vertically, and then horizontally."),
381 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok;
382 if (!map_analog_stick_accepted) {
383 return;
384 }
385 }
369 HandleClick( 386 HandleClick(
370 analog_map_buttons[analog_id][sub_button_id], 387 analog_map_buttons[analog_id][sub_button_id],
371 [=, this](const Common::ParamPackage& params) { 388 [=, this](const Common::ParamPackage& params) {
@@ -455,11 +472,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
455 }); 472 });
456 } 473 }
457 474
475 if (debug || player_index == 9) {
476 ui->groupConnectedController->setCheckable(false);
477 }
478
458 // The Debug Controller can only choose the Pro Controller. 479 // The Debug Controller can only choose the Pro Controller.
459 if (debug) { 480 if (debug) {
460 ui->buttonScreenshot->setEnabled(false); 481 ui->buttonScreenshot->setEnabled(false);
461 ui->buttonHome->setEnabled(false); 482 ui->buttonHome->setEnabled(false);
462 ui->groupConnectedController->setCheckable(false);
463 QStringList debug_controller_types = { 483 QStringList debug_controller_types = {
464 tr("Pro Controller"), 484 tr("Pro Controller"),
465 }; 485 };
@@ -477,11 +497,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
477 UpdateMotionButtons(); 497 UpdateMotionButtons();
478 }); 498 });
479 499
480 connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this, 500 connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
481 &ConfigureInputPlayer::UpdateMappingWithDefaults); 501 &ConfigureInputPlayer::UpdateMappingWithDefaults);
482 502
503 ui->comboDevices->setCurrentIndex(-1);
504
483 ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); 505 ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
484 UpdateInputDevices();
485 connect(ui->buttonRefreshDevices, &QPushButton::clicked, 506 connect(ui->buttonRefreshDevices, &QPushButton::clicked,
486 [this] { emit RefreshInputDevices(); }); 507 [this] { emit RefreshInputDevices(); });
487 508
@@ -492,14 +513,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
492 Common::ParamPackage params; 513 Common::ParamPackage params;
493 if (input_subsystem->GetGCButtons()->IsPolling()) { 514 if (input_subsystem->GetGCButtons()->IsPolling()) {
494 params = input_subsystem->GetGCButtons()->GetNextInput(); 515 params = input_subsystem->GetGCButtons()->GetNextInput();
495 if (params.Has("engine")) { 516 if (params.Has("engine") && IsInputAcceptable(params)) {
496 SetPollingResult(params, false); 517 SetPollingResult(params, false);
497 return; 518 return;
498 } 519 }
499 } 520 }
500 if (input_subsystem->GetGCAnalogs()->IsPolling()) { 521 if (input_subsystem->GetGCAnalogs()->IsPolling()) {
501 params = input_subsystem->GetGCAnalogs()->GetNextInput(); 522 params = input_subsystem->GetGCAnalogs()->GetNextInput();
502 if (params.Has("engine")) { 523 if (params.Has("engine") && IsInputAcceptable(params)) {
503 SetPollingResult(params, false); 524 SetPollingResult(params, false);
504 return; 525 return;
505 } 526 }
@@ -513,13 +534,24 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
513 } 534 }
514 for (auto& poller : device_pollers) { 535 for (auto& poller : device_pollers) {
515 params = poller->GetNextInput(); 536 params = poller->GetNextInput();
516 if (params.Has("engine")) { 537 if (params.Has("engine") && IsInputAcceptable(params)) {
517 SetPollingResult(params, false); 538 SetPollingResult(params, false);
518 return; 539 return;
519 } 540 }
520 } 541 }
521 }); 542 });
522 543
544 UpdateInputProfiles();
545
546 connect(ui->buttonProfilesNew, &QPushButton::clicked, this,
547 &ConfigureInputPlayer::CreateProfile);
548 connect(ui->buttonProfilesDelete, &QPushButton::clicked, this,
549 &ConfigureInputPlayer::DeleteProfile);
550 connect(ui->comboProfiles, qOverload<int>(&QComboBox::activated), this,
551 &ConfigureInputPlayer::LoadProfile);
552 connect(ui->buttonProfilesSave, &QPushButton::clicked, this,
553 &ConfigureInputPlayer::SaveProfile);
554
523 LoadConfiguration(); 555 LoadConfiguration();
524 556
525 // TODO(wwylele): enable this when we actually emulate it 557 // TODO(wwylele): enable this when we actually emulate it
@@ -529,7 +561,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
529ConfigureInputPlayer::~ConfigureInputPlayer() = default; 561ConfigureInputPlayer::~ConfigureInputPlayer() = default;
530 562
531void ConfigureInputPlayer::ApplyConfiguration() { 563void ConfigureInputPlayer::ApplyConfiguration() {
532 auto& player = Settings::values.players[player_index]; 564 auto& player = Settings::values.players.GetValue()[player_index];
533 auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons; 565 auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons;
534 auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs; 566 auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs;
535 567
@@ -543,33 +575,58 @@ void ConfigureInputPlayer::ApplyConfiguration() {
543 } 575 }
544 576
545 auto& motions = player.motions; 577 auto& motions = player.motions;
578
546 std::transform(motions_param.begin(), motions_param.end(), motions.begin(), 579 std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
547 [](const Common::ParamPackage& param) { return param.Serialize(); }); 580 [](const Common::ParamPackage& param) { return param.Serialize(); });
548 581
549 player.controller_type = 582 const auto controller_type =
550 static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex()); 583 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
551 player.connected = ui->groupConnectedController->isChecked(); 584 const auto player_connected = ui->groupConnectedController->isChecked() &&
585 controller_type != Settings::ControllerType::Handheld;
552 586
553 // Player 2-8 587 if (player.controller_type == controller_type && player.connected == player_connected) {
554 if (player_index != 0) { 588 // Set vibration devices in the event that the input device has changed.
555 UpdateController(player.controller_type, player_index, player.connected); 589 ConfigureVibration::SetVibrationDevices(player_index);
556 return; 590 return;
557 } 591 }
558 592
559 // Player 1 and Handheld 593 // Disconnect the controller first.
560 auto& handheld = Settings::values.players[HANDHELD_INDEX]; 594 UpdateController(controller_type, player_index, false);
561 // If Handheld is selected, copy all the settings from Player 1 to Handheld. 595
562 if (player.controller_type == Settings::ControllerType::Handheld) { 596 player.controller_type = controller_type;
563 handheld = player; 597 player.connected = player_connected;
564 handheld.connected = ui->groupConnectedController->isChecked(); 598
565 player.connected = false; // Disconnect Player 1 599 ConfigureVibration::SetVibrationDevices(player_index);
566 } else { 600
567 player.connected = ui->groupConnectedController->isChecked(); 601 // Handheld
568 handheld.connected = false; // Disconnect Handheld 602 if (player_index == 0) {
603 auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
604 if (controller_type == Settings::ControllerType::Handheld) {
605 handheld = player;
606 }
607 handheld.connected = ui->groupConnectedController->isChecked() &&
608 controller_type == Settings::ControllerType::Handheld;
609 UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected);
610 }
611
612 if (!player.connected) {
613 return;
569 } 614 }
570 615
571 UpdateController(player.controller_type, player_index, player.connected); 616 // This emulates a delay between disconnecting and reconnecting controllers as some games
572 UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected); 617 // do not respond to a change in controller type if it was instantaneous.
618 using namespace std::chrono_literals;
619 std::this_thread::sleep_for(20ms);
620
621 UpdateController(controller_type, player_index, player_connected);
622}
623
624void ConfigureInputPlayer::showEvent(QShowEvent* event) {
625 if (bottom_row == nullptr) {
626 return;
627 }
628 QWidget::showEvent(event);
629 ui->main->addWidget(bottom_row);
573} 630}
574 631
575void ConfigureInputPlayer::changeEvent(QEvent* event) { 632void ConfigureInputPlayer::changeEvent(QEvent* event) {
@@ -586,7 +643,7 @@ void ConfigureInputPlayer::RetranslateUI() {
586} 643}
587 644
588void ConfigureInputPlayer::LoadConfiguration() { 645void ConfigureInputPlayer::LoadConfiguration() {
589 auto& player = Settings::values.players[player_index]; 646 auto& player = Settings::values.players.GetValue()[player_index];
590 if (debug) { 647 if (debug) {
591 std::transform(Settings::values.debug_pad_buttons.begin(), 648 std::transform(Settings::values.debug_pad_buttons.begin(),
592 Settings::values.debug_pad_buttons.end(), buttons_param.begin(), 649 Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
@@ -604,6 +661,7 @@ void ConfigureInputPlayer::LoadConfiguration() {
604 } 661 }
605 662
606 UpdateUI(); 663 UpdateUI();
664 UpdateInputDeviceCombobox();
607 665
608 if (debug) { 666 if (debug) {
609 return; 667 return;
@@ -612,44 +670,75 @@ void ConfigureInputPlayer::LoadConfiguration() {
612 ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type)); 670 ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type));
613 ui->groupConnectedController->setChecked( 671 ui->groupConnectedController->setChecked(
614 player.connected || 672 player.connected ||
615 (player_index == 0 && Settings::values.players[HANDHELD_INDEX].connected)); 673 (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected));
616} 674}
617 675
618void ConfigureInputPlayer::UpdateInputDevices() { 676void ConfigureInputPlayer::ConnectPlayer(bool connected) {
619 input_devices = input_subsystem->GetInputDevices(); 677 ui->groupConnectedController->setChecked(connected);
620 ui->comboDevices->clear();
621 for (auto device : input_devices) {
622 ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
623 }
624} 678}
625 679
626void ConfigureInputPlayer::RestoreDefaults() { 680void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
627 // Reset Buttons 681 // Skip input device persistence if "Input Devices" is set to "Any".
628 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { 682 if (ui->comboDevices->currentIndex() == 0) {
629 buttons_param[button_id] = Common::ParamPackage{ 683 UpdateInputDevices();
630 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; 684 return;
631 } 685 }
632 686
633 // Reset Analogs and Modifier Buttons 687 // Find the first button that isn't empty.
634 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { 688 const auto button_param =
635 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { 689 std::find_if(buttons_param.begin(), buttons_param.end(),
636 Common::ParamPackage params{InputCommon::GenerateKeyboardParam( 690 [](const Common::ParamPackage param) { return param.Has("engine"); });
637 Config::default_analogs[analog_id][sub_button_id])}; 691 const bool buttons_empty = button_param == buttons_param.end();
638 SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
639 }
640 692
641 analogs_param[analog_id].Set( 693 const auto current_engine = button_param->Get("engine", "");
642 "modifier", InputCommon::GenerateKeyboardParam(Config::default_stick_mod[analog_id])); 694 const auto current_guid = button_param->Get("guid", "");
695 const auto current_port = button_param->Get("port", "");
696
697 const bool is_keyboard_mouse = current_engine == "keyboard" || current_engine == "mouse";
698
699 UpdateInputDevices();
700
701 if (buttons_empty) {
702 return;
643 } 703 }
644 704
645 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { 705 const bool all_one_device =
646 motions_param[motion_id] = Common::ParamPackage{ 706 std::all_of(buttons_param.begin(), buttons_param.end(),
647 InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])}; 707 [current_engine, current_guid, current_port,
708 is_keyboard_mouse](const Common::ParamPackage param) {
709 if (is_keyboard_mouse) {
710 return !param.Has("engine") || param.Get("engine", "") == "keyboard" ||
711 param.Get("engine", "") == "mouse";
712 }
713 return !param.Has("engine") || (param.Get("engine", "") == current_engine &&
714 param.Get("guid", "") == current_guid &&
715 param.Get("port", "") == current_port);
716 });
717
718 if (all_one_device) {
719 if (is_keyboard_mouse) {
720 ui->comboDevices->setCurrentIndex(1);
721 return;
722 }
723 const auto devices_it = std::find_if(
724 input_devices.begin(), input_devices.end(),
725 [current_engine, current_guid, current_port](const Common::ParamPackage param) {
726 return param.Get("class", "") == current_engine &&
727 param.Get("guid", "") == current_guid &&
728 param.Get("port", "") == current_port;
729 });
730 const int device_index =
731 devices_it != input_devices.end()
732 ? static_cast<int>(std::distance(input_devices.begin(), devices_it))
733 : 0;
734 ui->comboDevices->setCurrentIndex(device_index);
735 } else {
736 ui->comboDevices->setCurrentIndex(0);
648 } 737 }
738}
649 739
650 UpdateUI(); 740void ConfigureInputPlayer::RestoreDefaults() {
651 UpdateInputDevices(); 741 UpdateMappingWithDefaults();
652 ui->comboControllerType->setCurrentIndex(0);
653} 742}
654 743
655void ConfigureInputPlayer::ClearAll() { 744void ConfigureInputPlayer::ClearAll() {
@@ -752,10 +841,167 @@ void ConfigureInputPlayer::UpdateUI() {
752 } 841 }
753} 842}
754 843
844void ConfigureInputPlayer::UpdateInputDevices() {
845 input_devices = input_subsystem->GetInputDevices();
846 ui->comboDevices->clear();
847 for (auto device : input_devices) {
848 ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
849 }
850}
851
852void ConfigureInputPlayer::UpdateControllerIcon() {
853 // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its
854 // "nonstandard" to use an image through the icon support)
855 const QString stylesheet = [this] {
856 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
857 case Settings::ControllerType::ProController:
858 return QStringLiteral("image: url(:/controller/pro_controller%0)");
859 case Settings::ControllerType::DualJoyconDetached:
860 return QStringLiteral("image: url(:/controller/dual_joycon%0)");
861 case Settings::ControllerType::LeftJoycon:
862 return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)");
863 case Settings::ControllerType::RightJoycon:
864 return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)");
865 case Settings::ControllerType::Handheld:
866 return QStringLiteral("image: url(:/controller/handheld%0)");
867 default:
868 return QString{};
869 }
870 }();
871
872 const QString theme = [] {
873 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
874 return QStringLiteral("_dark");
875 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
876 return QStringLiteral("_midnight");
877 } else {
878 return QString{};
879 }
880 }();
881
882 ui->controllerFrame->setStyleSheet(stylesheet.arg(theme));
883}
884
885void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
886 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
887 if (debug) {
888 layout = Settings::ControllerType::ProController;
889 }
890
891 // List of all the widgets that will be hidden by any of the following layouts that need
892 // "unhidden" after the controller type changes
893 const std::array<QWidget*, 9> layout_show = {
894 ui->buttonShoulderButtonsSLSR,
895 ui->horizontalSpacerShoulderButtonsWidget,
896 ui->horizontalSpacerShoulderButtonsWidget2,
897 ui->buttonShoulderButtonsLeft,
898 ui->buttonMiscButtonsMinusScreenshot,
899 ui->bottomLeft,
900 ui->buttonShoulderButtonsRight,
901 ui->buttonMiscButtonsPlusHome,
902 ui->bottomRight,
903 };
904
905 for (auto* widget : layout_show) {
906 widget->show();
907 }
908
909 std::vector<QWidget*> layout_hidden;
910 switch (layout) {
911 case Settings::ControllerType::ProController:
912 case Settings::ControllerType::DualJoyconDetached:
913 case Settings::ControllerType::Handheld:
914 layout_hidden = {
915 ui->buttonShoulderButtonsSLSR,
916 ui->horizontalSpacerShoulderButtonsWidget2,
917 };
918 break;
919 case Settings::ControllerType::LeftJoycon:
920 layout_hidden = {
921 ui->horizontalSpacerShoulderButtonsWidget2,
922 ui->buttonShoulderButtonsRight,
923 ui->buttonMiscButtonsPlusHome,
924 ui->bottomRight,
925 };
926 break;
927 case Settings::ControllerType::RightJoycon:
928 layout_hidden = {
929 ui->horizontalSpacerShoulderButtonsWidget,
930 ui->buttonShoulderButtonsLeft,
931 ui->buttonMiscButtonsMinusScreenshot,
932 ui->bottomLeft,
933 };
934 break;
935 }
936
937 for (auto* widget : layout_hidden) {
938 widget->hide();
939 }
940}
941
942void ConfigureInputPlayer::UpdateMotionButtons() {
943 if (debug) {
944 // Motion isn't used with the debug controller, hide both groupboxes.
945 ui->buttonMotionLeftGroup->hide();
946 ui->buttonMotionRightGroup->hide();
947 return;
948 }
949
950 // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller.
951 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
952 case Settings::ControllerType::ProController:
953 case Settings::ControllerType::LeftJoycon:
954 case Settings::ControllerType::Handheld:
955 // Show "Motion 1" and hide "Motion 2".
956 ui->buttonMotionLeftGroup->show();
957 ui->buttonMotionRightGroup->hide();
958 break;
959 case Settings::ControllerType::RightJoycon:
960 // Show "Motion 2" and hide "Motion 1".
961 ui->buttonMotionLeftGroup->hide();
962 ui->buttonMotionRightGroup->show();
963 break;
964 case Settings::ControllerType::DualJoyconDetached:
965 default:
966 // Show both "Motion 1/2".
967 ui->buttonMotionLeftGroup->show();
968 ui->buttonMotionRightGroup->show();
969 break;
970 }
971}
972
755void ConfigureInputPlayer::UpdateMappingWithDefaults() { 973void ConfigureInputPlayer::UpdateMappingWithDefaults() {
756 if (ui->comboDevices->currentIndex() < 2) { 974 if (ui->comboDevices->currentIndex() == 0) {
757 return; 975 return;
758 } 976 }
977
978 if (ui->comboDevices->currentIndex() == 1) {
979 // Reset keyboard bindings
980 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
981 buttons_param[button_id] = Common::ParamPackage{
982 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
983 }
984 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
985 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
986 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
987 Config::default_analogs[analog_id][sub_button_id])};
988 SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
989 }
990
991 analogs_param[analog_id].Set("modifier", InputCommon::GenerateKeyboardParam(
992 Config::default_stick_mod[analog_id]));
993 }
994
995 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
996 motions_param[motion_id] = Common::ParamPackage{
997 InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
998 }
999
1000 UpdateUI();
1001 return;
1002 }
1003
1004 // Reset controller bindings
759 const auto& device = input_devices[ui->comboDevices->currentIndex()]; 1005 const auto& device = input_devices[ui->comboDevices->currentIndex()];
760 auto button_mapping = input_subsystem->GetButtonMappingForDevice(device); 1006 auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
761 auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device); 1007 auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
@@ -828,9 +1074,27 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
828 } 1074 }
829 1075
830 UpdateUI(); 1076 UpdateUI();
1077 UpdateInputDeviceCombobox();
1078
831 input_setter = std::nullopt; 1079 input_setter = std::nullopt;
832} 1080}
833 1081
1082bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params) const {
1083 if (ui->comboDevices->currentIndex() == 0) {
1084 return true;
1085 }
1086
1087 // Keyboard/Mouse
1088 if (ui->comboDevices->currentIndex() == 1) {
1089 return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse";
1090 }
1091
1092 const auto current_input_device = input_devices[ui->comboDevices->currentIndex()];
1093 return params.Get("engine", "") == current_input_device.Get("class", "") &&
1094 params.Get("guid", "") == current_input_device.Get("guid", "") &&
1095 params.Get("port", "") == current_input_device.Get("port", "");
1096}
1097
834void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) { 1098void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
835 if (!input_setter || !event) { 1099 if (!input_setter || !event) {
836 return; 1100 return;
@@ -865,135 +1129,101 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
865 SetPollingResult({}, true); 1129 SetPollingResult({}, true);
866} 1130}
867 1131
868void ConfigureInputPlayer::UpdateControllerIcon() { 1132void ConfigureInputPlayer::CreateProfile() {
869 // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its 1133 const auto profile_name =
870 // "nonstandard" to use an image through the icon support) 1134 LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 20);
871 const QString stylesheet = [this] {
872 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
873 case Settings::ControllerType::ProController:
874 return QStringLiteral("image: url(:/controller/pro_controller%0)");
875 case Settings::ControllerType::DualJoyconDetached:
876 return QStringLiteral("image: url(:/controller/dual_joycon%0)");
877 case Settings::ControllerType::LeftJoycon:
878 return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)");
879 case Settings::ControllerType::RightJoycon:
880 return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)");
881 case Settings::ControllerType::Handheld:
882 return QStringLiteral("image: url(:/controller/handheld%0)");
883 default:
884 return QString{};
885 }
886 }();
887
888 const QString theme = [this] {
889 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
890 return QStringLiteral("_dark");
891 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
892 return QStringLiteral("_midnight");
893 } else {
894 return QString{};
895 }
896 }();
897 1135
898 ui->controllerFrame->setStyleSheet(stylesheet.arg(theme)); 1136 if (profile_name.isEmpty()) {
899} 1137 return;
1138 }
900 1139
901void ConfigureInputPlayer::UpdateControllerAvailableButtons() { 1140 if (!InputProfiles::IsProfileNameValid(profile_name.toStdString())) {
902 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); 1141 QMessageBox::critical(this, tr("Create Input Profile"),
903 if (debug) { 1142 tr("The given profile name is not valid!"));
904 layout = Settings::ControllerType::ProController; 1143 return;
905 } 1144 }
906 1145
907 // List of all the widgets that will be hidden by any of the following layouts that need 1146 ApplyConfiguration();
908 // "unhidden" after the controller type changes
909 const std::array<QWidget*, 9> layout_show = {
910 ui->buttonShoulderButtonsSLSR,
911 ui->horizontalSpacerShoulderButtonsWidget,
912 ui->horizontalSpacerShoulderButtonsWidget2,
913 ui->buttonShoulderButtonsLeft,
914 ui->buttonMiscButtonsMinusScreenshot,
915 ui->bottomLeft,
916 ui->buttonShoulderButtonsRight,
917 ui->buttonMiscButtonsPlusHome,
918 ui->bottomRight,
919 };
920 1147
921 for (auto* widget : layout_show) { 1148 if (!profiles->CreateProfile(profile_name.toStdString(), player_index)) {
922 widget->show(); 1149 QMessageBox::critical(this, tr("Create Input Profile"),
1150 tr("Failed to create the input profile \"%1\"").arg(profile_name));
1151 UpdateInputProfiles();
1152 emit RefreshInputProfiles(player_index);
1153 return;
923 } 1154 }
924 1155
925 std::vector<QWidget*> layout_hidden; 1156 emit RefreshInputProfiles(player_index);
926 switch (layout) { 1157
927 case Settings::ControllerType::ProController: 1158 ui->comboProfiles->addItem(profile_name);
928 case Settings::ControllerType::DualJoyconDetached: 1159 ui->comboProfiles->setCurrentIndex(ui->comboProfiles->count() - 1);
929 case Settings::ControllerType::Handheld: 1160}
930 layout_hidden = { 1161
931 ui->buttonShoulderButtonsSLSR, 1162void ConfigureInputPlayer::DeleteProfile() {
932 ui->horizontalSpacerShoulderButtonsWidget2, 1163 const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
933 }; 1164
934 break; 1165 if (profile_name.isEmpty()) {
935 case Settings::ControllerType::LeftJoycon: 1166 return;
936 layout_hidden = {
937 ui->horizontalSpacerShoulderButtonsWidget2,
938 ui->buttonShoulderButtonsRight,
939 ui->buttonMiscButtonsPlusHome,
940 ui->bottomRight,
941 };
942 break;
943 case Settings::ControllerType::RightJoycon:
944 layout_hidden = {
945 ui->horizontalSpacerShoulderButtonsWidget,
946 ui->buttonShoulderButtonsLeft,
947 ui->buttonMiscButtonsMinusScreenshot,
948 ui->bottomLeft,
949 };
950 break;
951 } 1167 }
952 1168
953 for (auto* widget : layout_hidden) { 1169 if (!profiles->DeleteProfile(profile_name.toStdString())) {
954 widget->hide(); 1170 QMessageBox::critical(this, tr("Delete Input Profile"),
1171 tr("Failed to delete the input profile \"%1\"").arg(profile_name));
1172 UpdateInputProfiles();
1173 emit RefreshInputProfiles(player_index);
1174 return;
955 } 1175 }
1176
1177 emit RefreshInputProfiles(player_index);
1178
1179 ui->comboProfiles->removeItem(ui->comboProfiles->currentIndex());
1180 ui->comboProfiles->setCurrentIndex(-1);
956} 1181}
957 1182
958void ConfigureInputPlayer::UpdateMotionButtons() { 1183void ConfigureInputPlayer::LoadProfile() {
959 if (debug) { 1184 const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
960 // Motion isn't used with the debug controller, hide both groupboxes. 1185
961 ui->buttonMotionLeftGroup->hide(); 1186 if (profile_name.isEmpty()) {
962 ui->buttonMotionRightGroup->hide();
963 return; 1187 return;
964 } 1188 }
965 1189
966 // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller. 1190 ApplyConfiguration();
967 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { 1191
968 case Settings::ControllerType::ProController: 1192 if (!profiles->LoadProfile(profile_name.toStdString(), player_index)) {
969 case Settings::ControllerType::LeftJoycon: 1193 QMessageBox::critical(this, tr("Load Input Profile"),
970 case Settings::ControllerType::Handheld: 1194 tr("Failed to load the input profile \"%1\"").arg(profile_name));
971 // Show "Motion 1" and hide "Motion 2". 1195 UpdateInputProfiles();
972 ui->buttonMotionLeftGroup->show(); 1196 emit RefreshInputProfiles(player_index);
973 ui->buttonMotionRightGroup->hide(); 1197 return;
974 break;
975 case Settings::ControllerType::RightJoycon:
976 // Show "Motion 2" and hide "Motion 1".
977 ui->buttonMotionLeftGroup->hide();
978 ui->buttonMotionRightGroup->show();
979 break;
980 case Settings::ControllerType::DualJoyconDetached:
981 default:
982 // Show both "Motion 1/2".
983 ui->buttonMotionLeftGroup->show();
984 ui->buttonMotionRightGroup->show();
985 break;
986 } 1198 }
1199
1200 LoadConfiguration();
987} 1201}
988 1202
989void ConfigureInputPlayer::showEvent(QShowEvent* event) { 1203void ConfigureInputPlayer::SaveProfile() {
990 if (bottom_row == nullptr) { 1204 const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
1205
1206 if (profile_name.isEmpty()) {
1207 return;
1208 }
1209
1210 ApplyConfiguration();
1211
1212 if (!profiles->SaveProfile(profile_name.toStdString(), player_index)) {
1213 QMessageBox::critical(this, tr("Save Input Profile"),
1214 tr("Failed to save the input profile \"%1\"").arg(profile_name));
1215 UpdateInputProfiles();
1216 emit RefreshInputProfiles(player_index);
991 return; 1217 return;
992 } 1218 }
993 QWidget::showEvent(event);
994 ui->main->addWidget(bottom_row);
995} 1219}
996 1220
997void ConfigureInputPlayer::ConnectPlayer(bool connected) { 1221void ConfigureInputPlayer::UpdateInputProfiles() {
998 ui->groupConnectedController->setChecked(connected); 1222 ui->comboProfiles->clear();
1223
1224 for (const auto& profile_name : profiles->GetInputProfileNames()) {
1225 ui->comboProfiles->addItem(QString::fromStdString(profile_name));
1226 }
1227
1228 ui->comboProfiles->setCurrentIndex(-1);
999} 1229}
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index c19aefffa..23cf6f958 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -26,6 +26,8 @@ class QString;
26class QTimer; 26class QTimer;
27class QWidget; 27class QWidget;
28 28
29class InputProfiles;
30
29namespace InputCommon { 31namespace InputCommon {
30class InputSubsystem; 32class InputSubsystem;
31} 33}
@@ -45,14 +47,20 @@ class ConfigureInputPlayer : public QWidget {
45public: 47public:
46 explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row, 48 explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row,
47 InputCommon::InputSubsystem* input_subsystem_, 49 InputCommon::InputSubsystem* input_subsystem_,
48 bool debug = false); 50 InputProfiles* profiles_, bool debug = false);
49 ~ConfigureInputPlayer() override; 51 ~ConfigureInputPlayer() override;
50 52
51 /// Save all button configurations to settings file. 53 /// Save all button configurations to settings file.
52 void ApplyConfiguration(); 54 void ApplyConfiguration();
53 55
56 /// Set the connection state checkbox (used to sync state).
57 void ConnectPlayer(bool connected);
58
54 /// Update the input devices combobox. 59 /// Update the input devices combobox.
55 void UpdateInputDevices(); 60 void UpdateInputDeviceCombobox();
61
62 /// Updates the list of controller profiles.
63 void UpdateInputProfiles();
56 64
57 /// Restore all buttons to their default values. 65 /// Restore all buttons to their default values.
58 void RestoreDefaults(); 66 void RestoreDefaults();
@@ -60,9 +68,6 @@ public:
60 /// Clear all input configuration. 68 /// Clear all input configuration.
61 void ClearAll(); 69 void ClearAll();
62 70
63 /// Set the connection state checkbox (used to sync state).
64 void ConnectPlayer(bool connected);
65
66signals: 71signals:
67 /// Emitted when this controller is connected by the user. 72 /// Emitted when this controller is connected by the user.
68 void Connected(bool connected); 73 void Connected(bool connected);
@@ -70,6 +75,12 @@ signals:
70 void HandheldStateChanged(bool is_handheld); 75 void HandheldStateChanged(bool is_handheld);
71 /// Emitted when the input devices combobox is being refreshed. 76 /// Emitted when the input devices combobox is being refreshed.
72 void RefreshInputDevices(); 77 void RefreshInputDevices();
78 /**
79 * Emitted when the input profiles combobox is being refreshed.
80 * The player_index represents the current player's index, and the profile combobox
81 * will not be updated for this index as they are already updated by other mechanisms.
82 */
83 void RefreshInputProfiles(std::size_t player_index);
73 84
74protected: 85protected:
75 void showEvent(QShowEvent* event) override; 86 void showEvent(QShowEvent* event) override;
@@ -89,6 +100,9 @@ private:
89 /// Finish polling and configure input using the input_setter. 100 /// Finish polling and configure input using the input_setter.
90 void SetPollingResult(const Common::ParamPackage& params, bool abort); 101 void SetPollingResult(const Common::ParamPackage& params, bool abort);
91 102
103 /// Checks whether a given input can be accepted.
104 bool IsInputAcceptable(const Common::ParamPackage& params) const;
105
92 /// Handle mouse button press events. 106 /// Handle mouse button press events.
93 void mousePressEvent(QMouseEvent* event) override; 107 void mousePressEvent(QMouseEvent* event) override;
94 108
@@ -98,8 +112,8 @@ private:
98 /// Update UI to reflect current configuration. 112 /// Update UI to reflect current configuration.
99 void UpdateUI(); 113 void UpdateUI();
100 114
101 /// Update the controller selection combobox 115 /// Update the available input devices.
102 void UpdateControllerCombobox(); 116 void UpdateInputDevices();
103 117
104 /// Update the current controller icon. 118 /// Update the current controller icon.
105 void UpdateControllerIcon(); 119 void UpdateControllerIcon();
@@ -113,6 +127,18 @@ private:
113 /// Gets the default controller mapping for this device and auto configures the input to match. 127 /// Gets the default controller mapping for this device and auto configures the input to match.
114 void UpdateMappingWithDefaults(); 128 void UpdateMappingWithDefaults();
115 129
130 /// Creates a controller profile.
131 void CreateProfile();
132
133 /// Deletes the selected controller profile.
134 void DeleteProfile();
135
136 /// Loads the selected controller profile.
137 void LoadProfile();
138
139 /// Saves the current controller configuration into a selected controller profile.
140 void SaveProfile();
141
116 std::unique_ptr<Ui::ConfigureInputPlayer> ui; 142 std::unique_ptr<Ui::ConfigureInputPlayer> ui;
117 143
118 std::size_t player_index; 144 std::size_t player_index;
@@ -120,6 +146,8 @@ private:
120 146
121 InputCommon::InputSubsystem* input_subsystem; 147 InputCommon::InputSubsystem* input_subsystem;
122 148
149 InputProfiles* profiles;
150
123 std::unique_ptr<QTimer> timeout_timer; 151 std::unique_ptr<QTimer> timeout_timer;
124 std::unique_ptr<QTimer> poll_timer; 152 std::unique_ptr<QTimer> poll_timer;
125 153
@@ -159,12 +187,15 @@ private:
159 187
160 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; 188 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
161 189
190 /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once.
191 bool map_analog_stick_accepted{};
192
162 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, 193 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
163 /// keyboard events are ignored. 194 /// keyboard events are ignored.
164 bool want_keyboard_mouse = false; 195 bool want_keyboard_mouse{};
165 196
166 /// List of physical devices users can map with. If a SDL backed device is selected, then you 197 /// List of physical devices users can map with. If a SDL backed device is selected, then you
167 /// can usue this device to get a default mapping. 198 /// can use this device to get a default mapping.
168 std::vector<Common::ParamPackage> input_devices; 199 std::vector<Common::ParamPackage> input_devices;
169 200
170 /// Bottom row is where console wide settings are held, and its "owned" by the parent 201 /// Bottom row is where console wide settings are held, and its "owned" by the parent
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index e03461d9d..1e78b4c10 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -83,6 +83,12 @@
83 </property> 83 </property>
84 <item> 84 <item>
85 <widget class="QComboBox" name="comboControllerType"> 85 <widget class="QComboBox" name="comboControllerType">
86 <property name="minimumSize">
87 <size>
88 <width>0</width>
89 <height>21</height>
90 </size>
91 </property>
86 <item> 92 <item>
87 <property name="text"> 93 <property name="text">
88 <string>Pro Controller</string> 94 <string>Pro Controller</string>
@@ -136,6 +142,12 @@
136 </property> 142 </property>
137 <item> 143 <item>
138 <widget class="QComboBox" name="comboDevices"> 144 <widget class="QComboBox" name="comboDevices">
145 <property name="minimumSize">
146 <size>
147 <width>0</width>
148 <height>21</height>
149 </size>
150 </property>
139 <item> 151 <item>
140 <property name="text"> 152 <property name="text">
141 <string>Any</string> 153 <string>Any</string>
@@ -152,14 +164,14 @@
152 <widget class="QPushButton" name="buttonRefreshDevices"> 164 <widget class="QPushButton" name="buttonRefreshDevices">
153 <property name="minimumSize"> 165 <property name="minimumSize">
154 <size> 166 <size>
155 <width>24</width> 167 <width>21</width>
156 <height>22</height> 168 <height>21</height>
157 </size> 169 </size>
158 </property> 170 </property>
159 <property name="maximumSize"> 171 <property name="maximumSize">
160 <size> 172 <size>
161 <width>24</width> 173 <width>21</width>
162 <height>22</height> 174 <height>21</height>
163 </size> 175 </size>
164 </property> 176 </property>
165 <property name="styleSheet"> 177 <property name="styleSheet">
@@ -198,18 +210,25 @@
198 <number>5</number> 210 <number>5</number>
199 </property> 211 </property>
200 <item> 212 <item>
201 <widget class="QComboBox" name="comboProfiles"/> 213 <widget class="QComboBox" name="comboProfiles">
214 <property name="minimumSize">
215 <size>
216 <width>0</width>
217 <height>21</height>
218 </size>
219 </property>
220 </widget>
202 </item> 221 </item>
203 <item> 222 <item>
204 <widget class="QPushButton" name="buttonProfilesSave"> 223 <widget class="QPushButton" name="buttonProfilesSave">
205 <property name="maximumSize"> 224 <property name="maximumSize">
206 <size> 225 <size>
207 <width>55</width> 226 <width>68</width>
208 <height>16777215</height> 227 <height>16777215</height>
209 </size> 228 </size>
210 </property> 229 </property>
211 <property name="styleSheet"> 230 <property name="styleSheet">
212 <string notr="true">min-width: 55px;</string> 231 <string notr="true">min-width: 68px;</string>
213 </property> 232 </property>
214 <property name="text"> 233 <property name="text">
215 <string>Save</string> 234 <string>Save</string>
@@ -220,12 +239,12 @@
220 <widget class="QPushButton" name="buttonProfilesNew"> 239 <widget class="QPushButton" name="buttonProfilesNew">
221 <property name="maximumSize"> 240 <property name="maximumSize">
222 <size> 241 <size>
223 <width>55</width> 242 <width>68</width>
224 <height>16777215</height> 243 <height>16777215</height>
225 </size> 244 </size>
226 </property> 245 </property>
227 <property name="styleSheet"> 246 <property name="styleSheet">
228 <string notr="true">min-width: 55px;</string> 247 <string notr="true">min-width: 68px;</string>
229 </property> 248 </property>
230 <property name="text"> 249 <property name="text">
231 <string>New</string> 250 <string>New</string>
@@ -236,12 +255,12 @@
236 <widget class="QPushButton" name="buttonProfilesDelete"> 255 <widget class="QPushButton" name="buttonProfilesDelete">
237 <property name="maximumSize"> 256 <property name="maximumSize">
238 <size> 257 <size>
239 <width>55</width> 258 <width>68</width>
240 <height>16777215</height> 259 <height>16777215</height>
241 </size> 260 </size>
242 </property> 261 </property>
243 <property name="styleSheet"> 262 <property name="styleSheet">
244 <string notr="true">min-width: 55px;</string> 263 <string notr="true">min-width: 68px;</string>
245 </property> 264 </property>
246 <property name="text"> 265 <property name="text">
247 <string>Delete</string> 266 <string>Delete</string>
@@ -393,18 +412,18 @@
393 <widget class="QPushButton" name="buttonLStickUp"> 412 <widget class="QPushButton" name="buttonLStickUp">
394 <property name="minimumSize"> 413 <property name="minimumSize">
395 <size> 414 <size>
396 <width>57</width> 415 <width>68</width>
397 <height>0</height> 416 <height>0</height>
398 </size> 417 </size>
399 </property> 418 </property>
400 <property name="maximumSize"> 419 <property name="maximumSize">
401 <size> 420 <size>
402 <width>55</width> 421 <width>68</width>
403 <height>16777215</height> 422 <height>16777215</height>
404 </size> 423 </size>
405 </property> 424 </property>
406 <property name="styleSheet"> 425 <property name="styleSheet">
407 <string notr="true">min-width: 55px;</string> 426 <string notr="true">min-width: 68px;</string>
408 </property> 427 </property>
409 <property name="text"> 428 <property name="text">
410 <string>Up</string> 429 <string>Up</string>
@@ -463,18 +482,18 @@
463 <widget class="QPushButton" name="buttonLStickLeft"> 482 <widget class="QPushButton" name="buttonLStickLeft">
464 <property name="minimumSize"> 483 <property name="minimumSize">
465 <size> 484 <size>
466 <width>57</width> 485 <width>68</width>
467 <height>0</height> 486 <height>0</height>
468 </size> 487 </size>
469 </property> 488 </property>
470 <property name="maximumSize"> 489 <property name="maximumSize">
471 <size> 490 <size>
472 <width>55</width> 491 <width>68</width>
473 <height>16777215</height> 492 <height>16777215</height>
474 </size> 493 </size>
475 </property> 494 </property>
476 <property name="styleSheet"> 495 <property name="styleSheet">
477 <string notr="true">min-width: 55px;</string> 496 <string notr="true">min-width: 68px;</string>
478 </property> 497 </property>
479 <property name="text"> 498 <property name="text">
480 <string>Left</string> 499 <string>Left</string>
@@ -512,18 +531,18 @@
512 <widget class="QPushButton" name="buttonLStickRight"> 531 <widget class="QPushButton" name="buttonLStickRight">
513 <property name="minimumSize"> 532 <property name="minimumSize">
514 <size> 533 <size>
515 <width>57</width> 534 <width>68</width>
516 <height>0</height> 535 <height>0</height>
517 </size> 536 </size>
518 </property> 537 </property>
519 <property name="maximumSize"> 538 <property name="maximumSize">
520 <size> 539 <size>
521 <width>55</width> 540 <width>68</width>
522 <height>16777215</height> 541 <height>16777215</height>
523 </size> 542 </size>
524 </property> 543 </property>
525 <property name="styleSheet"> 544 <property name="styleSheet">
526 <string notr="true">min-width: 55px;</string> 545 <string notr="true">min-width: 68px;</string>
527 </property> 546 </property>
528 <property name="text"> 547 <property name="text">
529 <string>Right</string> 548 <string>Right</string>
@@ -594,18 +613,18 @@
594 <widget class="QPushButton" name="buttonLStickDown"> 613 <widget class="QPushButton" name="buttonLStickDown">
595 <property name="minimumSize"> 614 <property name="minimumSize">
596 <size> 615 <size>
597 <width>57</width> 616 <width>68</width>
598 <height>0</height> 617 <height>0</height>
599 </size> 618 </size>
600 </property> 619 </property>
601 <property name="maximumSize"> 620 <property name="maximumSize">
602 <size> 621 <size>
603 <width>55</width> 622 <width>68</width>
604 <height>16777215</height> 623 <height>16777215</height>
605 </size> 624 </size>
606 </property> 625 </property>
607 <property name="styleSheet"> 626 <property name="styleSheet">
608 <string notr="true">min-width: 55px;</string> 627 <string notr="true">min-width: 68px;</string>
609 </property> 628 </property>
610 <property name="text"> 629 <property name="text">
611 <string>Down</string> 630 <string>Down</string>
@@ -664,18 +683,18 @@
664 <widget class="QPushButton" name="buttonLStick"> 683 <widget class="QPushButton" name="buttonLStick">
665 <property name="minimumSize"> 684 <property name="minimumSize">
666 <size> 685 <size>
667 <width>57</width> 686 <width>68</width>
668 <height>0</height> 687 <height>0</height>
669 </size> 688 </size>
670 </property> 689 </property>
671 <property name="maximumSize"> 690 <property name="maximumSize">
672 <size> 691 <size>
673 <width>55</width> 692 <width>68</width>
674 <height>16777215</height> 693 <height>16777215</height>
675 </size> 694 </size>
676 </property> 695 </property>
677 <property name="styleSheet"> 696 <property name="styleSheet">
678 <string notr="true">min-width: 55px;</string> 697 <string notr="true">min-width: 68px;</string>
679 </property> 698 </property>
680 <property name="text"> 699 <property name="text">
681 <string>Pressed</string> 700 <string>Pressed</string>
@@ -713,18 +732,18 @@
713 <widget class="QPushButton" name="buttonLStickMod"> 732 <widget class="QPushButton" name="buttonLStickMod">
714 <property name="minimumSize"> 733 <property name="minimumSize">
715 <size> 734 <size>
716 <width>57</width> 735 <width>68</width>
717 <height>0</height> 736 <height>0</height>
718 </size> 737 </size>
719 </property> 738 </property>
720 <property name="maximumSize"> 739 <property name="maximumSize">
721 <size> 740 <size>
722 <width>55</width> 741 <width>68</width>
723 <height>16777215</height> 742 <height>16777215</height>
724 </size> 743 </size>
725 </property> 744 </property>
726 <property name="styleSheet"> 745 <property name="styleSheet">
727 <string notr="true">min-width: 55px;</string> 746 <string notr="true">min-width: 68px;</string>
728 </property> 747 </property>
729 <property name="text"> 748 <property name="text">
730 <string>Modifier</string> 749 <string>Modifier</string>
@@ -759,13 +778,13 @@
759 <widget class="QSpinBox" name="spinboxLStickRange"> 778 <widget class="QSpinBox" name="spinboxLStickRange">
760 <property name="minimumSize"> 779 <property name="minimumSize">
761 <size> 780 <size>
762 <width>55</width> 781 <width>68</width>
763 <height>21</height> 782 <height>21</height>
764 </size> 783 </size>
765 </property> 784 </property>
766 <property name="maximumSize"> 785 <property name="maximumSize">
767 <size> 786 <size>
768 <width>55</width> 787 <width>68</width>
769 <height>16777215</height> 788 <height>16777215</height>
770 </size> 789 </size>
771 </property> 790 </property>
@@ -966,18 +985,18 @@
966 <widget class="QPushButton" name="buttonDpadUp"> 985 <widget class="QPushButton" name="buttonDpadUp">
967 <property name="minimumSize"> 986 <property name="minimumSize">
968 <size> 987 <size>
969 <width>57</width> 988 <width>68</width>
970 <height>0</height> 989 <height>0</height>
971 </size> 990 </size>
972 </property> 991 </property>
973 <property name="maximumSize"> 992 <property name="maximumSize">
974 <size> 993 <size>
975 <width>55</width> 994 <width>68</width>
976 <height>16777215</height> 995 <height>16777215</height>
977 </size> 996 </size>
978 </property> 997 </property>
979 <property name="styleSheet"> 998 <property name="styleSheet">
980 <string notr="true">min-width: 55px;</string> 999 <string notr="true">min-width: 68px;</string>
981 </property> 1000 </property>
982 <property name="text"> 1001 <property name="text">
983 <string>Up</string> 1002 <string>Up</string>
@@ -1036,18 +1055,18 @@
1036 <widget class="QPushButton" name="buttonDpadLeft"> 1055 <widget class="QPushButton" name="buttonDpadLeft">
1037 <property name="minimumSize"> 1056 <property name="minimumSize">
1038 <size> 1057 <size>
1039 <width>57</width> 1058 <width>68</width>
1040 <height>0</height> 1059 <height>0</height>
1041 </size> 1060 </size>
1042 </property> 1061 </property>
1043 <property name="maximumSize"> 1062 <property name="maximumSize">
1044 <size> 1063 <size>
1045 <width>55</width> 1064 <width>68</width>
1046 <height>16777215</height> 1065 <height>16777215</height>
1047 </size> 1066 </size>
1048 </property> 1067 </property>
1049 <property name="styleSheet"> 1068 <property name="styleSheet">
1050 <string notr="true">min-width: 55px;</string> 1069 <string notr="true">min-width: 68px;</string>
1051 </property> 1070 </property>
1052 <property name="text"> 1071 <property name="text">
1053 <string>Left</string> 1072 <string>Left</string>
@@ -1085,18 +1104,18 @@
1085 <widget class="QPushButton" name="buttonDpadRight"> 1104 <widget class="QPushButton" name="buttonDpadRight">
1086 <property name="minimumSize"> 1105 <property name="minimumSize">
1087 <size> 1106 <size>
1088 <width>57</width> 1107 <width>68</width>
1089 <height>0</height> 1108 <height>0</height>
1090 </size> 1109 </size>
1091 </property> 1110 </property>
1092 <property name="maximumSize"> 1111 <property name="maximumSize">
1093 <size> 1112 <size>
1094 <width>55</width> 1113 <width>68</width>
1095 <height>16777215</height> 1114 <height>16777215</height>
1096 </size> 1115 </size>
1097 </property> 1116 </property>
1098 <property name="styleSheet"> 1117 <property name="styleSheet">
1099 <string notr="true">min-width: 55px;</string> 1118 <string notr="true">min-width: 68px;</string>
1100 </property> 1119 </property>
1101 <property name="text"> 1120 <property name="text">
1102 <string>Right</string> 1121 <string>Right</string>
@@ -1167,18 +1186,18 @@
1167 <widget class="QPushButton" name="buttonDpadDown"> 1186 <widget class="QPushButton" name="buttonDpadDown">
1168 <property name="minimumSize"> 1187 <property name="minimumSize">
1169 <size> 1188 <size>
1170 <width>57</width> 1189 <width>68</width>
1171 <height>0</height> 1190 <height>0</height>
1172 </size> 1191 </size>
1173 </property> 1192 </property>
1174 <property name="maximumSize"> 1193 <property name="maximumSize">
1175 <size> 1194 <size>
1176 <width>55</width> 1195 <width>68</width>
1177 <height>16777215</height> 1196 <height>16777215</height>
1178 </size> 1197 </size>
1179 </property> 1198 </property>
1180 <property name="styleSheet"> 1199 <property name="styleSheet">
1181 <string notr="true">min-width: 55px;</string> 1200 <string notr="true">min-width: 68px;</string>
1182 </property> 1201 </property>
1183 <property name="text"> 1202 <property name="text">
1184 <string>Down</string> 1203 <string>Down</string>
@@ -1292,18 +1311,18 @@
1292 <widget class="QPushButton" name="buttonL"> 1311 <widget class="QPushButton" name="buttonL">
1293 <property name="minimumSize"> 1312 <property name="minimumSize">
1294 <size> 1313 <size>
1295 <width>57</width> 1314 <width>68</width>
1296 <height>0</height> 1315 <height>0</height>
1297 </size> 1316 </size>
1298 </property> 1317 </property>
1299 <property name="maximumSize"> 1318 <property name="maximumSize">
1300 <size> 1319 <size>
1301 <width>55</width> 1320 <width>68</width>
1302 <height>16777215</height> 1321 <height>16777215</height>
1303 </size> 1322 </size>
1304 </property> 1323 </property>
1305 <property name="styleSheet"> 1324 <property name="styleSheet">
1306 <string notr="true">min-width: 55px;</string> 1325 <string notr="true">min-width: 68px;</string>
1307 </property> 1326 </property>
1308 <property name="text"> 1327 <property name="text">
1309 <string>L</string> 1328 <string>L</string>
@@ -1341,18 +1360,18 @@
1341 <widget class="QPushButton" name="buttonZL"> 1360 <widget class="QPushButton" name="buttonZL">
1342 <property name="minimumSize"> 1361 <property name="minimumSize">
1343 <size> 1362 <size>
1344 <width>57</width> 1363 <width>68</width>
1345 <height>0</height> 1364 <height>0</height>
1346 </size> 1365 </size>
1347 </property> 1366 </property>
1348 <property name="maximumSize"> 1367 <property name="maximumSize">
1349 <size> 1368 <size>
1350 <width>55</width> 1369 <width>68</width>
1351 <height>16777215</height> 1370 <height>16777215</height>
1352 </size> 1371 </size>
1353 </property> 1372 </property>
1354 <property name="styleSheet"> 1373 <property name="styleSheet">
1355 <string notr="true">min-width: 55px;</string> 1374 <string notr="true">min-width: 68px;</string>
1356 </property> 1375 </property>
1357 <property name="text"> 1376 <property name="text">
1358 <string>ZL</string> 1377 <string>ZL</string>
@@ -1445,18 +1464,18 @@
1445 <widget class="QPushButton" name="buttonMinus"> 1464 <widget class="QPushButton" name="buttonMinus">
1446 <property name="minimumSize"> 1465 <property name="minimumSize">
1447 <size> 1466 <size>
1448 <width>57</width> 1467 <width>68</width>
1449 <height>0</height> 1468 <height>0</height>
1450 </size> 1469 </size>
1451 </property> 1470 </property>
1452 <property name="maximumSize"> 1471 <property name="maximumSize">
1453 <size> 1472 <size>
1454 <width>55</width> 1473 <width>68</width>
1455 <height>16777215</height> 1474 <height>16777215</height>
1456 </size> 1475 </size>
1457 </property> 1476 </property>
1458 <property name="styleSheet"> 1477 <property name="styleSheet">
1459 <string notr="true">min-width: 55px;</string> 1478 <string notr="true">min-width: 68px;</string>
1460 </property> 1479 </property>
1461 <property name="text"> 1480 <property name="text">
1462 <string>Minus</string> 1481 <string>Minus</string>
@@ -1494,18 +1513,18 @@
1494 <widget class="QPushButton" name="buttonScreenshot"> 1513 <widget class="QPushButton" name="buttonScreenshot">
1495 <property name="minimumSize"> 1514 <property name="minimumSize">
1496 <size> 1515 <size>
1497 <width>57</width> 1516 <width>68</width>
1498 <height>0</height> 1517 <height>0</height>
1499 </size> 1518 </size>
1500 </property> 1519 </property>
1501 <property name="maximumSize"> 1520 <property name="maximumSize">
1502 <size> 1521 <size>
1503 <width>55</width> 1522 <width>68</width>
1504 <height>16777215</height> 1523 <height>16777215</height>
1505 </size> 1524 </size>
1506 </property> 1525 </property>
1507 <property name="styleSheet"> 1526 <property name="styleSheet">
1508 <string notr="true">min-width: 55px;</string> 1527 <string notr="true">min-width: 68px;</string>
1509 </property> 1528 </property>
1510 <property name="text"> 1529 <property name="text">
1511 <string>Capture</string> 1530 <string>Capture</string>
@@ -1564,18 +1583,18 @@
1564 <widget class="QPushButton" name="buttonPlus"> 1583 <widget class="QPushButton" name="buttonPlus">
1565 <property name="minimumSize"> 1584 <property name="minimumSize">
1566 <size> 1585 <size>
1567 <width>57</width> 1586 <width>68</width>
1568 <height>0</height> 1587 <height>0</height>
1569 </size> 1588 </size>
1570 </property> 1589 </property>
1571 <property name="maximumSize"> 1590 <property name="maximumSize">
1572 <size> 1591 <size>
1573 <width>55</width> 1592 <width>68</width>
1574 <height>16777215</height> 1593 <height>16777215</height>
1575 </size> 1594 </size>
1576 </property> 1595 </property>
1577 <property name="styleSheet"> 1596 <property name="styleSheet">
1578 <string notr="true">min-width: 55px;</string> 1597 <string notr="true">min-width: 68px;</string>
1579 </property> 1598 </property>
1580 <property name="text"> 1599 <property name="text">
1581 <string>Plus</string> 1600 <string>Plus</string>
@@ -1613,18 +1632,18 @@
1613 <widget class="QPushButton" name="buttonHome"> 1632 <widget class="QPushButton" name="buttonHome">
1614 <property name="minimumSize"> 1633 <property name="minimumSize">
1615 <size> 1634 <size>
1616 <width>57</width> 1635 <width>68</width>
1617 <height>0</height> 1636 <height>0</height>
1618 </size> 1637 </size>
1619 </property> 1638 </property>
1620 <property name="maximumSize"> 1639 <property name="maximumSize">
1621 <size> 1640 <size>
1622 <width>55</width> 1641 <width>68</width>
1623 <height>16777215</height> 1642 <height>16777215</height>
1624 </size> 1643 </size>
1625 </property> 1644 </property>
1626 <property name="styleSheet"> 1645 <property name="styleSheet">
1627 <string notr="true">min-width: 55px;</string> 1646 <string notr="true">min-width: 68px;</string>
1628 </property> 1647 </property>
1629 <property name="text"> 1648 <property name="text">
1630 <string>Home</string> 1649 <string>Home</string>
@@ -1717,18 +1736,18 @@
1717 <widget class="QPushButton" name="buttonR"> 1736 <widget class="QPushButton" name="buttonR">
1718 <property name="minimumSize"> 1737 <property name="minimumSize">
1719 <size> 1738 <size>
1720 <width>57</width> 1739 <width>68</width>
1721 <height>0</height> 1740 <height>0</height>
1722 </size> 1741 </size>
1723 </property> 1742 </property>
1724 <property name="maximumSize"> 1743 <property name="maximumSize">
1725 <size> 1744 <size>
1726 <width>55</width> 1745 <width>68</width>
1727 <height>16777215</height> 1746 <height>16777215</height>
1728 </size> 1747 </size>
1729 </property> 1748 </property>
1730 <property name="styleSheet"> 1749 <property name="styleSheet">
1731 <string notr="true">min-width: 55px;</string> 1750 <string notr="true">min-width: 68px;</string>
1732 </property> 1751 </property>
1733 <property name="text"> 1752 <property name="text">
1734 <string>R</string> 1753 <string>R</string>
@@ -1766,18 +1785,18 @@
1766 <widget class="QPushButton" name="buttonZR"> 1785 <widget class="QPushButton" name="buttonZR">
1767 <property name="minimumSize"> 1786 <property name="minimumSize">
1768 <size> 1787 <size>
1769 <width>57</width> 1788 <width>68</width>
1770 <height>0</height> 1789 <height>0</height>
1771 </size> 1790 </size>
1772 </property> 1791 </property>
1773 <property name="maximumSize"> 1792 <property name="maximumSize">
1774 <size> 1793 <size>
1775 <width>55</width> 1794 <width>68</width>
1776 <height>16777215</height> 1795 <height>16777215</height>
1777 </size> 1796 </size>
1778 </property> 1797 </property>
1779 <property name="styleSheet"> 1798 <property name="styleSheet">
1780 <string notr="true">min-width: 55px;</string> 1799 <string notr="true">min-width: 68px;</string>
1781 </property> 1800 </property>
1782 <property name="text"> 1801 <property name="text">
1783 <string>ZR</string> 1802 <string>ZR</string>
@@ -1870,18 +1889,18 @@
1870 <widget class="QPushButton" name="buttonSL"> 1889 <widget class="QPushButton" name="buttonSL">
1871 <property name="minimumSize"> 1890 <property name="minimumSize">
1872 <size> 1891 <size>
1873 <width>57</width> 1892 <width>68</width>
1874 <height>0</height> 1893 <height>0</height>
1875 </size> 1894 </size>
1876 </property> 1895 </property>
1877 <property name="maximumSize"> 1896 <property name="maximumSize">
1878 <size> 1897 <size>
1879 <width>55</width> 1898 <width>68</width>
1880 <height>16777215</height> 1899 <height>16777215</height>
1881 </size> 1900 </size>
1882 </property> 1901 </property>
1883 <property name="styleSheet"> 1902 <property name="styleSheet">
1884 <string notr="true">min-width: 55px;</string> 1903 <string notr="true">min-width: 68px;</string>
1885 </property> 1904 </property>
1886 <property name="text"> 1905 <property name="text">
1887 <string>SL</string> 1906 <string>SL</string>
@@ -1919,18 +1938,18 @@
1919 <widget class="QPushButton" name="buttonSR"> 1938 <widget class="QPushButton" name="buttonSR">
1920 <property name="minimumSize"> 1939 <property name="minimumSize">
1921 <size> 1940 <size>
1922 <width>57</width> 1941 <width>68</width>
1923 <height>0</height> 1942 <height>0</height>
1924 </size> 1943 </size>
1925 </property> 1944 </property>
1926 <property name="maximumSize"> 1945 <property name="maximumSize">
1927 <size> 1946 <size>
1928 <width>55</width> 1947 <width>68</width>
1929 <height>16777215</height> 1948 <height>16777215</height>
1930 </size> 1949 </size>
1931 </property> 1950 </property>
1932 <property name="styleSheet"> 1951 <property name="styleSheet">
1933 <string notr="true">min-width: 55px;</string> 1952 <string notr="true">min-width: 68px;</string>
1934 </property> 1953 </property>
1935 <property name="text"> 1954 <property name="text">
1936 <string>SR</string> 1955 <string>SR</string>
@@ -2027,18 +2046,18 @@
2027 <widget class="QPushButton" name="buttonMotionLeft"> 2046 <widget class="QPushButton" name="buttonMotionLeft">
2028 <property name="minimumSize"> 2047 <property name="minimumSize">
2029 <size> 2048 <size>
2030 <width>57</width> 2049 <width>68</width>
2031 <height>0</height> 2050 <height>0</height>
2032 </size> 2051 </size>
2033 </property> 2052 </property>
2034 <property name="maximumSize"> 2053 <property name="maximumSize">
2035 <size> 2054 <size>
2036 <width>55</width> 2055 <width>68</width>
2037 <height>16777215</height> 2056 <height>16777215</height>
2038 </size> 2057 </size>
2039 </property> 2058 </property>
2040 <property name="styleSheet"> 2059 <property name="styleSheet">
2041 <string notr="true">min-width: 55px;</string> 2060 <string notr="true">min-width: 68px;</string>
2042 </property> 2061 </property>
2043 <property name="text"> 2062 <property name="text">
2044 <string>Left</string> 2063 <string>Left</string>
@@ -2076,18 +2095,18 @@
2076 <widget class="QPushButton" name="buttonMotionRight"> 2095 <widget class="QPushButton" name="buttonMotionRight">
2077 <property name="minimumSize"> 2096 <property name="minimumSize">
2078 <size> 2097 <size>
2079 <width>57</width> 2098 <width>68</width>
2080 <height>0</height> 2099 <height>0</height>
2081 </size> 2100 </size>
2082 </property> 2101 </property>
2083 <property name="maximumSize"> 2102 <property name="maximumSize">
2084 <size> 2103 <size>
2085 <width>55</width> 2104 <width>68</width>
2086 <height>16777215</height> 2105 <height>16777215</height>
2087 </size> 2106 </size>
2088 </property> 2107 </property>
2089 <property name="styleSheet"> 2108 <property name="styleSheet">
2090 <string notr="true">min-width: 55px;</string> 2109 <string notr="true">min-width: 68px;</string>
2091 </property> 2110 </property>
2092 <property name="text"> 2111 <property name="text">
2093 <string>Right</string> 2112 <string>Right</string>
@@ -2225,18 +2244,18 @@
2225 <widget class="QPushButton" name="buttonX"> 2244 <widget class="QPushButton" name="buttonX">
2226 <property name="minimumSize"> 2245 <property name="minimumSize">
2227 <size> 2246 <size>
2228 <width>57</width> 2247 <width>68</width>
2229 <height>0</height> 2248 <height>0</height>
2230 </size> 2249 </size>
2231 </property> 2250 </property>
2232 <property name="maximumSize"> 2251 <property name="maximumSize">
2233 <size> 2252 <size>
2234 <width>55</width> 2253 <width>68</width>
2235 <height>16777215</height> 2254 <height>16777215</height>
2236 </size> 2255 </size>
2237 </property> 2256 </property>
2238 <property name="styleSheet"> 2257 <property name="styleSheet">
2239 <string notr="true">min-width: 55px;</string> 2258 <string notr="true">min-width: 68px;</string>
2240 </property> 2259 </property>
2241 <property name="text"> 2260 <property name="text">
2242 <string>X</string> 2261 <string>X</string>
@@ -2295,18 +2314,18 @@
2295 <widget class="QPushButton" name="buttonY"> 2314 <widget class="QPushButton" name="buttonY">
2296 <property name="minimumSize"> 2315 <property name="minimumSize">
2297 <size> 2316 <size>
2298 <width>57</width> 2317 <width>68</width>
2299 <height>0</height> 2318 <height>0</height>
2300 </size> 2319 </size>
2301 </property> 2320 </property>
2302 <property name="maximumSize"> 2321 <property name="maximumSize">
2303 <size> 2322 <size>
2304 <width>55</width> 2323 <width>68</width>
2305 <height>16777215</height> 2324 <height>16777215</height>
2306 </size> 2325 </size>
2307 </property> 2326 </property>
2308 <property name="styleSheet"> 2327 <property name="styleSheet">
2309 <string notr="true">min-width: 55px;</string> 2328 <string notr="true">min-width: 68px;</string>
2310 </property> 2329 </property>
2311 <property name="text"> 2330 <property name="text">
2312 <string>Y</string> 2331 <string>Y</string>
@@ -2344,18 +2363,18 @@
2344 <widget class="QPushButton" name="buttonA"> 2363 <widget class="QPushButton" name="buttonA">
2345 <property name="minimumSize"> 2364 <property name="minimumSize">
2346 <size> 2365 <size>
2347 <width>57</width> 2366 <width>68</width>
2348 <height>0</height> 2367 <height>0</height>
2349 </size> 2368 </size>
2350 </property> 2369 </property>
2351 <property name="maximumSize"> 2370 <property name="maximumSize">
2352 <size> 2371 <size>
2353 <width>55</width> 2372 <width>68</width>
2354 <height>16777215</height> 2373 <height>16777215</height>
2355 </size> 2374 </size>
2356 </property> 2375 </property>
2357 <property name="styleSheet"> 2376 <property name="styleSheet">
2358 <string notr="true">min-width: 55px;</string> 2377 <string notr="true">min-width: 68px;</string>
2359 </property> 2378 </property>
2360 <property name="text"> 2379 <property name="text">
2361 <string>A</string> 2380 <string>A</string>
@@ -2426,18 +2445,18 @@
2426 <widget class="QPushButton" name="buttonB"> 2445 <widget class="QPushButton" name="buttonB">
2427 <property name="minimumSize"> 2446 <property name="minimumSize">
2428 <size> 2447 <size>
2429 <width>57</width> 2448 <width>68</width>
2430 <height>0</height> 2449 <height>0</height>
2431 </size> 2450 </size>
2432 </property> 2451 </property>
2433 <property name="maximumSize"> 2452 <property name="maximumSize">
2434 <size> 2453 <size>
2435 <width>55</width> 2454 <width>68</width>
2436 <height>16777215</height> 2455 <height>16777215</height>
2437 </size> 2456 </size>
2438 </property> 2457 </property>
2439 <property name="styleSheet"> 2458 <property name="styleSheet">
2440 <string notr="true">min-width: 55px;</string> 2459 <string notr="true">min-width: 68px;</string>
2441 </property> 2460 </property>
2442 <property name="text"> 2461 <property name="text">
2443 <string>B</string> 2462 <string>B</string>
@@ -2580,18 +2599,18 @@
2580 <widget class="QPushButton" name="buttonRStickUp"> 2599 <widget class="QPushButton" name="buttonRStickUp">
2581 <property name="minimumSize"> 2600 <property name="minimumSize">
2582 <size> 2601 <size>
2583 <width>57</width> 2602 <width>68</width>
2584 <height>0</height> 2603 <height>0</height>
2585 </size> 2604 </size>
2586 </property> 2605 </property>
2587 <property name="maximumSize"> 2606 <property name="maximumSize">
2588 <size> 2607 <size>
2589 <width>55</width> 2608 <width>68</width>
2590 <height>16777215</height> 2609 <height>16777215</height>
2591 </size> 2610 </size>
2592 </property> 2611 </property>
2593 <property name="styleSheet"> 2612 <property name="styleSheet">
2594 <string notr="true">min-width: 55px;</string> 2613 <string notr="true">min-width: 68px;</string>
2595 </property> 2614 </property>
2596 <property name="text"> 2615 <property name="text">
2597 <string>Up</string> 2616 <string>Up</string>
@@ -2650,18 +2669,18 @@
2650 <widget class="QPushButton" name="buttonRStickLeft"> 2669 <widget class="QPushButton" name="buttonRStickLeft">
2651 <property name="minimumSize"> 2670 <property name="minimumSize">
2652 <size> 2671 <size>
2653 <width>57</width> 2672 <width>68</width>
2654 <height>0</height> 2673 <height>0</height>
2655 </size> 2674 </size>
2656 </property> 2675 </property>
2657 <property name="maximumSize"> 2676 <property name="maximumSize">
2658 <size> 2677 <size>
2659 <width>55</width> 2678 <width>68</width>
2660 <height>16777215</height> 2679 <height>16777215</height>
2661 </size> 2680 </size>
2662 </property> 2681 </property>
2663 <property name="styleSheet"> 2682 <property name="styleSheet">
2664 <string notr="true">min-width: 55px;</string> 2683 <string notr="true">min-width: 68px;</string>
2665 </property> 2684 </property>
2666 <property name="text"> 2685 <property name="text">
2667 <string>Left</string> 2686 <string>Left</string>
@@ -2699,18 +2718,18 @@
2699 <widget class="QPushButton" name="buttonRStickRight"> 2718 <widget class="QPushButton" name="buttonRStickRight">
2700 <property name="minimumSize"> 2719 <property name="minimumSize">
2701 <size> 2720 <size>
2702 <width>57</width> 2721 <width>68</width>
2703 <height>0</height> 2722 <height>0</height>
2704 </size> 2723 </size>
2705 </property> 2724 </property>
2706 <property name="maximumSize"> 2725 <property name="maximumSize">
2707 <size> 2726 <size>
2708 <width>55</width> 2727 <width>68</width>
2709 <height>16777215</height> 2728 <height>16777215</height>
2710 </size> 2729 </size>
2711 </property> 2730 </property>
2712 <property name="styleSheet"> 2731 <property name="styleSheet">
2713 <string notr="true">min-width: 55px;</string> 2732 <string notr="true">min-width: 68px;</string>
2714 </property> 2733 </property>
2715 <property name="text"> 2734 <property name="text">
2716 <string>Right</string> 2735 <string>Right</string>
@@ -2781,18 +2800,18 @@
2781 <widget class="QPushButton" name="buttonRStickDown"> 2800 <widget class="QPushButton" name="buttonRStickDown">
2782 <property name="minimumSize"> 2801 <property name="minimumSize">
2783 <size> 2802 <size>
2784 <width>57</width> 2803 <width>68</width>
2785 <height>0</height> 2804 <height>0</height>
2786 </size> 2805 </size>
2787 </property> 2806 </property>
2788 <property name="maximumSize"> 2807 <property name="maximumSize">
2789 <size> 2808 <size>
2790 <width>55</width> 2809 <width>68</width>
2791 <height>16777215</height> 2810 <height>16777215</height>
2792 </size> 2811 </size>
2793 </property> 2812 </property>
2794 <property name="styleSheet"> 2813 <property name="styleSheet">
2795 <string notr="true">min-width: 55px;</string> 2814 <string notr="true">min-width: 68px;</string>
2796 </property> 2815 </property>
2797 <property name="text"> 2816 <property name="text">
2798 <string>Down</string> 2817 <string>Down</string>
@@ -2851,18 +2870,18 @@
2851 <widget class="QPushButton" name="buttonRStick"> 2870 <widget class="QPushButton" name="buttonRStick">
2852 <property name="minimumSize"> 2871 <property name="minimumSize">
2853 <size> 2872 <size>
2854 <width>57</width> 2873 <width>68</width>
2855 <height>0</height> 2874 <height>0</height>
2856 </size> 2875 </size>
2857 </property> 2876 </property>
2858 <property name="maximumSize"> 2877 <property name="maximumSize">
2859 <size> 2878 <size>
2860 <width>55</width> 2879 <width>68</width>
2861 <height>16777215</height> 2880 <height>16777215</height>
2862 </size> 2881 </size>
2863 </property> 2882 </property>
2864 <property name="styleSheet"> 2883 <property name="styleSheet">
2865 <string notr="true">min-width: 55px;</string> 2884 <string notr="true">min-width: 68px;</string>
2866 </property> 2885 </property>
2867 <property name="text"> 2886 <property name="text">
2868 <string>Pressed</string> 2887 <string>Pressed</string>
@@ -2900,18 +2919,18 @@
2900 <widget class="QPushButton" name="buttonRStickMod"> 2919 <widget class="QPushButton" name="buttonRStickMod">
2901 <property name="minimumSize"> 2920 <property name="minimumSize">
2902 <size> 2921 <size>
2903 <width>57</width> 2922 <width>68</width>
2904 <height>0</height> 2923 <height>0</height>
2905 </size> 2924 </size>
2906 </property> 2925 </property>
2907 <property name="maximumSize"> 2926 <property name="maximumSize">
2908 <size> 2927 <size>
2909 <width>55</width> 2928 <width>68</width>
2910 <height>16777215</height> 2929 <height>16777215</height>
2911 </size> 2930 </size>
2912 </property> 2931 </property>
2913 <property name="styleSheet"> 2932 <property name="styleSheet">
2914 <string notr="true">min-width: 55px;</string> 2933 <string notr="true">min-width: 68px;</string>
2915 </property> 2934 </property>
2916 <property name="text"> 2935 <property name="text">
2917 <string>Modifier</string> 2936 <string>Modifier</string>
@@ -2946,13 +2965,13 @@
2946 <widget class="QSpinBox" name="spinboxRStickRange"> 2965 <widget class="QSpinBox" name="spinboxRStickRange">
2947 <property name="minimumSize"> 2966 <property name="minimumSize">
2948 <size> 2967 <size>
2949 <width>55</width> 2968 <width>68</width>
2950 <height>21</height> 2969 <height>21</height>
2951 </size> 2970 </size>
2952 </property> 2971 </property>
2953 <property name="maximumSize"> 2972 <property name="maximumSize">
2954 <size> 2973 <size>
2955 <width>55</width> 2974 <width>68</width>
2956 <height>16777215</height> 2975 <height>16777215</height>
2957 </size> 2976 </size>
2958 </property> 2977 </property>
diff --git a/src/yuzu/configuration/configure_input_profile_dialog.cpp b/src/yuzu/configuration/configure_input_profile_dialog.cpp
new file mode 100644
index 000000000..1f5cfa75b
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_profile_dialog.cpp
@@ -0,0 +1,37 @@
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_input_profile_dialog.h"
6#include "yuzu/configuration/configure_input_player.h"
7#include "yuzu/configuration/configure_input_profile_dialog.h"
8
9ConfigureInputProfileDialog::ConfigureInputProfileDialog(
10 QWidget* parent, InputCommon::InputSubsystem* input_subsystem, InputProfiles* profiles)
11 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputProfileDialog>()),
12 profile_widget(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, false)) {
13 ui->setupUi(this);
14
15 ui->controllerLayout->addWidget(profile_widget);
16
17 connect(ui->clear_all_button, &QPushButton::clicked, this,
18 [this] { profile_widget->ClearAll(); });
19 connect(ui->restore_defaults_button, &QPushButton::clicked, this,
20 [this] { profile_widget->RestoreDefaults(); });
21
22 RetranslateUI();
23}
24
25ConfigureInputProfileDialog::~ConfigureInputProfileDialog() = default;
26
27void ConfigureInputProfileDialog::changeEvent(QEvent* event) {
28 if (event->type() == QEvent::LanguageChange) {
29 RetranslateUI();
30 }
31
32 QDialog::changeEvent(event);
33}
34
35void ConfigureInputProfileDialog::RetranslateUI() {
36 ui->retranslateUi(this);
37}
diff --git a/src/yuzu/configuration/configure_input_profile_dialog.h b/src/yuzu/configuration/configure_input_profile_dialog.h
new file mode 100644
index 000000000..e6386bdbb
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_profile_dialog.h
@@ -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#pragma once
6
7#include <memory>
8#include <QDialog>
9
10class QPushButton;
11
12class ConfigureInputPlayer;
13
14class InputProfiles;
15
16namespace InputCommon {
17class InputSubsystem;
18}
19
20namespace Ui {
21class ConfigureInputProfileDialog;
22}
23
24class ConfigureInputProfileDialog : public QDialog {
25 Q_OBJECT
26
27public:
28 explicit ConfigureInputProfileDialog(QWidget* parent,
29 InputCommon::InputSubsystem* input_subsystem,
30 InputProfiles* profiles);
31 ~ConfigureInputProfileDialog() override;
32
33private:
34 void changeEvent(QEvent* event) override;
35 void RetranslateUI();
36
37 std::unique_ptr<Ui::ConfigureInputProfileDialog> ui;
38
39 ConfigureInputPlayer* profile_widget;
40};
diff --git a/src/yuzu/configuration/configure_input_dialog.ui b/src/yuzu/configuration/configure_input_profile_dialog.ui
index b92ddb200..726cf6905 100644
--- a/src/yuzu/configuration/configure_input_dialog.ui
+++ b/src/yuzu/configuration/configure_input_profile_dialog.ui
@@ -1,7 +1,7 @@
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>ConfigureInputDialog</class> 3 <class>ConfigureInputProfileDialog</class>
4 <widget class="QDialog" name="ConfigureInputDialog"> 4 <widget class="QDialog" name="ConfigureInputProfileDialog">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
@@ -11,7 +11,7 @@
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
14 <string>Configure Input</string> 14 <string>Create Input Profile</string>
15 </property> 15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout"> 16 <layout class="QVBoxLayout" name="verticalLayout">
17 <property name="spacing"> 17 <property name="spacing">
@@ -30,11 +30,25 @@
30 <number>9</number> 30 <number>9</number>
31 </property> 31 </property>
32 <item> 32 <item>
33 <layout class="QHBoxLayout" name="inputLayout"/> 33 <layout class="QHBoxLayout" name="controllerLayout"/>
34 </item> 34 </item>
35 <item> 35 <item>
36 <layout class="QHBoxLayout" name="horizontalLayout"> 36 <layout class="QHBoxLayout" name="horizontalLayout">
37 <item> 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>
38 <widget class="QDialogButtonBox" name="buttonBox"> 52 <widget class="QDialogButtonBox" name="buttonBox">
39 <property name="standardButtons"> 53 <property name="standardButtons">
40 <set>QDialogButtonBox::Ok</set> 54 <set>QDialogButtonBox::Ok</set>
@@ -50,7 +64,7 @@
50 <connection> 64 <connection>
51 <sender>buttonBox</sender> 65 <sender>buttonBox</sender>
52 <signal>accepted()</signal> 66 <signal>accepted()</signal>
53 <receiver>ConfigureInputDialog</receiver> 67 <receiver>ConfigureInputProfileDialog</receiver>
54 <slot>accept()</slot> 68 <slot>accept()</slot>
55 </connection> 69 </connection>
56 </connections> 70 </connections>
diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui
index 602cf8cd8..5b78c5a4b 100644
--- a/src/yuzu/configuration/configure_motion_touch.ui
+++ b/src/yuzu/configuration/configure_motion_touch.ui
@@ -312,16 +312,6 @@
312 <signal>accepted()</signal> 312 <signal>accepted()</signal>
313 <receiver>ConfigureMotionTouch</receiver> 313 <receiver>ConfigureMotionTouch</receiver>
314 <slot>ApplyConfiguration()</slot> 314 <slot>ApplyConfiguration()</slot>
315 <hints>
316 <hint type="sourcelabel">
317 <x>220</x>
318 <y>380</y>
319 </hint>
320 <hint type="destinationlabel">
321 <x>220</x>
322 <y>200</y>
323 </hint>
324 </hints>
325 </connection> 315 </connection>
326 </connections> 316 </connections>
327</ui> 317</ui>
diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui
index 74552fdbd..5b99e1c37 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.ui
+++ b/src/yuzu/configuration/configure_mouse_advanced.ui
@@ -15,7 +15,7 @@
15 </property> 15 </property>
16 <property name="styleSheet"> 16 <property name="styleSheet">
17 <string notr="true">QPushButton { 17 <string notr="true">QPushButton {
18 min-width: 55px; 18 min-width: 60px;
19}</string> 19}</string>
20 </property> 20 </property>
21 <layout class="QVBoxLayout" name="verticalLayout"> 21 <layout class="QVBoxLayout" name="verticalLayout">
@@ -42,13 +42,13 @@
42 <widget class="QPushButton" name="forward_button"> 42 <widget class="QPushButton" name="forward_button">
43 <property name="minimumSize"> 43 <property name="minimumSize">
44 <size> 44 <size>
45 <width>57</width> 45 <width>68</width>
46 <height>0</height> 46 <height>0</height>
47 </size> 47 </size>
48 </property> 48 </property>
49 <property name="maximumSize"> 49 <property name="maximumSize">
50 <size> 50 <size>
51 <width>16777215</width> 51 <width>68</width>
52 <height>16777215</height> 52 <height>16777215</height>
53 </size> 53 </size>
54 </property> 54 </property>
@@ -82,7 +82,7 @@
82 <widget class="QPushButton" name="back_button"> 82 <widget class="QPushButton" name="back_button">
83 <property name="minimumSize"> 83 <property name="minimumSize">
84 <size> 84 <size>
85 <width>57</width> 85 <width>68</width>
86 <height>0</height> 86 <height>0</height>
87 </size> 87 </size>
88 </property> 88 </property>
@@ -110,7 +110,7 @@
110 <widget class="QPushButton" name="left_button"> 110 <widget class="QPushButton" name="left_button">
111 <property name="minimumSize"> 111 <property name="minimumSize">
112 <size> 112 <size>
113 <width>57</width> 113 <width>68</width>
114 <height>0</height> 114 <height>0</height>
115 </size> 115 </size>
116 </property> 116 </property>
@@ -138,13 +138,13 @@
138 <widget class="QPushButton" name="middle_button"> 138 <widget class="QPushButton" name="middle_button">
139 <property name="minimumSize"> 139 <property name="minimumSize">
140 <size> 140 <size>
141 <width>57</width> 141 <width>68</width>
142 <height>0</height> 142 <height>0</height>
143 </size> 143 </size>
144 </property> 144 </property>
145 <property name="maximumSize"> 145 <property name="maximumSize">
146 <size> 146 <size>
147 <width>16777215</width> 147 <width>68</width>
148 <height>16777215</height> 148 <height>16777215</height>
149 </size> 149 </size>
150 </property> 150 </property>
@@ -204,13 +204,13 @@
204 <widget class="QPushButton" name="right_button"> 204 <widget class="QPushButton" name="right_button">
205 <property name="minimumSize"> 205 <property name="minimumSize">
206 <size> 206 <size>
207 <width>57</width> 207 <width>68</width>
208 <height>0</height> 208 <height>0</height>
209 </size> 209 </size>
210 </property> 210 </property>
211 <property name="maximumSize"> 211 <property name="maximumSize">
212 <size> 212 <size>
213 <width>16777215</width> 213 <width>68</width>
214 <height>16777215</height> 214 <height>16777215</height>
215 </size> 215 </size>
216 </property> 216 </property>
@@ -256,13 +256,13 @@
256 <widget class="QPushButton" name="buttonClearAll"> 256 <widget class="QPushButton" name="buttonClearAll">
257 <property name="minimumSize"> 257 <property name="minimumSize">
258 <size> 258 <size>
259 <width>57</width> 259 <width>68</width>
260 <height>0</height> 260 <height>0</height>
261 </size> 261 </size>
262 </property> 262 </property>
263 <property name="maximumSize"> 263 <property name="maximumSize">
264 <size> 264 <size>
265 <width>16777215</width> 265 <width>68</width>
266 <height>16777215</height> 266 <height>16777215</height>
267 </size> 267 </size>
268 </property> 268 </property>
@@ -275,13 +275,13 @@
275 <widget class="QPushButton" name="buttonRestoreDefaults"> 275 <widget class="QPushButton" name="buttonRestoreDefaults">
276 <property name="minimumSize"> 276 <property name="minimumSize">
277 <size> 277 <size>
278 <width>57</width> 278 <width>68</width>
279 <height>0</height> 279 <height>0</height>
280 </size> 280 </size>
281 </property> 281 </property>
282 <property name="maximumSize"> 282 <property name="maximumSize">
283 <size> 283 <size>
284 <width>16777215</width> 284 <width>68</width>
285 <height>16777215</height> 285 <height>16777215</height>
286 </size> 286 </size>
287 </property> 287 </property>
@@ -324,32 +324,12 @@
324 <signal>accepted()</signal> 324 <signal>accepted()</signal>
325 <receiver>ConfigureMouseAdvanced</receiver> 325 <receiver>ConfigureMouseAdvanced</receiver>
326 <slot>accept()</slot> 326 <slot>accept()</slot>
327 <hints>
328 <hint type="sourcelabel">
329 <x>124</x>
330 <y>266</y>
331 </hint>
332 <hint type="destinationlabel">
333 <x>124</x>
334 <y>143</y>
335 </hint>
336 </hints>
337 </connection> 327 </connection>
338 <connection> 328 <connection>
339 <sender>buttonBox</sender> 329 <sender>buttonBox</sender>
340 <signal>rejected()</signal> 330 <signal>rejected()</signal>
341 <receiver>ConfigureMouseAdvanced</receiver> 331 <receiver>ConfigureMouseAdvanced</receiver>
342 <slot>reject()</slot> 332 <slot>reject()</slot>
343 <hints>
344 <hint type="sourcelabel">
345 <x>124</x>
346 <y>266</y>
347 </hint>
348 <hint type="destinationlabel">
349 <x>124</x>
350 <y>143</y>
351 </hint>
352 </hints>
353 </connection> 333 </connection>
354 </connections> 334 </connections>
355</ui> 335</ui>
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 002db3f93..8eac3bd9d 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -16,6 +16,7 @@
16 16
17#include "common/common_paths.h" 17#include "common/common_paths.h"
18#include "common/file_util.h" 18#include "common/file_util.h"
19#include "core/core.h"
19#include "core/file_sys/control_metadata.h" 20#include "core/file_sys/control_metadata.h"
20#include "core/file_sys/patch_manager.h" 21#include "core/file_sys/patch_manager.h"
21#include "core/file_sys/xts_archive.h" 22#include "core/file_sys/xts_archive.h"
@@ -29,7 +30,8 @@
29 30
30ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id) 31ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id)
31 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) { 32 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) {
32 game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false); 33 game_config = std::make_unique<Config>(fmt::format("{:016X}", title_id),
34 Config::ConfigType::PerGameConfig);
33 35
34 Settings::SetConfiguringGlobal(false); 36 Settings::SetConfiguringGlobal(false);
35 37
@@ -88,9 +90,11 @@ void ConfigurePerGame::LoadConfiguration() {
88 ui->display_title_id->setText( 90 ui->display_title_id->setText(
89 QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper()); 91 QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper());
90 92
91 FileSys::PatchManager pm{title_id}; 93 auto& system = Core::System::GetInstance();
94 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
95 system.GetContentProvider()};
92 const auto control = pm.GetControlMetadata(); 96 const auto control = pm.GetControlMetadata();
93 const auto loader = Loader::GetLoader(file); 97 const auto loader = Loader::GetLoader(system, file);
94 98
95 if (control.first != nullptr) { 99 if (control.first != nullptr) {
96 ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); 100 ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
diff --git a/src/yuzu/configuration/configure_per_game.ui b/src/yuzu/configuration/configure_per_game.ui
index d2057c4ab..25975b3b9 100644
--- a/src/yuzu/configuration/configure_per_game.ui
+++ b/src/yuzu/configuration/configure_per_game.ui
@@ -319,32 +319,12 @@
319 <signal>accepted()</signal> 319 <signal>accepted()</signal>
320 <receiver>ConfigurePerGame</receiver> 320 <receiver>ConfigurePerGame</receiver>
321 <slot>accept()</slot> 321 <slot>accept()</slot>
322 <hints>
323 <hint type="sourcelabel">
324 <x>248</x>
325 <y>254</y>
326 </hint>
327 <hint type="destinationlabel">
328 <x>157</x>
329 <y>274</y>
330 </hint>
331 </hints>
332 </connection> 322 </connection>
333 <connection> 323 <connection>
334 <sender>buttonBox</sender> 324 <sender>buttonBox</sender>
335 <signal>rejected()</signal> 325 <signal>rejected()</signal>
336 <receiver>ConfigurePerGame</receiver> 326 <receiver>ConfigurePerGame</receiver>
337 <slot>reject()</slot> 327 <slot>reject()</slot>
338 <hints>
339 <hint type="sourcelabel">
340 <x>316</x>
341 <y>260</y>
342 </hint>
343 <hint type="destinationlabel">
344 <x>286</x>
345 <y>274</y>
346 </hint>
347 </hints>
348 </connection> 328 </connection>
349 </connections> 329 </connections>
350</ui> 330</ui>
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index 793fd8975..cdeeec01c 100644
--- a/src/yuzu/configuration/configure_per_game_addons.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -112,8 +112,10 @@ void ConfigurePerGameAddons::LoadConfiguration() {
112 return; 112 return;
113 } 113 }
114 114
115 FileSys::PatchManager pm{title_id}; 115 auto& system = Core::System::GetInstance();
116 const auto loader = Loader::GetLoader(file); 116 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
117 system.GetContentProvider()};
118 const auto loader = Loader::GetLoader(system, file);
117 119
118 FileSys::VirtualFile update_raw; 120 FileSys::VirtualFile update_raw;
119 loader->ReadUpdateRaw(update_raw); 121 loader->ReadUpdateRaw(update_raw);
diff --git a/src/yuzu/configuration/configure_touch_from_button.ui b/src/yuzu/configuration/configure_touch_from_button.ui
index f581e27e0..757219d54 100644
--- a/src/yuzu/configuration/configure_touch_from_button.ui
+++ b/src/yuzu/configuration/configure_touch_from_button.ui
@@ -216,16 +216,6 @@ Drag points to change position, or double-click table cells to edit values.</str
216 <signal>rejected()</signal> 216 <signal>rejected()</signal>
217 <receiver>ConfigureTouchFromButton</receiver> 217 <receiver>ConfigureTouchFromButton</receiver>
218 <slot>reject()</slot> 218 <slot>reject()</slot>
219 <hints>
220 <hint type="sourcelabel">
221 <x>249</x>
222 <y>428</y>
223 </hint>
224 <hint type="destinationlabel">
225 <x>249</x>
226 <y>224</y>
227 </hint>
228 </hints>
229 </connection> 219 </connection>
230 </connections> 220 </connections>
231</ui> 221</ui>
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.ui b/src/yuzu/configuration/configure_touchscreen_advanced.ui
index 1171c2dd1..30ceccddb 100644
--- a/src/yuzu/configuration/configure_touchscreen_advanced.ui
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.ui
@@ -168,32 +168,12 @@
168 <signal>accepted()</signal> 168 <signal>accepted()</signal>
169 <receiver>ConfigureTouchscreenAdvanced</receiver> 169 <receiver>ConfigureTouchscreenAdvanced</receiver>
170 <slot>accept()</slot> 170 <slot>accept()</slot>
171 <hints>
172 <hint type="sourcelabel">
173 <x>140</x>
174 <y>318</y>
175 </hint>
176 <hint type="destinationlabel">
177 <x>140</x>
178 <y>169</y>
179 </hint>
180 </hints>
181 </connection> 171 </connection>
182 <connection> 172 <connection>
183 <sender>buttonBox</sender> 173 <sender>buttonBox</sender>
184 <signal>rejected()</signal> 174 <signal>rejected()</signal>
185 <receiver>ConfigureTouchscreenAdvanced</receiver> 175 <receiver>ConfigureTouchscreenAdvanced</receiver>
186 <slot>reject()</slot> 176 <slot>reject()</slot>
187 <hints> 177 </connection>
188 <hint type="sourcelabel">
189 <x>140</x>
190 <y>318</y>
191 </hint>
192 <hint type="destinationlabel">
193 <x>140</x>
194 <y>169</y>
195 </hint>
196 </hints>
197 </connection>
198 </connections> 178 </connections>
199</ui> 179</ui>
diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp
new file mode 100644
index 000000000..7dcb2c5b9
--- /dev/null
+++ b/src/yuzu/configuration/configure_vibration.cpp
@@ -0,0 +1,146 @@
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 <algorithm>
6#include <unordered_map>
7
8#include <fmt/format.h>
9
10#include "common/param_package.h"
11#include "core/settings.h"
12#include "ui_configure_vibration.h"
13#include "yuzu/configuration/configure_vibration.h"
14
15ConfigureVibration::ConfigureVibration(QWidget* parent)
16 : QDialog(parent), ui(std::make_unique<Ui::ConfigureVibration>()) {
17 ui->setupUi(this);
18
19 vibration_groupboxes = {
20 ui->vibrationGroupPlayer1, ui->vibrationGroupPlayer2, ui->vibrationGroupPlayer3,
21 ui->vibrationGroupPlayer4, ui->vibrationGroupPlayer5, ui->vibrationGroupPlayer6,
22 ui->vibrationGroupPlayer7, ui->vibrationGroupPlayer8,
23 };
24
25 vibration_spinboxes = {
26 ui->vibrationSpinPlayer1, ui->vibrationSpinPlayer2, ui->vibrationSpinPlayer3,
27 ui->vibrationSpinPlayer4, ui->vibrationSpinPlayer5, ui->vibrationSpinPlayer6,
28 ui->vibrationSpinPlayer7, ui->vibrationSpinPlayer8,
29 };
30
31 const auto& players = Settings::values.players.GetValue();
32
33 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
34 vibration_groupboxes[i]->setChecked(players[i].vibration_enabled);
35 vibration_spinboxes[i]->setValue(players[i].vibration_strength);
36 }
37
38 ui->checkBoxAccurateVibration->setChecked(
39 Settings::values.enable_accurate_vibrations.GetValue());
40
41 if (!Settings::IsConfiguringGlobal()) {
42 ui->checkBoxAccurateVibration->setDisabled(true);
43 }
44
45 RetranslateUI();
46}
47
48ConfigureVibration::~ConfigureVibration() = default;
49
50void ConfigureVibration::ApplyConfiguration() {
51 auto& players = Settings::values.players.GetValue();
52
53 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
54 players[i].vibration_enabled = vibration_groupboxes[i]->isChecked();
55 players[i].vibration_strength = vibration_spinboxes[i]->value();
56 }
57
58 Settings::values.enable_accurate_vibrations.SetValue(
59 ui->checkBoxAccurateVibration->isChecked());
60}
61
62void ConfigureVibration::SetVibrationDevices(std::size_t player_index) {
63 using namespace Settings::NativeButton;
64 static constexpr std::array<std::array<Settings::NativeButton::Values, 6>, 2> buttons{{
65 {DLeft, DUp, DRight, DDown, L, ZL}, // Left Buttons
66 {A, B, X, Y, R, ZR}, // Right Buttons
67 }};
68
69 auto& player = Settings::values.players.GetValue()[player_index];
70
71 for (std::size_t device_idx = 0; device_idx < buttons.size(); ++device_idx) {
72 std::unordered_map<std::string, int> params_count;
73
74 for (const auto button_index : buttons[device_idx]) {
75 const auto& player_button = player.buttons[button_index];
76
77 if (params_count.find(player_button) != params_count.end()) {
78 ++params_count[player_button];
79 continue;
80 }
81
82 params_count.insert_or_assign(player_button, 1);
83 }
84
85 const auto it = std::max_element(
86 params_count.begin(), params_count.end(),
87 [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; });
88
89 auto& vibration_param_str = player.vibrations[device_idx];
90 vibration_param_str.clear();
91
92 if (it->first.empty()) {
93 continue;
94 }
95
96 const auto param = Common::ParamPackage(it->first);
97
98 const auto engine = param.Get("engine", "");
99 const auto guid = param.Get("guid", "");
100 const auto port = param.Get("port", "");
101
102 if (engine.empty() || engine == "keyboard" || engine == "mouse") {
103 continue;
104 }
105
106 vibration_param_str += fmt::format("engine:{}", engine);
107
108 if (!port.empty()) {
109 vibration_param_str += fmt::format(",port:{}", port);
110 }
111 if (!guid.empty()) {
112 vibration_param_str += fmt::format(",guid:{}", guid);
113 }
114 }
115
116 if (player.vibrations[0] != player.vibrations[1]) {
117 return;
118 }
119
120 if (!player.vibrations[0].empty() &&
121 player.controller_type != Settings::ControllerType::RightJoycon) {
122 player.vibrations[1].clear();
123 } else if (!player.vibrations[1].empty() &&
124 player.controller_type == Settings::ControllerType::RightJoycon) {
125 player.vibrations[0].clear();
126 }
127}
128
129void ConfigureVibration::SetAllVibrationDevices() {
130 // Set vibration devices for all player indices including handheld
131 for (std::size_t player_idx = 0; player_idx < NUM_PLAYERS + 1; ++player_idx) {
132 SetVibrationDevices(player_idx);
133 }
134}
135
136void ConfigureVibration::changeEvent(QEvent* event) {
137 if (event->type() == QEvent::LanguageChange) {
138 RetranslateUI();
139 }
140
141 QDialog::changeEvent(event);
142}
143
144void ConfigureVibration::RetranslateUI() {
145 ui->retranslateUi(this);
146}
diff --git a/src/yuzu/configuration/configure_vibration.h b/src/yuzu/configuration/configure_vibration.h
new file mode 100644
index 000000000..07411a86f
--- /dev/null
+++ b/src/yuzu/configuration/configure_vibration.h
@@ -0,0 +1,43 @@
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 <QDialog>
10
11class QGroupBox;
12class QSpinBox;
13
14namespace Ui {
15class ConfigureVibration;
16}
17
18class ConfigureVibration : public QDialog {
19 Q_OBJECT
20
21public:
22 explicit ConfigureVibration(QWidget* parent);
23 ~ConfigureVibration() override;
24
25 void ApplyConfiguration();
26
27 static void SetVibrationDevices(std::size_t player_index);
28 static void SetAllVibrationDevices();
29
30private:
31 void changeEvent(QEvent* event) override;
32 void RetranslateUI();
33
34 std::unique_ptr<Ui::ConfigureVibration> ui;
35
36 static constexpr std::size_t NUM_PLAYERS = 8;
37
38 // Groupboxes encapsulating the vibration strength spinbox.
39 std::array<QGroupBox*, NUM_PLAYERS> vibration_groupboxes;
40
41 // Spinboxes representing the vibration strength percentage.
42 std::array<QSpinBox*, NUM_PLAYERS> vibration_spinboxes;
43};
diff --git a/src/yuzu/configuration/configure_vibration.ui b/src/yuzu/configuration/configure_vibration.ui
new file mode 100644
index 000000000..efdf317a9
--- /dev/null
+++ b/src/yuzu/configuration/configure_vibration.ui
@@ -0,0 +1,546 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureVibration</class>
4 <widget class="QDialog" name="ConfigureVibration">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>364</width>
10 <height>242</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Vibration</string>
15 </property>
16 <property name="styleSheet">
17 <string notr="true"/>
18 </property>
19 <layout class="QVBoxLayout">
20 <item>
21 <widget class="QGroupBox" name="vibrationStrengthGroup">
22 <property name="title">
23 <string>Vibration</string>
24 </property>
25 <layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,0">
26 <property name="leftMargin">
27 <number>9</number>
28 </property>
29 <property name="topMargin">
30 <number>9</number>
31 </property>
32 <property name="rightMargin">
33 <number>9</number>
34 </property>
35 <property name="bottomMargin">
36 <number>9</number>
37 </property>
38 <item>
39 <widget class="QWidget" name="player14Widget" native="true">
40 <layout class="QHBoxLayout" name="horizontalLayout_4">
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="QGroupBox" name="vibrationGroupPlayer1">
55 <property name="title">
56 <string>Player 1</string>
57 </property>
58 <property name="checkable">
59 <bool>true</bool>
60 </property>
61 <layout class="QHBoxLayout" name="horizontalLayout_8">
62 <property name="leftMargin">
63 <number>3</number>
64 </property>
65 <property name="topMargin">
66 <number>3</number>
67 </property>
68 <property name="rightMargin">
69 <number>3</number>
70 </property>
71 <property name="bottomMargin">
72 <number>3</number>
73 </property>
74 <item>
75 <widget class="QSpinBox" name="vibrationSpinPlayer1">
76 <property name="minimumSize">
77 <size>
78 <width>68</width>
79 <height>21</height>
80 </size>
81 </property>
82 <property name="maximumSize">
83 <size>
84 <width>68</width>
85 <height>16777215</height>
86 </size>
87 </property>
88 <property name="suffix">
89 <string>%</string>
90 </property>
91 <property name="minimum">
92 <number>1</number>
93 </property>
94 <property name="maximum">
95 <number>150</number>
96 </property>
97 <property name="value">
98 <number>100</number>
99 </property>
100 </widget>
101 </item>
102 </layout>
103 </widget>
104 </item>
105 <item>
106 <widget class="QGroupBox" name="vibrationGroupPlayer2">
107 <property name="title">
108 <string>Player 2</string>
109 </property>
110 <property name="checkable">
111 <bool>true</bool>
112 </property>
113 <layout class="QHBoxLayout" name="horizontalLayout_9">
114 <property name="leftMargin">
115 <number>3</number>
116 </property>
117 <property name="topMargin">
118 <number>3</number>
119 </property>
120 <property name="rightMargin">
121 <number>3</number>
122 </property>
123 <property name="bottomMargin">
124 <number>3</number>
125 </property>
126 <item>
127 <widget class="QSpinBox" name="vibrationSpinPlayer2">
128 <property name="minimumSize">
129 <size>
130 <width>68</width>
131 <height>21</height>
132 </size>
133 </property>
134 <property name="maximumSize">
135 <size>
136 <width>68</width>
137 <height>16777215</height>
138 </size>
139 </property>
140 <property name="suffix">
141 <string>%</string>
142 </property>
143 <property name="minimum">
144 <number>1</number>
145 </property>
146 <property name="maximum">
147 <number>150</number>
148 </property>
149 <property name="value">
150 <number>100</number>
151 </property>
152 </widget>
153 </item>
154 </layout>
155 </widget>
156 </item>
157 <item>
158 <widget class="QGroupBox" name="vibrationGroupPlayer3">
159 <property name="title">
160 <string>Player 3</string>
161 </property>
162 <property name="checkable">
163 <bool>true</bool>
164 </property>
165 <layout class="QHBoxLayout" name="horizontalLayout_10">
166 <property name="leftMargin">
167 <number>3</number>
168 </property>
169 <property name="topMargin">
170 <number>3</number>
171 </property>
172 <property name="rightMargin">
173 <number>3</number>
174 </property>
175 <property name="bottomMargin">
176 <number>3</number>
177 </property>
178 <item>
179 <widget class="QSpinBox" name="vibrationSpinPlayer3">
180 <property name="minimumSize">
181 <size>
182 <width>68</width>
183 <height>21</height>
184 </size>
185 </property>
186 <property name="maximumSize">
187 <size>
188 <width>68</width>
189 <height>16777215</height>
190 </size>
191 </property>
192 <property name="suffix">
193 <string>%</string>
194 </property>
195 <property name="minimum">
196 <number>1</number>
197 </property>
198 <property name="maximum">
199 <number>150</number>
200 </property>
201 <property name="value">
202 <number>100</number>
203 </property>
204 </widget>
205 </item>
206 </layout>
207 </widget>
208 </item>
209 <item>
210 <widget class="QGroupBox" name="vibrationGroupPlayer4">
211 <property name="title">
212 <string>Player 4</string>
213 </property>
214 <property name="checkable">
215 <bool>true</bool>
216 </property>
217 <layout class="QHBoxLayout" name="horizontalLayout_11">
218 <property name="leftMargin">
219 <number>3</number>
220 </property>
221 <property name="topMargin">
222 <number>3</number>
223 </property>
224 <property name="rightMargin">
225 <number>3</number>
226 </property>
227 <property name="bottomMargin">
228 <number>3</number>
229 </property>
230 <item>
231 <widget class="QSpinBox" name="vibrationSpinPlayer4">
232 <property name="minimumSize">
233 <size>
234 <width>68</width>
235 <height>21</height>
236 </size>
237 </property>
238 <property name="maximumSize">
239 <size>
240 <width>68</width>
241 <height>16777215</height>
242 </size>
243 </property>
244 <property name="suffix">
245 <string>%</string>
246 </property>
247 <property name="minimum">
248 <number>1</number>
249 </property>
250 <property name="maximum">
251 <number>150</number>
252 </property>
253 <property name="value">
254 <number>100</number>
255 </property>
256 </widget>
257 </item>
258 </layout>
259 </widget>
260 </item>
261 </layout>
262 </widget>
263 </item>
264 <item>
265 <widget class="QWidget" name="player58Widget" native="true">
266 <layout class="QHBoxLayout" name="horizontalLayout_6">
267 <property name="leftMargin">
268 <number>0</number>
269 </property>
270 <property name="topMargin">
271 <number>0</number>
272 </property>
273 <property name="rightMargin">
274 <number>0</number>
275 </property>
276 <property name="bottomMargin">
277 <number>0</number>
278 </property>
279 <item>
280 <widget class="QGroupBox" name="vibrationGroupPlayer7">
281 <property name="title">
282 <string>Player 5</string>
283 </property>
284 <property name="checkable">
285 <bool>true</bool>
286 </property>
287 <layout class="QHBoxLayout" name="horizontalLayout_14">
288 <property name="leftMargin">
289 <number>3</number>
290 </property>
291 <property name="topMargin">
292 <number>3</number>
293 </property>
294 <property name="rightMargin">
295 <number>3</number>
296 </property>
297 <property name="bottomMargin">
298 <number>3</number>
299 </property>
300 <item>
301 <widget class="QSpinBox" name="vibrationSpinPlayer7">
302 <property name="minimumSize">
303 <size>
304 <width>68</width>
305 <height>21</height>
306 </size>
307 </property>
308 <property name="maximumSize">
309 <size>
310 <width>68</width>
311 <height>16777215</height>
312 </size>
313 </property>
314 <property name="suffix">
315 <string>%</string>
316 </property>
317 <property name="minimum">
318 <number>1</number>
319 </property>
320 <property name="maximum">
321 <number>150</number>
322 </property>
323 <property name="value">
324 <number>100</number>
325 </property>
326 </widget>
327 </item>
328 </layout>
329 </widget>
330 </item>
331 <item>
332 <widget class="QGroupBox" name="vibrationGroupPlayer8">
333 <property name="title">
334 <string>Player 6</string>
335 </property>
336 <property name="checkable">
337 <bool>true</bool>
338 </property>
339 <layout class="QHBoxLayout" name="horizontalLayout_15">
340 <property name="leftMargin">
341 <number>3</number>
342 </property>
343 <property name="topMargin">
344 <number>3</number>
345 </property>
346 <property name="rightMargin">
347 <number>3</number>
348 </property>
349 <property name="bottomMargin">
350 <number>3</number>
351 </property>
352 <item>
353 <widget class="QSpinBox" name="vibrationSpinPlayer8">
354 <property name="minimumSize">
355 <size>
356 <width>68</width>
357 <height>21</height>
358 </size>
359 </property>
360 <property name="maximumSize">
361 <size>
362 <width>68</width>
363 <height>16777215</height>
364 </size>
365 </property>
366 <property name="suffix">
367 <string>%</string>
368 </property>
369 <property name="minimum">
370 <number>1</number>
371 </property>
372 <property name="maximum">
373 <number>150</number>
374 </property>
375 <property name="value">
376 <number>100</number>
377 </property>
378 </widget>
379 </item>
380 </layout>
381 </widget>
382 </item>
383 <item>
384 <widget class="QGroupBox" name="vibrationGroupPlayer5">
385 <property name="title">
386 <string>Player 7</string>
387 </property>
388 <property name="checkable">
389 <bool>true</bool>
390 </property>
391 <layout class="QHBoxLayout" name="horizontalLayout_12">
392 <property name="leftMargin">
393 <number>3</number>
394 </property>
395 <property name="topMargin">
396 <number>3</number>
397 </property>
398 <property name="rightMargin">
399 <number>3</number>
400 </property>
401 <property name="bottomMargin">
402 <number>3</number>
403 </property>
404 <item>
405 <widget class="QSpinBox" name="vibrationSpinPlayer5">
406 <property name="minimumSize">
407 <size>
408 <width>68</width>
409 <height>21</height>
410 </size>
411 </property>
412 <property name="maximumSize">
413 <size>
414 <width>68</width>
415 <height>16777215</height>
416 </size>
417 </property>
418 <property name="suffix">
419 <string>%</string>
420 </property>
421 <property name="minimum">
422 <number>1</number>
423 </property>
424 <property name="maximum">
425 <number>150</number>
426 </property>
427 <property name="value">
428 <number>100</number>
429 </property>
430 </widget>
431 </item>
432 </layout>
433 </widget>
434 </item>
435 <item>
436 <widget class="QGroupBox" name="vibrationGroupPlayer6">
437 <property name="title">
438 <string>Player 8</string>
439 </property>
440 <property name="checkable">
441 <bool>true</bool>
442 </property>
443 <layout class="QHBoxLayout" name="horizontalLayout_13">
444 <property name="leftMargin">
445 <number>3</number>
446 </property>
447 <property name="topMargin">
448 <number>3</number>
449 </property>
450 <property name="rightMargin">
451 <number>3</number>
452 </property>
453 <property name="bottomMargin">
454 <number>3</number>
455 </property>
456 <item>
457 <widget class="QSpinBox" name="vibrationSpinPlayer6">
458 <property name="minimumSize">
459 <size>
460 <width>68</width>
461 <height>21</height>
462 </size>
463 </property>
464 <property name="maximumSize">
465 <size>
466 <width>68</width>
467 <height>16777215</height>
468 </size>
469 </property>
470 <property name="suffix">
471 <string>%</string>
472 </property>
473 <property name="minimum">
474 <number>1</number>
475 </property>
476 <property name="maximum">
477 <number>150</number>
478 </property>
479 <property name="value">
480 <number>100</number>
481 </property>
482 </widget>
483 </item>
484 </layout>
485 </widget>
486 </item>
487 </layout>
488 </widget>
489 </item>
490 </layout>
491 </widget>
492 </item>
493 <item>
494 <widget class="QGroupBox" name="vibrationSettingsGroup">
495 <property name="title">
496 <string>Settings</string>
497 </property>
498 <layout class="QVBoxLayout" name="verticalLayout">
499 <item>
500 <widget class="QCheckBox" name="checkBoxAccurateVibration">
501 <property name="text">
502 <string>Enable Accurate Vibration</string>
503 </property>
504 </widget>
505 </item>
506 </layout>
507 </widget>
508 </item>
509 <item>
510 <spacer name="spacerVibration">
511 <property name="orientation">
512 <enum>Qt::Vertical</enum>
513 </property>
514 <property name="sizeHint" stdset="0">
515 <size>
516 <width>167</width>
517 <height>55</height>
518 </size>
519 </property>
520 </spacer>
521 </item>
522 <item>
523 <widget class="QDialogButtonBox" name="buttonBoxVibration">
524 <property name="standardButtons">
525 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
526 </property>
527 </widget>
528 </item>
529 </layout>
530 </widget>
531 <resources/>
532 <connections>
533 <connection>
534 <sender>buttonBoxVibration</sender>
535 <signal>accepted()</signal>
536 <receiver>ConfigureVibration</receiver>
537 <slot>accept()</slot>
538 </connection>
539 <connection>
540 <sender>buttonBoxVibration</sender>
541 <signal>rejected()</signal>
542 <receiver>ConfigureVibration</receiver>
543 <slot>reject()</slot>
544 </connection>
545 </connections>
546</ui>
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp
new file mode 100644
index 000000000..e87aededb
--- /dev/null
+++ b/src/yuzu/configuration/input_profiles.cpp
@@ -0,0 +1,131 @@
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 <fmt/format.h>
6
7#include "common/common_paths.h"
8#include "common/file_util.h"
9#include "yuzu/configuration/config.h"
10#include "yuzu/configuration/input_profiles.h"
11
12namespace FS = Common::FS;
13
14namespace {
15
16bool ProfileExistsInFilesystem(std::string_view profile_name) {
17 return FS::Exists(fmt::format("{}input" DIR_SEP "{}.ini",
18 FS::GetUserPath(FS::UserPath::ConfigDir), profile_name));
19}
20
21bool IsINI(std::string_view filename) {
22 const std::size_t index = filename.rfind('.');
23
24 if (index == std::string::npos) {
25 return false;
26 }
27
28 return filename.substr(index) == ".ini";
29}
30
31std::string GetNameWithoutExtension(const std::string& filename) {
32 const std::size_t index = filename.rfind('.');
33
34 if (index == std::string::npos) {
35 return filename;
36 }
37
38 return filename.substr(0, index);
39}
40
41} // namespace
42
43InputProfiles::InputProfiles() {
44 const std::string input_profile_loc =
45 fmt::format("{}input", FS::GetUserPath(FS::UserPath::ConfigDir));
46
47 FS::ForeachDirectoryEntry(
48 nullptr, input_profile_loc,
49 [this](u64* entries_out, const std::string& directory, const std::string& filename) {
50 if (IsINI(filename) && IsProfileNameValid(GetNameWithoutExtension(filename))) {
51 map_profiles.insert_or_assign(
52 GetNameWithoutExtension(filename),
53 std::make_unique<Config>(GetNameWithoutExtension(filename),
54 Config::ConfigType::InputProfile));
55 }
56 return true;
57 });
58}
59
60InputProfiles::~InputProfiles() = default;
61
62std::vector<std::string> InputProfiles::GetInputProfileNames() {
63 std::vector<std::string> profile_names;
64 profile_names.reserve(map_profiles.size());
65
66 for (const auto& [profile_name, config] : map_profiles) {
67 if (!ProfileExistsInFilesystem(profile_name)) {
68 DeleteProfile(profile_name);
69 continue;
70 }
71
72 profile_names.push_back(profile_name);
73 }
74
75 return profile_names;
76}
77
78bool InputProfiles::IsProfileNameValid(std::string_view profile_name) {
79 return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos;
80}
81
82bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t player_index) {
83 if (ProfileExistsInMap(profile_name)) {
84 return false;
85 }
86
87 map_profiles.insert_or_assign(
88 profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile));
89
90 return SaveProfile(profile_name, player_index);
91}
92
93bool InputProfiles::DeleteProfile(const std::string& profile_name) {
94 if (!ProfileExistsInMap(profile_name)) {
95 return false;
96 }
97
98 if (!ProfileExistsInFilesystem(profile_name) ||
99 FS::Delete(map_profiles[profile_name]->GetConfigFilePath())) {
100 map_profiles.erase(profile_name);
101 }
102
103 return !ProfileExistsInMap(profile_name) && !ProfileExistsInFilesystem(profile_name);
104}
105
106bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t player_index) {
107 if (!ProfileExistsInMap(profile_name)) {
108 return false;
109 }
110
111 if (!ProfileExistsInFilesystem(profile_name)) {
112 map_profiles.erase(profile_name);
113 return false;
114 }
115
116 map_profiles[profile_name]->ReadControlPlayerValue(player_index);
117 return true;
118}
119
120bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t player_index) {
121 if (!ProfileExistsInMap(profile_name)) {
122 return false;
123 }
124
125 map_profiles[profile_name]->SaveControlPlayerValue(player_index);
126 return true;
127}
128
129bool InputProfiles::ProfileExistsInMap(const std::string& profile_name) const {
130 return map_profiles.find(profile_name) != map_profiles.end();
131}
diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h
new file mode 100644
index 000000000..cb41fd9be
--- /dev/null
+++ b/src/yuzu/configuration/input_profiles.h
@@ -0,0 +1,32 @@
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 <string>
8#include <string_view>
9#include <unordered_map>
10
11class Config;
12
13class InputProfiles {
14
15public:
16 explicit InputProfiles();
17 virtual ~InputProfiles();
18
19 std::vector<std::string> GetInputProfileNames();
20
21 static bool IsProfileNameValid(std::string_view profile_name);
22
23 bool CreateProfile(const std::string& profile_name, std::size_t player_index);
24 bool DeleteProfile(const std::string& profile_name);
25 bool LoadProfile(const std::string& profile_name, std::size_t player_index);
26 bool SaveProfile(const std::string& profile_name, std::size_t player_index);
27
28private:
29 bool ProfileExistsInMap(const std::string& profile_name) const;
30
31 std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles;
32};
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index e0ce45fd9..23643aea2 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -235,12 +235,11 @@ GameListWorker::~GameListWorker() = default;
235void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { 235void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
236 using namespace FileSys; 236 using namespace FileSys;
237 237
238 const auto& cache = 238 auto& system = Core::System::GetInstance();
239 dynamic_cast<ContentProviderUnion&>(Core::System::GetInstance().GetContentProvider()); 239 const auto& cache = dynamic_cast<ContentProviderUnion&>(system.GetContentProvider());
240 240
241 std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> installed_games; 241 auto installed_games = cache.ListEntriesFilterOrigin(std::nullopt, TitleType::Application,
242 installed_games = cache.ListEntriesFilterOrigin(std::nullopt, TitleType::Application, 242 ContentRecordType::Program);
243 ContentRecordType::Program);
244 243
245 if (parent_dir->type() == static_cast<int>(GameListItemType::SdmcDir)) { 244 if (parent_dir->type() == static_cast<int>(GameListItemType::SdmcDir)) {
246 installed_games = cache.ListEntriesFilterOrigin( 245 installed_games = cache.ListEntriesFilterOrigin(
@@ -254,23 +253,27 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
254 } 253 }
255 254
256 for (const auto& [slot, game] : installed_games) { 255 for (const auto& [slot, game] : installed_games) {
257 if (slot == ContentProviderUnionSlot::FrontendManual) 256 if (slot == ContentProviderUnionSlot::FrontendManual) {
258 continue; 257 continue;
258 }
259 259
260 const auto file = cache.GetEntryUnparsed(game.title_id, game.type); 260 const auto file = cache.GetEntryUnparsed(game.title_id, game.type);
261 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); 261 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(system, file);
262 if (!loader) 262 if (!loader) {
263 continue; 263 continue;
264 }
264 265
265 std::vector<u8> icon; 266 std::vector<u8> icon;
266 std::string name; 267 std::string name;
267 u64 program_id = 0; 268 u64 program_id = 0;
268 loader->ReadProgramId(program_id); 269 loader->ReadProgramId(program_id);
269 270
270 const PatchManager patch{program_id}; 271 const PatchManager patch{program_id, system.GetFileSystemController(),
272 system.GetContentProvider()};
271 const auto control = cache.GetEntry(game.title_id, ContentRecordType::Control); 273 const auto control = cache.GetEntry(game.title_id, ContentRecordType::Control);
272 if (control != nullptr) 274 if (control != nullptr) {
273 GetMetadataFromControlNCA(patch, *control, icon, name); 275 GetMetadataFromControlNCA(patch, *control, icon, name);
276 }
274 277
275 emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id, 278 emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id,
276 compatibility_list, patch), 279 compatibility_list, patch),
@@ -280,9 +283,11 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
280 283
281void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, 284void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path,
282 unsigned int recursion, GameListDir* parent_dir) { 285 unsigned int recursion, GameListDir* parent_dir) {
283 const auto callback = [this, target, recursion, 286 auto& system = Core::System::GetInstance();
284 parent_dir](u64* num_entries_out, const std::string& directory, 287
285 const std::string& virtual_name) -> bool { 288 const auto callback = [this, target, recursion, parent_dir,
289 &system](u64* num_entries_out, const std::string& directory,
290 const std::string& virtual_name) -> bool {
286 if (stop_processing) { 291 if (stop_processing) {
287 // Breaks the callback loop. 292 // Breaks the callback loop.
288 return false; 293 return false;
@@ -293,7 +298,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
293 if (!is_dir && 298 if (!is_dir &&
294 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { 299 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
295 const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read); 300 const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read);
296 auto loader = Loader::GetLoader(file); 301 auto loader = Loader::GetLoader(system, file);
297 if (!loader) { 302 if (!loader) {
298 return true; 303 return true;
299 } 304 }
@@ -331,7 +336,8 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
331 std::string name = " "; 336 std::string name = " ";
332 [[maybe_unused]] const auto res3 = loader->ReadTitle(name); 337 [[maybe_unused]] const auto res3 = loader->ReadTitle(name);
333 338
334 const FileSys::PatchManager patch{program_id}; 339 const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
340 system.GetContentProvider()};
335 341
336 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, 342 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id,
337 compatibility_list, patch), 343 compatibility_list, patch),
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 18e68e590..805619ccf 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -18,6 +18,7 @@
18#include "applets/web_browser.h" 18#include "applets/web_browser.h"
19#include "configuration/configure_input.h" 19#include "configuration/configure_input.h"
20#include "configuration/configure_per_game.h" 20#include "configuration/configure_per_game.h"
21#include "configuration/configure_vibration.h"
21#include "core/file_sys/vfs.h" 22#include "core/file_sys/vfs.h"
22#include "core/file_sys/vfs_real.h" 23#include "core/file_sys/vfs_real.h"
23#include "core/frontend/applets/controller.h" 24#include "core/frontend/applets/controller.h"
@@ -50,12 +51,14 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
50#include <QDesktopServices> 51#include <QDesktopServices>
51#include <QDesktopWidget> 52#include <QDesktopWidget>
52#include <QDialogButtonBox> 53#include <QDialogButtonBox>
54#include <QDir>
53#include <QFile> 55#include <QFile>
54#include <QFileDialog> 56#include <QFileDialog>
55#include <QInputDialog> 57#include <QInputDialog>
56#include <QMessageBox> 58#include <QMessageBox>
57#include <QProgressBar> 59#include <QProgressBar>
58#include <QProgressDialog> 60#include <QProgressDialog>
61#include <QPushButton>
59#include <QShortcut> 62#include <QShortcut>
60#include <QStatusBar> 63#include <QStatusBar>
61#include <QSysInfo> 64#include <QSysInfo>
@@ -277,6 +280,8 @@ GMainWindow::GMainWindow()
277 if (args.length() >= 2) { 280 if (args.length() >= 2) {
278 BootGame(args[1]); 281 BootGame(args[1]);
279 } 282 }
283
284 MigrateConfigFiles();
280} 285}
281 286
282GMainWindow::~GMainWindow() { 287GMainWindow::~GMainWindow() {
@@ -288,6 +293,7 @@ GMainWindow::~GMainWindow() {
288void GMainWindow::ControllerSelectorReconfigureControllers( 293void GMainWindow::ControllerSelectorReconfigureControllers(
289 const Core::Frontend::ControllerParameters& parameters) { 294 const Core::Frontend::ControllerParameters& parameters) {
290 QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get()); 295 QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get());
296
291 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | 297 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
292 Qt::WindowTitleHint | Qt::WindowSystemMenuHint); 298 Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
293 dialog.setWindowModality(Qt::WindowModal); 299 dialog.setWindowModality(Qt::WindowModal);
@@ -547,13 +553,14 @@ void GMainWindow::InitializeWidgets() {
547 dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); 553 dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
548 dock_status_button->setFocusPolicy(Qt::NoFocus); 554 dock_status_button->setFocusPolicy(Qt::NoFocus);
549 connect(dock_status_button, &QPushButton::clicked, [&] { 555 connect(dock_status_button, &QPushButton::clicked, [&] {
550 Settings::values.use_docked_mode = !Settings::values.use_docked_mode; 556 Settings::values.use_docked_mode.SetValue(!Settings::values.use_docked_mode.GetValue());
551 dock_status_button->setChecked(Settings::values.use_docked_mode); 557 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
552 OnDockedModeChanged(!Settings::values.use_docked_mode, Settings::values.use_docked_mode); 558 OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(),
559 Settings::values.use_docked_mode.GetValue());
553 }); 560 });
554 dock_status_button->setText(tr("DOCK")); 561 dock_status_button->setText(tr("DOCK"));
555 dock_status_button->setCheckable(true); 562 dock_status_button->setCheckable(true);
556 dock_status_button->setChecked(Settings::values.use_docked_mode); 563 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
557 statusBar()->insertPermanentWidget(0, dock_status_button); 564 statusBar()->insertPermanentWidget(0, dock_status_button);
558 565
559 // Setup ASync button 566 // Setup ASync button
@@ -792,10 +799,11 @@ void GMainWindow::InitializeHotkeys() {
792 }); 799 });
793 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Change Docked Mode"), this), 800 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Change Docked Mode"), this),
794 &QShortcut::activated, this, [&] { 801 &QShortcut::activated, this, [&] {
795 Settings::values.use_docked_mode = !Settings::values.use_docked_mode; 802 Settings::values.use_docked_mode.SetValue(
796 OnDockedModeChanged(!Settings::values.use_docked_mode, 803 !Settings::values.use_docked_mode.GetValue());
797 Settings::values.use_docked_mode); 804 OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(),
798 dock_status_button->setChecked(Settings::values.use_docked_mode); 805 Settings::values.use_docked_mode.GetValue());
806 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
799 }); 807 });
800 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this), 808 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this),
801 &QShortcut::activated, this, 809 &QShortcut::activated, this,
@@ -970,7 +978,7 @@ void GMainWindow::AllowOSSleep() {
970#endif 978#endif
971} 979}
972 980
973bool GMainWindow::LoadROM(const QString& filename) { 981bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
974 // Shutdown previous session if the emu thread is still active... 982 // Shutdown previous session if the emu thread is still active...
975 if (emu_thread != nullptr) 983 if (emu_thread != nullptr)
976 ShutdownGame(); 984 ShutdownGame();
@@ -995,7 +1003,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
995 1003
996 system.RegisterHostThread(); 1004 system.RegisterHostThread();
997 1005
998 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; 1006 const Core::System::ResultStatus result{
1007 system.Load(*render_window, filename.toStdString(), program_index)};
999 1008
1000 const auto drd_callout = 1009 const auto drd_callout =
1001 (UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0; 1010 (UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
@@ -1077,26 +1086,32 @@ void GMainWindow::SelectAndSetCurrentUser() {
1077 Settings::values.current_user = dialog.GetIndex(); 1086 Settings::values.current_user = dialog.GetIndex();
1078} 1087}
1079 1088
1080void GMainWindow::BootGame(const QString& filename) { 1089void GMainWindow::BootGame(const QString& filename, std::size_t program_index) {
1081 LOG_INFO(Frontend, "yuzu starting..."); 1090 LOG_INFO(Frontend, "yuzu starting...");
1082 StoreRecentFile(filename); // Put the filename on top of the list 1091 StoreRecentFile(filename); // Put the filename on top of the list
1083 1092
1084 u64 title_id{0}; 1093 u64 title_id{0};
1085 1094
1095 last_filename_booted = filename;
1096
1097 auto& system = Core::System::GetInstance();
1086 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData()); 1098 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
1087 const auto loader = Loader::GetLoader(v_file); 1099 const auto loader = Loader::GetLoader(system, v_file, program_index);
1100
1088 if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) { 1101 if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) {
1089 // Load per game settings 1102 // Load per game settings
1090 Config per_game_config(fmt::format("{:016X}.ini", title_id), false); 1103 Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig);
1091 } 1104 }
1092 1105
1106 ConfigureVibration::SetAllVibrationDevices();
1107
1093 Settings::LogSettings(); 1108 Settings::LogSettings();
1094 1109
1095 if (UISettings::values.select_user_on_boot) { 1110 if (UISettings::values.select_user_on_boot) {
1096 SelectAndSetCurrentUser(); 1111 SelectAndSetCurrentUser();
1097 } 1112 }
1098 1113
1099 if (!LoadROM(filename)) 1114 if (!LoadROM(filename, program_index))
1100 return; 1115 return;
1101 1116
1102 // Create and start the emulation thread 1117 // Create and start the emulation thread
@@ -1104,6 +1119,10 @@ void GMainWindow::BootGame(const QString& filename) {
1104 emit EmulationStarting(emu_thread.get()); 1119 emit EmulationStarting(emu_thread.get());
1105 emu_thread->start(); 1120 emu_thread->start();
1106 1121
1122 // Register an ExecuteProgram callback such that Core can execute a sub-program
1123 system.RegisterExecuteProgramCallback(
1124 [this](std::size_t program_index) { render_window->ExecuteProgram(program_index); });
1125
1107 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); 1126 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
1108 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views 1127 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
1109 // before the CPU continues 1128 // before the CPU continues
@@ -1134,9 +1153,13 @@ void GMainWindow::BootGame(const QString& filename) {
1134 1153
1135 std::string title_name; 1154 std::string title_name;
1136 std::string title_version; 1155 std::string title_version;
1137 const auto res = Core::System::GetInstance().GetGameName(title_name); 1156 const auto res = system.GetGameName(title_name);
1138 1157
1139 const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata(); 1158 const auto metadata = [&system, title_id] {
1159 const FileSys::PatchManager pm(title_id, system.GetFileSystemController(),
1160 system.GetContentProvider());
1161 return pm.GetControlMetadata();
1162 }();
1140 if (metadata.first != nullptr) { 1163 if (metadata.first != nullptr) {
1141 title_version = metadata.first->GetVersionString(); 1164 title_version = metadata.first->GetVersionString();
1142 title_name = metadata.first->GetApplicationName(); 1165 title_name = metadata.first->GetApplicationName();
@@ -1147,7 +1170,7 @@ void GMainWindow::BootGame(const QString& filename) {
1147 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); 1170 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
1148 UpdateWindowTitle(title_name, title_version); 1171 UpdateWindowTitle(title_name, title_version);
1149 1172
1150 loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); 1173 loading_screen->Prepare(system.GetAppLoader());
1151 loading_screen->show(); 1174 loading_screen->show();
1152 1175
1153 emulation_running = true; 1176 emulation_running = true;
@@ -1266,16 +1289,18 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
1266 const std::string& game_path) { 1289 const std::string& game_path) {
1267 std::string path; 1290 std::string path;
1268 QString open_target; 1291 QString open_target;
1292 auto& system = Core::System::GetInstance();
1269 1293
1270 const auto [user_save_size, device_save_size] = [this, &program_id, &game_path] { 1294 const auto [user_save_size, device_save_size] = [this, &game_path, &program_id, &system] {
1271 FileSys::PatchManager pm{program_id}; 1295 const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
1296 system.GetContentProvider()};
1272 const auto control = pm.GetControlMetadata().first; 1297 const auto control = pm.GetControlMetadata().first;
1273 if (control != nullptr) { 1298 if (control != nullptr) {
1274 return std::make_pair(control->GetDefaultNormalSaveSize(), 1299 return std::make_pair(control->GetDefaultNormalSaveSize(),
1275 control->GetDeviceSaveDataSize()); 1300 control->GetDeviceSaveDataSize());
1276 } else { 1301 } else {
1277 const auto file = Core::GetGameFileFromPath(vfs, game_path); 1302 const auto file = Core::GetGameFileFromPath(vfs, game_path);
1278 const auto loader = Loader::GetLoader(file); 1303 const auto loader = Loader::GetLoader(system, file);
1279 1304
1280 FileSys::NACP nacp{}; 1305 FileSys::NACP nacp{};
1281 loader->ReadControlData(nacp); 1306 loader->ReadControlData(nacp);
@@ -1577,7 +1602,8 @@ void GMainWindow::RemoveCustomConfiguration(u64 program_id) {
1577 const QString config_dir = 1602 const QString config_dir =
1578 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir)); 1603 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir));
1579 const QString custom_config_file_path = 1604 const QString custom_config_file_path =
1580 config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id)); 1605 config_dir + QStringLiteral("custom") + QDir::separator() +
1606 QString::fromStdString(fmt::format("{:016X}.ini", program_id));
1581 1607
1582 if (!QFile::exists(custom_config_file_path)) { 1608 if (!QFile::exists(custom_config_file_path)) {
1583 QMessageBox::warning(this, tr("Error Removing Custom Configuration"), 1609 QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
@@ -1601,7 +1627,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1601 "cancelled the operation.")); 1627 "cancelled the operation."));
1602 }; 1628 };
1603 1629
1604 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); 1630 auto& system = Core::System::GetInstance();
1631 const auto loader = Loader::GetLoader(system, vfs->OpenFile(game_path, FileSys::Mode::Read));
1605 if (loader == nullptr) { 1632 if (loader == nullptr) {
1606 failed(); 1633 failed();
1607 return; 1634 return;
@@ -1613,7 +1640,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1613 return; 1640 return;
1614 } 1641 }
1615 1642
1616 const auto& installed = Core::System::GetInstance().GetContentProvider(); 1643 const auto& installed = system.GetContentProvider();
1617 const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id); 1644 const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id);
1618 1645
1619 if (!romfs_title_id) { 1646 if (!romfs_title_id) {
@@ -1628,7 +1655,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1628 1655
1629 if (*romfs_title_id == program_id) { 1656 if (*romfs_title_id == program_id) {
1630 const u64 ivfc_offset = loader->ReadRomFSIVFCOffset(); 1657 const u64 ivfc_offset = loader->ReadRomFSIVFCOffset();
1631 FileSys::PatchManager pm{program_id}; 1658 const FileSys::PatchManager pm{program_id, system.GetFileSystemController(), installed};
1632 romfs = pm.PatchRomFS(file, ivfc_offset, FileSys::ContentRecordType::Program); 1659 romfs = pm.PatchRomFS(file, ivfc_offset, FileSys::ContentRecordType::Program);
1633 } else { 1660 } else {
1634 romfs = installed.GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS(); 1661 romfs = installed.GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
@@ -1745,7 +1772,8 @@ void GMainWindow::OnGameListShowList(bool show) {
1745void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { 1772void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
1746 u64 title_id{}; 1773 u64 title_id{};
1747 const auto v_file = Core::GetGameFileFromPath(vfs, file); 1774 const auto v_file = Core::GetGameFileFromPath(vfs, file);
1748 const auto loader = Loader::GetLoader(v_file); 1775 const auto loader = Loader::GetLoader(Core::System::GetInstance(), v_file);
1776
1749 if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) { 1777 if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
1750 QMessageBox::information(this, tr("Properties"), 1778 QMessageBox::information(this, tr("Properties"),
1751 tr("The game properties could not be loaded.")); 1779 tr("The game properties could not be loaded."));
@@ -2117,6 +2145,11 @@ void GMainWindow::OnLoadComplete() {
2117 loading_screen->OnLoadComplete(); 2145 loading_screen->OnLoadComplete();
2118} 2146}
2119 2147
2148void GMainWindow::OnExecuteProgram(std::size_t program_index) {
2149 ShutdownGame();
2150 BootGame(last_filename_booted, program_index);
2151}
2152
2120void GMainWindow::ErrorDisplayDisplayError(QString body) { 2153void GMainWindow::ErrorDisplayDisplayError(QString body) {
2121 QMessageBox::critical(this, tr("Error Display"), body); 2154 QMessageBox::critical(this, tr("Error Display"), body);
2122 emit ErrorDisplayFinished(); 2155 emit ErrorDisplayFinished();
@@ -2393,6 +2426,29 @@ void GMainWindow::OnCaptureScreenshot() {
2393 OnStartGame(); 2426 OnStartGame();
2394} 2427}
2395 2428
2429// TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant
2430void GMainWindow::MigrateConfigFiles() {
2431 const std::string& config_dir_str = Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir);
2432 const QDir config_dir = QDir(QString::fromStdString(config_dir_str));
2433 const QStringList config_dir_list = config_dir.entryList(QStringList(QStringLiteral("*.ini")));
2434
2435 Common::FS::CreateFullPath(fmt::format("{}custom" DIR_SEP, config_dir_str));
2436 for (QStringList::const_iterator it = config_dir_list.constBegin();
2437 it != config_dir_list.constEnd(); ++it) {
2438 const auto filename = it->toStdString();
2439 if (filename.find_first_not_of("0123456789abcdefACBDEF", 0) < 16) {
2440 continue;
2441 }
2442 const auto origin = fmt::format("{}{}", config_dir_str, filename);
2443 const auto destination = fmt::format("{}custom" DIR_SEP "{}", config_dir_str, filename);
2444 LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination);
2445 if (!Common::FS::Rename(origin, destination)) {
2446 // Delete the old config file if one already exists in the new location.
2447 Common::FS::Delete(origin);
2448 }
2449 }
2450}
2451
2396void GMainWindow::UpdateWindowTitle(const std::string& title_name, 2452void GMainWindow::UpdateWindowTitle(const std::string& title_name,
2397 const std::string& title_version) { 2453 const std::string& title_version) {
2398 const auto full_name = std::string(Common::g_build_fullname); 2454 const auto full_name = std::string(Common::g_build_fullname);
@@ -2450,7 +2506,7 @@ void GMainWindow::UpdateStatusBar() {
2450} 2506}
2451 2507
2452void GMainWindow::UpdateStatusButtons() { 2508void GMainWindow::UpdateStatusButtons() {
2453 dock_status_button->setChecked(Settings::values.use_docked_mode); 2509 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
2454 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); 2510 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
2455 Settings::values.use_asynchronous_gpu_emulation.SetValue( 2511 Settings::values.use_asynchronous_gpu_emulation.SetValue(
2456 Settings::values.use_asynchronous_gpu_emulation.GetValue() || 2512 Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index afcfa68a9..6242341d1 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -131,6 +131,7 @@ signals:
131 131
132public slots: 132public slots:
133 void OnLoadComplete(); 133 void OnLoadComplete();
134 void OnExecuteProgram(std::size_t program_index);
134 void ControllerSelectorReconfigureControllers( 135 void ControllerSelectorReconfigureControllers(
135 const Core::Frontend::ControllerParameters& parameters); 136 const Core::Frontend::ControllerParameters& parameters);
136 void ErrorDisplayDisplayError(QString body); 137 void ErrorDisplayDisplayError(QString body);
@@ -154,8 +155,8 @@ private:
154 void PreventOSSleep(); 155 void PreventOSSleep();
155 void AllowOSSleep(); 156 void AllowOSSleep();
156 157
157 bool LoadROM(const QString& filename); 158 bool LoadROM(const QString& filename, std::size_t program_index);
158 void BootGame(const QString& filename); 159 void BootGame(const QString& filename, std::size_t program_index = 0);
159 void ShutdownGame(); 160 void ShutdownGame();
160 161
161 void ShowTelemetryCallout(); 162 void ShowTelemetryCallout();
@@ -251,6 +252,7 @@ private:
251 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); 252 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
252 InstallResult InstallNSPXCI(const QString& filename); 253 InstallResult InstallNSPXCI(const QString& filename);
253 InstallResult InstallNCA(const QString& filename); 254 InstallResult InstallNCA(const QString& filename);
255 void MigrateConfigFiles();
254 void UpdateWindowTitle(const std::string& title_name = {}, 256 void UpdateWindowTitle(const std::string& title_name = {},
255 const std::string& title_version = {}); 257 const std::string& title_version = {});
256 void UpdateStatusBar(); 258 void UpdateStatusBar();
@@ -316,6 +318,9 @@ private:
316 // Install progress dialog 318 // Install progress dialog
317 QProgressDialog* install_progress; 319 QProgressDialog* install_progress;
318 320
321 // Last game booted, used for multi-process apps
322 QString last_filename_booted;
323
319protected: 324protected:
320 void dropEvent(QDropEvent* event) override; 325 void dropEvent(QDropEvent* event) override;
321 void dragEnterEvent(QDragEnterEvent* event) override; 326 void dragEnterEvent(QDragEnterEvent* event) override;
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 334038ef9..e1adbbf2b 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -228,24 +228,24 @@ static const std::array<int, 8> keyboard_mods{
228 228
229void Config::ReadValues() { 229void Config::ReadValues() {
230 // Controls 230 // Controls
231 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 231 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
232 const auto group = fmt::format("ControlsP{}", p); 232 const auto group = fmt::format("ControlsP{}", p);
233 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 233 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
234 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 234 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
235 Settings::values.players[p].buttons[i] = 235 Settings::values.players.GetValue()[p].buttons[i] =
236 sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param); 236 sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param);
237 if (Settings::values.players[p].buttons[i].empty()) 237 if (Settings::values.players.GetValue()[p].buttons[i].empty())
238 Settings::values.players[p].buttons[i] = default_param; 238 Settings::values.players.GetValue()[p].buttons[i] = default_param;
239 } 239 }
240 240
241 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 241 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
242 std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 242 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
243 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 243 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
244 default_analogs[i][3], default_analogs[i][4], 0.5f); 244 default_analogs[i][3], default_analogs[i][4], 0.5f);
245 Settings::values.players[p].analogs[i] = 245 Settings::values.players.GetValue()[p].analogs[i] =
246 sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param); 246 sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param);
247 if (Settings::values.players[p].analogs[i].empty()) 247 if (Settings::values.players.GetValue()[p].analogs[i].empty())
248 Settings::values.players[p].analogs[i] = default_param; 248 Settings::values.players.GetValue()[p].analogs[i] = default_param;
249 } 249 }
250 } 250 }
251 251
@@ -288,10 +288,12 @@ void Config::ReadValues() {
288 Settings::values.debug_pad_analogs[i] = default_param; 288 Settings::values.debug_pad_analogs[i] = default_param;
289 } 289 }
290 290
291 Settings::values.vibration_enabled = 291 Settings::values.vibration_enabled.SetValue(
292 sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true); 292 sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true));
293 Settings::values.motion_enabled = 293 Settings::values.enable_accurate_vibrations.SetValue(
294 sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true); 294 sdl2_config->GetBoolean("ControlsGeneral", "enable_accurate_vibrations", false));
295 Settings::values.motion_enabled.SetValue(
296 sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true));
295 Settings::values.touchscreen.enabled = 297 Settings::values.touchscreen.enabled =
296 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); 298 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
297 Settings::values.touchscreen.device = 299 Settings::values.touchscreen.device =
@@ -343,7 +345,8 @@ void Config::ReadValues() {
343 Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", ""); 345 Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", "");
344 346
345 // System 347 // System
346 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); 348 Settings::values.use_docked_mode.SetValue(
349 sdl2_config->GetBoolean("System", "use_docked_mode", false));
347 const auto size = sdl2_config->GetInteger("System", "users_size", 0); 350 const auto size = sdl2_config->GetInteger("System", "users_size", 0);
348 351
349 Settings::values.current_user = std::clamp<int>( 352 Settings::values.current_user = std::clamp<int>(
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 796e27df4..bcbbcd4ca 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -65,6 +65,14 @@ button_screenshot=
65lstick= 65lstick=
66rstick= 66rstick=
67 67
68# Whether to enable or disable vibration
69# 0: Disabled, 1 (default): Enabled
70vibration_enabled=
71
72# Whether to enable or disable accurate vibrations
73# 0 (default): Disabled, 1: Enabled
74enable_accurate_vibrations=
75
68# for motion input, the following devices are available: 76# for motion input, the following devices are available:
69# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters: 77# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters:
70# - "update_period": update period in milliseconds (default to 100) 78# - "update_period": update period in milliseconds (default to 100)
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 3a76c785f..14a23c71b 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -240,11 +240,11 @@ int main(int argc, char** argv) {
240 system.CurrentProcess()->GetTitleID(), false, 240 system.CurrentProcess()->GetTitleID(), false,
241 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {}); 241 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
242 242
243 system.Run(); 243 void(system.Run());
244 while (emu_window->IsOpen()) { 244 while (emu_window->IsOpen()) {
245 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 245 std::this_thread::sleep_for(std::chrono::milliseconds(1));
246 } 246 }
247 system.Pause(); 247 void(system.Pause());
248 system.Shutdown(); 248 system.Shutdown();
249 249
250 detached_tasks.WaitForAllTasks(); 250 detached_tasks.WaitForAllTasks();
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
index bc273fb51..b6cdc7c1c 100644
--- a/src/yuzu_tester/config.cpp
+++ b/src/yuzu_tester/config.cpp
@@ -47,13 +47,13 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
47 47
48void Config::ReadValues() { 48void Config::ReadValues() {
49 // Controls 49 // Controls
50 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 50 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
51 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 51 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
52 Settings::values.players[p].buttons[i] = ""; 52 Settings::values.players.GetValue()[p].buttons[i] = "";
53 } 53 }
54 54
55 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 55 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
56 Settings::values.players[p].analogs[i] = ""; 56 Settings::values.players.GetValue()[p].analogs[i] = "";
57 } 57 }
58 } 58 }
59 59
@@ -75,8 +75,9 @@ void Config::ReadValues() {
75 Settings::values.debug_pad_analogs[i] = ""; 75 Settings::values.debug_pad_analogs[i] = "";
76 } 76 }
77 77
78 Settings::values.vibration_enabled = true; 78 Settings::values.vibration_enabled.SetValue(true);
79 Settings::values.motion_enabled = true; 79 Settings::values.enable_accurate_vibrations.SetValue(false);
80 Settings::values.motion_enabled.SetValue(true);
80 Settings::values.touchscreen.enabled = ""; 81 Settings::values.touchscreen.enabled = "";
81 Settings::values.touchscreen.device = ""; 82 Settings::values.touchscreen.device = "";
82 Settings::values.touchscreen.finger = 0; 83 Settings::values.touchscreen.finger = 0;
@@ -84,8 +85,8 @@ void Config::ReadValues() {
84 Settings::values.touchscreen.diameter_x = 15; 85 Settings::values.touchscreen.diameter_x = 15;
85 Settings::values.touchscreen.diameter_y = 15; 86 Settings::values.touchscreen.diameter_y = 15;
86 87
87 Settings::values.use_docked_mode = 88 Settings::values.use_docked_mode.SetValue(
88 sdl2_config->GetBoolean("Controls", "use_docked_mode", false); 89 sdl2_config->GetBoolean("Controls", "use_docked_mode", false));
89 90
90 // Data Storage 91 // Data Storage
91 Settings::values.use_virtual_sd = 92 Settings::values.use_virtual_sd =
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index 5798ce43a..88e4bd1f7 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -256,11 +256,11 @@ int main(int argc, char** argv) {
256 256
257 system.GPU().Start(); 257 system.GPU().Start();
258 258
259 system.Run(); 259 void(system.Run());
260 while (!finished) { 260 while (!finished) {
261 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 261 std::this_thread::sleep_for(std::chrono::milliseconds(1));
262 } 262 }
263 system.Pause(); 263 void(system.Pause());
264 264
265 detached_tasks.WaitForAllTasks(); 265 detached_tasks.WaitForAllTasks();
266 return return_value; 266 return return_value;